import { expect } from 'chai' import hre from "hardhat"; import { getBytes, solidityPackedKeccak256, } from 'ethers' import { loadFixture, } from "@nomicfoundation/hardhat-toolbox/network-helpers"; describe('TokenClaim', function() { async function deployOneContract() { const [owner, otherAccount] = await hre.ethers.getSigners(); const verifier = owner.address; const OperatorAllowlist = await hre.ethers.getContractFactory("OperatorAllowlist"); const operatorAllowlist = await OperatorAllowlist.deploy(owner.address); const CFFT = await hre.ethers.getContractFactory("ImmutableERC20MinterBurnerPermit"); const ft = await CFFT.deploy(owner.address, owner.address, owner.address, "test usdc", "usdc", '100000000000000000000000000'); await ft.grantMinterRole(owner.address); const amount = '10000000000000000000000'; await ft.mint(owner.address, amount); const TokenClaim = await hre.ethers.getContractFactory("TokenClaim"); const claimer = await TokenClaim.deploy( verifier, ft.target, verifier, 3600); // @ts-ignore await ft.connect(owner).approve(claimer.target, amount); const chainId = hre.network.config.chainId await operatorAllowlist.grantRegistrarRole(owner.address) await operatorAllowlist.addAddressToAllowlist([claimer.target]) return { claimer, owner, otherAccount, verifier, ft, chainId }; } describe("Deployment", function () { it('should deploy TokenClaim with the correct verifier', async function() { const { claimer, verifier } = await loadFixture(deployOneContract); expect(await claimer.verifier()).to.equal(verifier); }); it('should deploy TokenClaim with the correct FT address', async function() { const { claimer, ft, owner } = await loadFixture(deployOneContract); expect(await claimer.erc20Wallets(ft.target)).to.equal(owner.address); }); }) describe("Claim", function () { it('should claim token success', async function() { const { claimer, ft, owner, chainId, otherAccount } = await loadFixture(deployOneContract); const amount = 100; const nonce = (Math.random() * 1000) | 0; const now = (Date.now() / 1000) | 0; const bit = 1; let localMsgHash = solidityPackedKeccak256(["address","address", "address", "address", "uint256","uint256", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, ft.target, claimer.target, chainId, amount, bit, now, nonce]); const signature = await owner.signMessage(getBytes(localMsgHash)); const vals = [amount, bit, now, nonce]; // @ts-ignore await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature)) .to.emit(claimer, "EventTokenClaimed") .withArgs(otherAccount.address, ft.target, otherAccount.address, amount, bit); expect(await ft.balanceOf(otherAccount.address)).to.equal(amount); }); it('should revert claim token for signature used', async function() { const { claimer, ft, owner, chainId, otherAccount } = await loadFixture(deployOneContract); const amount = 100; const nonce = (Math.random() * 1000) | 0; const now = (Date.now() / 1000) | 0; const bit = 1; let localMsgHash = solidityPackedKeccak256(["address","address", "address", "address", "uint256","uint256", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, ft.target, claimer.target, chainId, amount, bit, now, nonce]); const signature = await owner.signMessage(getBytes(localMsgHash)); const vals = [amount, bit, now, nonce]; //@ts-ignore await claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature) // @ts-ignore await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature)).to.be.revertedWith("signature used. please send another transaction with new signature"); }); it('should revert claim token for timeout', async function() { const { claimer, ft, owner, chainId, otherAccount } = await loadFixture(deployOneContract); const amount = 100; // @ts-ignore // await ft.connect(owner).approve(claimer.target, '10000000000000000000000'); const nonce = (Math.random() * 1000) | 0; const now = (Date.now() / 1000 - 3601) | 0; const bit = 1; let localMsgHash = solidityPackedKeccak256(["address","address", "address", "address", "uint256","uint256", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, ft.target, claimer.target, chainId, amount, bit, now, nonce]); const signature = await owner.signMessage(getBytes(localMsgHash)); const vals = [amount, bit, now, nonce]; // @ts-ignore await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature)).to.be.revertedWith("expired, please send another transaction with new signature"); }); it('should revert claim token for token not support', async function() { const { claimer, ft, owner, chainId, otherAccount } = await loadFixture(deployOneContract); await claimer.updateERC20Wallet(ft.target, '0x0000000000000000000000000000000000000000'); const amount = 100; const nonce = (Math.random() * 1000) | 0; const now = (Date.now() / 1000) | 0; const bit = 1; let localMsgHash = solidityPackedKeccak256(["address","address", "address", "address", "uint256","uint256", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, ft.target, claimer.target, chainId, amount, bit, now, nonce]); const signature = await owner.signMessage(getBytes(localMsgHash)); const vals = [amount, bit, now, nonce]; // @ts-ignore await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature)).to.be.revertedWith("TokenClaim: token is not supported"); }); it('should revert claim token for condition check failed', async function() { const { claimer, ft, owner, chainId, otherAccount } = await loadFixture(deployOneContract); const amount = 100; const nonce = (Math.random() * 1000) | 0; const now = (Date.now() / 1000) | 0; let bit = 1; let localMsgHash = solidityPackedKeccak256(["address","address", "address", "address", "uint256","uint256", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, ft.target, claimer.target, chainId, amount, bit, now, nonce]); let signature = await owner.signMessage(getBytes(localMsgHash)); let vals = [amount, bit, now, nonce]; // @ts-ignore await claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature) bit = 1 << 2 ; localMsgHash = solidityPackedKeccak256(["address","address", "address", "address", "uint256","uint256", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, ft.target, claimer.target, chainId, amount, bit, now, nonce]); signature = await owner.signMessage(getBytes(localMsgHash)); vals = [amount, bit, now, nonce]; // @ts-ignore await claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature) bit = 1 | 1 << 2 ; localMsgHash = solidityPackedKeccak256(["address","address", "address", "address", "uint256","uint256", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, ft.target, claimer.target, chainId, amount, bit, now, nonce]); signature = await owner.signMessage(getBytes(localMsgHash)); vals = [amount, bit, now, nonce]; // @ts-ignore await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature)).to.be.revertedWith("TokenClaim: condition check failed"); }); }) })