1

Code: https://www.contractreader.io/contract/mainnet/0x0C7D4bdA40CF78F9F1BAf6e3D5774f1E22c9CC4B

My question is about this verifyCalldata() call:

 function verifyAddress( uint256 _projectId, bytes32[] calldata _proof, address _address ) public view returns (bool) { return _proof.verifyCalldata( projectMerkleRoot[_projectId], hashAddress(_address) ); } 

If _proof is a bytes32[], how can it have the method verifyCalldata()?

bytes32[] is an array of bytes, right?

I see verifyCalldata() in @openzeppelin-4.7/contracts/utils/cryptography/MerkleProof.sol and it's signature is:

library MerkleProof { ... function verifyCalldata( bytes32[] calldata proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { ... 

So I would expect the call to be something like:

return MerkleProof.verifyCalldata( merkleProof, merkleRoot, leaf ); 

but it is not. Why?

I feel like there's some magic (or shorthand) I haven't learned yet.

1
  • A better title might be "How can a function with 3 parameters, be called with only 2 arguments?" ? Commented Dec 1, 2023 at 9:18

1 Answer 1

1

The trick is the using A for B directive at line 59:

using MerkleProof for bytes32[]; 

With that operation, you ask Solidity to consider all the methods it finds in the MerkleProof library, having a byte32[] as a first parameter, as valid methods of any byte32[] variable.

So, after that line, for example, all the following functions are valid methods for any byte32[] variable:

function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool); function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32); ... 

As an additional trick, Solidity passes the same variable as the method's first parameter so that the following two syntaxes become equivalent.

// with `using MerkleProof for bytes32[];` return _proof.verifyCalldata(projectMerkleRoot[_projectId], hashAddress(_address)); // without `using MerkleProof for bytes32[];` return MerkleProof.verifyCalldata(_proof, projectMerkleRoot[_projectId], hashAddress(_address)); 

You can read more about the use of the using A for B directive in the official Solidity documentation.

As a side note, library syntax is convenient for clarifying the code, but using it here seems an unnecessary overdesigned magic trick; it introduces misunderstanding instead of making things clearer.

The syntax you expected is the one that makes the most sense to me, so don't take this contract as an example. I suggest developing your contract so that lines of code are straightforward, not stylish.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.