Call a Solidity function and feed it with more arguments than what it is defined to accept

soliditytruffletruffle-contract

I am following the tutorial on this page, especially this part of code

 // SPDX-License-Identifier: MIT
    pragma solidity 0.8.3;

    import "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol";
    import "@openzeppelin/contracts/utils/Counters.sol";

    contract AisthisiToken is ERC721PresetMinterPauserAutoId {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    mapping (uint => uint) public tokenLockedFromTimestamp;
    mapping (uint => bytes32) public tokenUnlockCodeHashes;
    mapping (uint => bool) public tokenUnlocked;

    event TokenUnlocked(uint tokenId, address unlockerAddress);

    constructor() ERC721PresetMinterPauserAutoId("AisthisiToken", "AIS", "https://aisthisi.art/metadata/") {}

    function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {
        require(tokenLockedFromTimestamp[tokenId] > block.timestamp || tokenUnlocked[tokenId], "AishtisiToken: Token locked");
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function unlockToken(bytes32 unlockHash, uint256 tokenId) public {
        require(msg.sender == ownerOf(tokenId), "AishtisiToken: Only the Owner can unlock the Token"); //not 100% sure about that one yet
        require(keccak256(abi.encode(unlockHash)) == tokenUnlockCodeHashes[tokenId], "AishtisiToken: Unlock Code Incorrect");
        tokenUnlocked[tokenId] = true;
        emit TokenUnlocked(tokenId, msg.sender);
    }

    /**
    * This one is the mint function that sets the unlock code, then calls the parent mint
    */
    function mint(address to, uint lockedFromTimestamp, bytes32 unlockHash) public {
        tokenLockedFromTimestamp[_tokenIds.current()] = lockedFromTimestamp;
        tokenUnlockCodeHashes[_tokenIds.current()] = unlockHash;
        _tokenIds.increment();
        super.mint(to);
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
    return string(abi.encodePacked(super.tokenURI(tokenId),".json"));
    }
}

I can deploy it using Truffle and everything is fine.

Now when I want to call this function on Truffle console

function unlockToken(bytes32 unlockHash, uint256 tokenId) public {
    require(msg.sender == ownerOf(tokenId), "AishtisiToken: Only the Owner can unlock the Token"); //not 100% sure about that one yet
    require(keccak256(abi.encode(unlockHash)) == tokenUnlockCodeHashes[tokenId], "AishtisiToken: Unlock Code Incorrect");
    tokenUnlocked[tokenId] = true;
    emit TokenUnlocked(tokenId, msg.sender);
}

Then I need to do these on Truffle console

let token = await AisthisiToken.deployed()

let txMint = await token.mint(accounts[1], Math.round(Date.now()/1000)+3, web3.utils.sha3(web3.utils.sha3('mySecretHash')))

let txUnlock = await token.unlockToken(web3.utils.sha3('mySecretHash'), 0, {from: accounts[1]})

My question is function unlockToken only expects 2 arguments bytes32 unlockHash and uint256 tokenId.
Why do I need to call that function with 3 arguments (web3.utils.sha3('mySecretHash'), 0, {from: accounts[1]})?

If I call that function using 2 arguments only like let txUnlock = await token.unlockToken(web3.utils.sha3('mySecretHash'), 0) , then it will think I am calling that function as accounts[0] and thus will return error, since only accounts[1] is allowed to unlock the Token. So, it looks like I must provide the 3rd argument to explicitly specify that account[1] is the one who runs that function. But where does the 3rd argument go, since it is not defined in the function?

2 arguments returns error

3 arguments OK

Also, Can function in solidity accept more parameters than what it expects? I tried with a simple test and run it on Remix IDE

contract Simple {
    uint public sum;
    function taker(uint _a, uint _b) public returns (uint) {
        sum = _a + _b;
        return sum;
    }
}

Deploy it on Remix and test it

enter image description here

It will return me error

transact to Simple.taker errored: Error encoding arguments: Error: types/values length mismatch (count={"types":2,"values":3}, value={"types":["uint256","uint256"],"values":["1","2","3"]}, code=INVALID_ARGUMENT, version=abi/5.5.0)

So, it looks like we can't feed a function with more arguments than what it expects ?

Best Answer

The token object is a wrapper (or facade) around your contract, and not the contract itself. If you read the truffle docs you will notice the following paragraph:

We passed an object as the third parameter to sendCoin. Note that the sendCoin function in our Solidity contract doesn't have a third parameter. What you see above is a special object that can always be passed as the last parameter to a function that lets you edit specific details about the transaction ("transaction params"). Here, we set the from address ensuring this transaction came from accounts[0]. The transaction params that you can set correspond to the fields in an Ethereum transaction: from, to, gas, gasPrice, value, data, nonce

This basically means that Truffle is creating a helper object that makes it convenient for you to call the methods of your contract. As you probably already know, every Ethereum contract method needs to be called in the context of a wallet(address/public key), so this third argument provided by Truffle is what lets you determine the context for the method call.

Related Topic