1

I'm playing around with dapptools: dapp

And I want to write a test that checks the gas for OpenZeppelin's ERC721 implementation of the transferFrom(..) function.

Here's my test:

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.6;

import "ds-test/test.sol";

import "./DemoErc721.sol";

contract DemoErc721Test is DSTest {

    DemoErc721 a;
    address testWallet = address(0x7E67aF7FF72cb87b7B0100cA8128F4673D185234);
    address testWallet2 = address(0x7E67af7FF72Cb87B7b0100ca8128f4673D185235);

    function setUp() public {
        a = new DemoErc721();
    }

    function mintMultiple(uint256 numToMint) public {
        a.mint(testWallet, numToMint);
        assertEq(a.balanceOf(testWallet), numToMint);
    }

    function testMint_1() public {
        mintMultiple(1);
    }
    function testMint_2() public {
        mintMultiple(2);
    }
    function testMint_3() public {
        mintMultiple(3);
    }
    function testMint_4() public {
        mintMultiple(4);
    }
    function testMint_5() public {
        mintMultiple(5);
    }

    function testTransfer() public {
        mintMultiple(1);
        a.transferFrom(testWallet, testWallet2, 1);
    }
}

You can see that in the mintMultiple function, we are always minting tokens to the testWallet address.

So in the final test, testTransfer(), I expect to be able to transfer from testWallet to another wallet, because testWallet is the owner of NFT with tokenId 1.

However, I think the way I've written this test causes the calling address to be this test contract's address, rather than the testWallet address.

The analogous logic in Hardhat would be something like this:

Finally, to execute a contract's method from another account, all you need to do is connect the Contract with the method being executed:

await greeter.connect(addr1).setGreeting("Hallo, Erde!");

So my question is, how can I simulate making the transferFrom function call from testWallet?

2 Answers 2

0

According to @transmissions11 on twitter:

you can't - can either create a testing User contract or use Foundry which has a .prank() cheatcode for this

https://github.com/gakonst/foundry

0

You can create another contract called, let's say, TokenUser. Initialise it with a token instance in the constructor. Now, define the calls the user can make to the token and write a wrapper function around it. Something like this

contract TokenUser{

    Token  token;

    constructor(Token token_) public {
        token = token_;
    }

    function doTransfer(address to, uint amount) public returns (bool){
        return token.transfer(to, amount);
    }
    
    ...

    //other functions

}

Now in the setUp() in the test contract, create an instance of the token and pass the address to the user.

contract TokenTest is DSTest{

    Token token;
    TokenUser user1;
    TokenUser user2;

    function setUp() public {
       token = new Token("[required_inputs]");
       user1 = new TokenUser(token);
       user2 = new TokenUser(token);
    }

    // rest of the contract
    
}

For test functions, you can use the user instances

function testSomething() public {
    user1.transfer(address(user2),100);
    user2.approve(address(user1),100);
}

This method can be used to simulate sending transactions from different accounts (the accounts being different TokenUser contracts). There is an elegant implementation example by dapphub, you can find it here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.