Skip to content

Latest commit

 

History

History
163 lines (138 loc) · 7.66 KB

10.md

File metadata and controls

163 lines (138 loc) · 7.66 KB
title actions requireLogin material
ERC721 代币转移 —— 两步场景
checkAnswer
hints
true
editor
language startingCode answer
javascript
test/CryptoZombies.js test/helpers/utils.js
const CryptoZombies = artifacts.require("CryptoZombies"); const utils = require("./helpers/utils"); const zombieNames = ["Zombie 1", "Zombie 2"]; contract("CryptoZombies", (accounts) => { let [alice, bob] = accounts; let contractInstance; beforeEach(async () => { contractInstance = await CryptoZombies.new(); }); it("should be able to create a new zombie", async () => { const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice}); assert.equal(result.receipt.status, true); assert.equal(result.logs[0].args.name,zombieNames[0]); }) it("should not allow two zombies", async () => { await contractInstance.createRandomZombie(zombieNames[0], {from: alice}); await utils.shouldThrow(contractInstance.createRandomZombie(zombieNames[1], {from: alice})); }) context("with the single-step transfer scenario", async () => { it("should transfer a zombie", async () => { const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice}); const zombieId = result.logs[0].args.zombieId.toNumber(); await contractInstance.transferFrom(alice, bob, zombieId, {from: alice}); const newOwner = await contractInstance.ownerOf(zombieId); assert.equal(newOwner, bob); }) }) xcontext("with the two-step transfer scenario", async () => { it("should approve and then transfer a zombie when the approved address calls transferForm", async () => { const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice}); const zombieId = result.logs[0].args.zombieId.toNumber(); // start here await contractInstance.transferFrom(alice, bob, zombieId, {from: alice}); const newOwner = await contractInstance.ownerOf(zombieId); assert.equal(newOwner,bob); }) it("should approve and then transfer a zombie when the owner calls transferForm", async () => { // TODO: Test the two-step scenario. The owner calls transferFrom }) }) })
async function shouldThrow(promise) { try { await promise; assert(true); } catch (err) { return; } assert(false, "The contract did not throw."); } module.exports = { shouldThrow, };
const CryptoZombies = artifacts.require("CryptoZombies"); const utils = require("./helpers/utils"); const zombieNames = ["Zombie 1", "Zombie 2"]; contract("CryptoZombies", (accounts) => { let [alice, bob] = accounts; let contractInstance; beforeEach(async () => { contractInstance = await CryptoZombies.new(); }); it("should be able to create a new zombie", async () => { const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice}); assert.equal(result.receipt.status, true); assert.equal(result.logs[0].args.name,zombieNames[0]); }) it("should not allow two zombies", async () => { await contractInstance.createRandomZombie(zombieNames[0], {from: alice}); await utils.shouldThrow(contractInstance.createRandomZombie(zombieNames[1], {from: alice})); }) context("with the single-step transfer scenario", async () => { it("should transfer a zombie", async () => { const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice}); const zombieId = result.logs[0].args.zombieId.toNumber(); await contractInstance.transferFrom(alice, bob, zombieId, {from: alice}); const newOwner = await contractInstance.ownerOf(zombieId); assert.equal(newOwner, bob); }) }) context("with the two-step transfer scenario", async () => { it("should approve and then transfer a zombie when the approved address calls transferForm", async () => { const result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice}); const zombieId = result.logs[0].args.zombieId.toNumber(); await contractInstance.approve(bob, zombieId, {from: alice}); await contractInstance.transferFrom(alice, bob, zombieId, {from: bob}); const newOwner = await contractInstance.ownerOf(zombieId); assert.equal(newOwner,bob); }) xit("should approve and then transfer a zombie when the owner calls transferForm", async () => { // TODO: Test the two-step scenario. The owner calls transferFrom }) }) })

approve 后面跟着 transferFrom 的方式来转移代币可没那么简单,不过别担心,我会帮你的。

简而言之,我们得测试两个不同的场景:

  • Alice 批准 Bob 拿走 ERC721 代币。然后,Bob(批准的地址)调用 transferFrom

  • Alice 批准 Bob 拿走 ERC721 代币。接下来,Alice 转移 ERC721 代币。

这两种场景的区别在于 来调用实际的转移,是 Alice 还是 Bob。

这样解释就还蛮简单的,对吧?

来看下第一个场景。

Bob 调用 transferFrom

该场景步骤如下:

  • Alice 创建一个新僵尸,然后调用 approve
  • 接下来,Bob 运行 transferFrom,这会让他成为该 EC721 代币的主人。
  • 最后,我们得以 newOwnerbob 作为参数调用 assert.equal

实战演习

  1. 测试前两行代码与之前测试类似。我们已经帮你把它们复制粘贴好了。

  2. 接下来,为了批准 Bob 拿走该 ERC721 代币,调用 approve()。该函数以 bobzombieId 为参数。另外,确保 Alice 调用该方法(毕竟要转移可是她的 ERC721 代币)。

  3. 最后三行代码与之前的测试非常相似。我们再一次帮你把它们复制粘贴好了。来更新下 transferFrom() 函数调用,以便发送方是 Bob。

  4. 最后,“不跳过”这个场景并“跳过”最后一个测试用例,即我们仍需要编写的测试用例。

可以运行 truffle test 了,看看我们的测试是否能通过:

Contract: CryptoZombies
    ✓ should be able to create a new zombie (218ms)
    ✓ should not allow two zombies (175ms)
    with the single-step transfer scenario
      ✓ should transfer a zombie (334ms)
    with the two-step transfer scenario
      ✓ should approve and then transfer a zombie when the owner calls transferForm (360ms)
      - should approve and then transfer a zombie when the approved address calls transferForm


  4 passing (2s)
  1 pending

很棒!继续下一个测试吧。