使用merkle tree空投,白名单验证

  • dopa
  • 更新于 2022-10-04 20:47
  • 阅读 3996

Merkle Tree在高效验证数据的同时减少了链上计算和存储,因为非常适合基于区块链的白名单验证,空投,IDO等需要验证数据的业务。

merkle tree

默克尔树,在区块链出现前,曾广泛用于文件系统和P2P系统中。 在区块链中,默克尔树常用于高效验证数据,如,实现空投,白名单,IDO,混币器等。

默克尔树是一种hash树,底层叶子节点的hash变动会一层一层的传递直到树根root,所以roothash实际代表了底层所有数据的摘要,通过验证roothash来确定是否是它的叶子节点。那么只需要在链上记录树根就可以开始验证其叶子节点的归属,每当新增叶子节点,也只需更新roothash即可,而不必存储整棵树,并且roothash的计算也可放在链下进行。

Merkle Tree在高效验证数据的同时减少了链上计算和存储,因为非常适合基于区块链的白名单验证,空投,IDO等需要验证数据的业务。

创建Merkle Tree

为配合solidity,选择使用keccak256算法; merkletree.getProof()返回的是Array of objects;merkletree.getHexProof()返回的Proof array as hex strings,更适合作为参数传入合约。

const { MerkleTree } = require('merkletreejs')
const keccak256 = require('keccak256')

let whitelist = [
    "0xc11cb63bdca7627f74ec69c14612522fdb7a0c20",
    "0x1499b8312e6fe58b5d1164d4eccf795367c9e1d3",
    "0x55f510be6ab4c7e07ec6ee637aa83574975d6898",
    "0xcc2fe3615a45fcacc3534d53be41c6543a0a312d",
    "0xee226379db83cffc681495730c11fdde79ba4c0c",
    "0x55f510be6ab4c7e07ec6ee637aa83574975d6898",
    "0x18b2a687610328590bc8f2e5fedde3b582a49cda"
]

const leafNodes = whitelist.map(addr => keccak256(addr));
const merkletree = new MerkleTree(leafNodes, keccak256, {sortPairs: true});

const rootHash = merkletree.getRoot().toString('hex');
console.log("rootHash is: ", rootHash);
console.log(merkletree.toString());

console.log("--------verify------------");
const claimingAddr2 = leafNodes[0];     
const hexProof = merkletree.getHexProof(claimingAddr2);    
console.log(hexProof);      // Proof array as hex strings   
console.log(merkletree.verify(hexProof, claimingAddr2, rootHash));  

merkle白名单

使用openzeppelin的MerkleProof.verify进行验证。 在合约中只需要存储roothash,验证时用户需传入默克尔树证明,新增叶子节点时,也只需要更新roothash。 验证通过后可领取空投,并将状态改为已领取,避免重复领取。

前端

  1. 存储所有符合条件的地址,这样当用户访问站点时,可以立即查看他们是否符合条件;

  2. 如果符合条件,再调用智能合约验证,并执行接下来的操作;

    合约

  3. 设置merkle树根,并提供验证接口;

  4. 白名单地址可自行调用合约来领取空投;

    
    import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

contract WhiteListMerkle { bytes32 public merkleRoot = 0xb63cec15d66cdcc08199c64a2105f32356c226151d1d1b43a6e9d68fb2e7684f;

mapping(address => bool) public whitelistClaimed;

// verify the provided _merkleProof
function whitelist(bytes32[] calldata _merkleProof) public{
    require(!whitelistClaimed[msg.sender], "address has already claimed.");

    bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
    require(MerkleProof.verify(_merkleProof, merkleRoot, leaf), "Invaild proof.");

    whitelistClaimed[msg.sender] = true;
}

function setRootHash(bytes32 roothash_) external {
    merkleRoot = roothash_;
}

}

## 合约交互
调用合约API进行验证,需传入默克尔树证明。`merkletree.getHexProof()`

async function main() { const WhiteListMerkle = await ethers.getContractFactory("WhiteListMerkle"); const whiteListMerkle = await WhiteListMerkle.attach(contractAddress);

// verify 
// const merkleProof = [
//     '0x497de751e77af5954dbcae3eba7c203606e8c52006fb1fcc2c0df507b976363a',
//     '0x71249e6e1d6f7059bd2f07ab00601bade6fbd5ffd7c0d63b983cab0bc097bfe3',
//     '0xa25d73d7d8dbcf73b7b71286c8de55451b4efa13be5bac629d7b2c966588ea0c'
//   ]
// const tx_verify = await whiteListMerkle.whitelist(merkleProof);
// console.log(tx_verify);

// view whitelistClaimed
const view_claimed = await whiteListMerkle.whitelistClaimed(owner.address);
console.log(view_claimed);

}

## merkle空投完整案例

contract MerkleDistributor { address public immutable token; bytes32 public immutable merkleRoot;

// This is a packed array of booleans.
mapping(uint256 => uint256) private claimedBitMap;

constructor(address token_, bytes32 merkleRoot_) {
    token = token_;
    merkleRoot = merkleRoot_;
}

function isClaimed(uint256 index) public view returns (bool) {
    uint256 claimedWordIndex = index / 256;
    uint256 claimedBitIndex = index % 256;
    uint256 claimedWord = claimedBitMap[claimedWordIndex];
    uint256 mask = (1 << claimedBitIndex);
    return claimedWord & mask == mask;
}

function _setClaimed(uint256 index) private {
    uint256 claimedWordIndex = index / 256;
    uint256 claimedBitIndex = index % 256;
    claimedBitMap[claimedWordIndex] = claimedBitMap[claimedWordIndex] | (1 << claimedBitIndex);
}

function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) external {
    require(!isClaimed(index), 'MerkleDistributor: Drop already claimed.');

    // Verify the merkle proof.
    bytes32 node = keccak256(abi.encodePacked(index, account, amount));
    require(MerkleProof.verify(merkleProof, merkleRoot, node), 'MerkleDistributor: Invalid proof.');

    // Mark it claimed and send the token.
    _setClaimed(index);
    require(IERC20(token).transfer(account, amount), 'MerkleDistributor: Transfer failed.');

}

}

点赞 1
收藏 6
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

2 条评论

请先 登录 后评论
dopa
dopa
web3 is cool !