修改tokenclaim合约

This commit is contained in:
CounterFire2023 2024-08-22 15:18:47 +08:00
parent 7d9d6ce482
commit ffe2788c6d
11 changed files with 241 additions and 132 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
contract TokenClaimTest {
event EvmPrint(string);
constructor() {
emit EvmPrint("TokenClaim.constructor");
// Here you can either deploy your contracts via `new`, eg:
// Counter counter = new Counter();
// counter.increment();
// or interact with an existing deployment by specifying a `fork` url in `dbg.project.json`
// eg:
// ICounter counter = ICounter(0x12345678.....)
// counter.increment();
//
// If you have correct symbols (`artifacts`) for the deployed contract, you can step-into calls.
uint256 abc = 123;
uint256 def = abc + 5;
emit EvmPrint("DbgEntry return");
}
}

View File

@ -0,0 +1,13 @@
{
"entryPoint": "TokenClaimTest",
"solc": "0.8.26",
"sourceDirs": [
"."
],
"breakOnEntry": false,
"fork": {
"enable": false,
"url": "",
"blockNumber": 0
}
}

View File

@ -17,13 +17,13 @@ contract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {
mapping(address token => address wallet) public erc20Wallets; mapping(address token => address wallet) public erc20Wallets;
// store user's claimed amount // store user's claimed status
mapping(address user => mapping(address token => uint256 amount)) public claimedAmount; mapping(address user => mapping(address token => uint256 claimedBit)) public claimedBitMap;
event EventERC20Wallet(address erc20, address wallet); event EventERC20Wallet(address erc20, address wallet);
event EventVerifierUpdated(address indexed verifier); event EventVerifierUpdated(address indexed verifier);
event EventTokenClaimed(address indexed user, address indexed token, address passport, uint256 amount, uint256 nonce); event EventTokenClaimed(address indexed user, address indexed token, address account, uint256 amount, uint256 bit);
constructor(address _wallet, address _token, address _verifier, uint256 _duration) TimeChecker(_duration) { constructor(address _wallet, address _token, address _verifier, uint256 _duration) TimeChecker(_duration) {
_CACHED_CHAIN_ID = block.chainid; _CACHED_CHAIN_ID = block.chainid;
@ -68,60 +68,54 @@ contract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {
* @dev claim CEC with signature * @dev claim CEC with signature
* @param account address which eligible to claim, only for event log * @param account address which eligible to claim, only for event log
* @param token address of token * @param token address of token
* @param amount amount of token * @param vals array of amount, bit, signTime, saltNonce
* @param signTime time of signature
* @param saltNonce nonce of signature
* @param signature signature of claim * @param signature signature of claim
*/ */
function claim( function claim(
address account, address account,
address token, address token,
uint256 amount, uint256[4] calldata vals, // amount, bit, signTime, saltNonce
uint256 signTime,
uint256 saltNonce,
bytes calldata signature bytes calldata signature
) external signatureValid(signature) timeValid(signTime) nonReentrant whenNotPaused { ) external signatureValid(signature) timeValid(vals[2]) nonReentrant whenNotPaused {
require(erc20Wallets[token] != address(0), "TokenClaim: token is not supported"); require(erc20Wallets[token] != address(0), "TokenClaim: token is not supported");
require(amount > 0, "TokenClaim: amount is zero"); require(vals[0] > 0, "TokenClaim: amount is zero");
uint256 current = claimedBitMap[account][token];
require(current & vals[1] == 0, "TokenClaim: condition check failed");
address user = _msgSender(); address user = _msgSender();
bytes32 criteriaMessageHash = getMessageHash( bytes32 criteriaMessageHash = getMessageHash(
user, user,
account, account,
token, token,
amount,
_CACHED_THIS, _CACHED_THIS,
_CACHED_CHAIN_ID, _CACHED_CHAIN_ID,
signTime, vals
saltNonce
); );
checkSigner(verifier, criteriaMessageHash, signature); checkSigner(verifier, criteriaMessageHash, signature);
_useSignature(signature); _useSignature(signature);
claimedAmount[account][token] += amount; claimedBitMap[account][token] = current | vals[1];
IERC20(token).safeTransferFrom(erc20Wallets[token], user, amount); IERC20(token).safeTransferFrom(erc20Wallets[token], user, vals[0]);
emit EventTokenClaimed(user, token, account, amount, saltNonce); emit EventTokenClaimed(user, token, account, vals[0], vals[1]);
} }
function getMessageHash( function getMessageHash(
address _user, address _user,
address _account, address _account,
address _token, address _token,
uint256 _amount,
address _contract, address _contract,
uint256 _chainId, uint256 _chainId,
uint256 _signTime, uint256[4] calldata _vals
uint256 _saltNonce
) public pure returns (bytes32) { ) public pure returns (bytes32) {
bytes memory encoded = abi.encodePacked( bytes memory encoded = abi.encodePacked(
_user, _user,
_account, _account,
_token, _token,
_amount,
_contract, _contract,
_chainId, _chainId
_signTime,
_saltNonce
); );
for (uint256 i = 0; i < _vals.length; i++) {
encoded = bytes.concat(encoded, abi.encodePacked(_vals[i]));
}
return keccak256(encoded); return keccak256(encoded);
} }
} }

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
contract ClaimHistory {
mapping(address user=> mapping(uint256 index => uint256 val)) private claimedBitMap;
function isClaimed(address user, uint256 index) public view returns (bool) {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
uint256 claimedWord = claimedBitMap[user][claimedWordIndex];
uint256 mask = (1 << claimedBitIndex);
return claimedWord & mask == mask;
}
function setClaimed(address user, uint256 index) internal {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
claimedBitMap[user][claimedWordIndex] = claimedBitMap[user][claimedWordIndex] | (1 << claimedBitIndex);
}
}

13
dbg.project.json Normal file
View File

@ -0,0 +1,13 @@
{
"contractsDir": "contracts-dbg",
"selectedContract": "Test1",
"autoOpen": true,
"breakOnEntry": false,
"symbols": {
"hardhat": {
"projectPaths": [
"/Users/zhl/Documents/workspace/crypto/contracts-imtbl"
]
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -38,7 +38,10 @@
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\nimport \"./math/SignedMath.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `int256` to its ASCII `string` decimal representation.\n */\n function toString(int256 value) internal pure returns (string memory) {\n return string(abi.encodePacked(value < 0 ? \"-\" : \"\", toString(SignedMath.abs(value))));\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n\n /**\n * @dev Returns true if the two strings are equal.\n */\n function equal(string memory a, string memory b) internal pure returns (bool) {\n return keccak256(bytes(a)) == keccak256(bytes(b));\n }\n}\n" "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\nimport \"./math/SignedMath.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n uint8 private constant _ADDRESS_LENGTH = 20;\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n */\n function toString(uint256 value) internal pure returns (string memory) {\n unchecked {\n uint256 length = Math.log10(value) + 1;\n string memory buffer = new string(length);\n uint256 ptr;\n /// @solidity memory-safe-assembly\n assembly {\n ptr := add(buffer, add(32, length))\n }\n while (true) {\n ptr--;\n /// @solidity memory-safe-assembly\n assembly {\n mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n }\n value /= 10;\n if (value == 0) break;\n }\n return buffer;\n }\n }\n\n /**\n * @dev Converts a `int256` to its ASCII `string` decimal representation.\n */\n function toString(int256 value) internal pure returns (string memory) {\n return string(abi.encodePacked(value < 0 ? \"-\" : \"\", toString(SignedMath.abs(value))));\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n */\n function toHexString(uint256 value) internal pure returns (string memory) {\n unchecked {\n return toHexString(value, Math.log256(value) + 1);\n }\n }\n\n /**\n * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n */\n function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n bytes memory buffer = new bytes(2 * length + 2);\n buffer[0] = \"0\";\n buffer[1] = \"x\";\n for (uint256 i = 2 * length + 1; i > 1; --i) {\n buffer[i] = _SYMBOLS[value & 0xf];\n value >>= 4;\n }\n require(value == 0, \"Strings: hex length insufficient\");\n return string(buffer);\n }\n\n /**\n * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n */\n function toHexString(address addr) internal pure returns (string memory) {\n return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n }\n\n /**\n * @dev Returns true if the two strings are equal.\n */\n function equal(string memory a, string memory b) internal pure returns (bool) {\n return keccak256(bytes(a)) == keccak256(bytes(b));\n }\n}\n"
}, },
"contracts/activity/TokenClaim.sol": { "contracts/activity/TokenClaim.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport {ReentrancyGuard} from \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport {Pausable} from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {HasSignature} from \"../core/HasSignature.sol\";\nimport {TimeChecker} from \"../utils/TimeChecker.sol\";\n\ncontract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {\n using SafeERC20 for IERC20;\n\n uint256 public immutable _CACHED_CHAIN_ID;\n address public immutable _CACHED_THIS;\n address public verifier;\n\n mapping(address token => address wallet) public erc20Wallets;\n\n // store user's claimed amount\n mapping(address user => mapping(address token => uint256 amount)) public claimedAmount;\n\n event EventERC20Wallet(address erc20, address wallet);\n\n event EventVerifierUpdated(address indexed verifier);\n event EventTokenClaimed(address indexed user, address indexed token, address passport, uint256 amount, uint256 nonce);\n\n constructor(address _wallet, address _token, address _verifier, uint256 _duration) TimeChecker(_duration) {\n _CACHED_CHAIN_ID = block.chainid;\n _CACHED_THIS = address(this);\n erc20Wallets[_token] = _wallet;\n verifier = _verifier;\n }\n\n /**\n * @dev update verifier address\n */\n function updateVerifier(address _verifier) external onlyOwner {\n require(_verifier != address(0), \"TokenClaim: address can not be zero\");\n verifier = _verifier;\n emit EventVerifierUpdated(_verifier);\n }\n\n /**\n * @dev update pause state\n */\n function pause() external onlyOwner {\n _pause();\n }\n\n /**\n * @dev update unpause state\n */\n function unpause() external onlyOwner {\n _unpause();\n }\n\n /**\n * @dev update ERC20 wallet\n */\n function updateERC20Wallet(address erc20, address wallet) external onlyOwner {\n require(erc20Wallets[erc20] != wallet, \"TokenClaim: ERC20 wallet not changed\");\n erc20Wallets[erc20] = wallet;\n emit EventERC20Wallet(erc20, wallet);\n }\n\n /**\n * @dev claim CEC with signature\n * @param account address which eligible to claim, only for event log\n * @param token address of token\n * @param amount amount of token\n * @param signTime time of signature\n * @param saltNonce nonce of signature\n * @param signature signature of claim\n */\n\n function claim(\n address account,\n address token,\n uint256 amount,\n uint256 signTime,\n uint256 saltNonce,\n bytes calldata signature\n ) external signatureValid(signature) timeValid(signTime) nonReentrant whenNotPaused {\n require(erc20Wallets[token] != address(0), \"TokenClaim: token is not supported\");\n require(amount > 0, \"TokenClaim: amount is zero\");\n address user = _msgSender();\n bytes32 criteriaMessageHash = getMessageHash(\n user,\n account,\n token,\n amount,\n _CACHED_THIS,\n _CACHED_CHAIN_ID,\n signTime,\n saltNonce\n );\n checkSigner(verifier, criteriaMessageHash, signature);\n _useSignature(signature);\n claimedAmount[account][token] += amount;\n IERC20(token).safeTransferFrom(erc20Wallets[token], user, amount);\n emit EventTokenClaimed(user, token, account, amount, saltNonce);\n }\n\n function getMessageHash(\n address _user,\n address _account,\n address _token,\n uint256 _amount,\n address _contract,\n uint256 _chainId,\n uint256 _signTime,\n uint256 _saltNonce\n ) public pure returns (bytes32) {\n bytes memory encoded = abi.encodePacked(\n _user,\n _account,\n _token,\n _amount,\n _contract,\n _chainId,\n _signTime,\n _saltNonce\n );\n return keccak256(encoded);\n }\n}\n" "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport {ReentrancyGuard} from \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport {Pausable} from \"@openzeppelin/contracts/security/Pausable.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {HasSignature} from \"../core/HasSignature.sol\";\nimport {TimeChecker} from \"../utils/TimeChecker.sol\";\n\ncontract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {\n using SafeERC20 for IERC20;\n\n uint256 public immutable _CACHED_CHAIN_ID;\n address public immutable _CACHED_THIS;\n address public verifier;\n\n mapping(address token => address wallet) public erc20Wallets;\n\n // store user's claimed status\n mapping(address user => mapping(address token => uint256 claimedBit)) public claimedBitMap;\n\n event EventERC20Wallet(address erc20, address wallet);\n\n event EventVerifierUpdated(address indexed verifier);\n event EventTokenClaimed(address indexed user, address indexed token, address account, uint256 amount, uint256 bit);\n\n constructor(address _wallet, address _token, address _verifier, uint256 _duration) TimeChecker(_duration) {\n _CACHED_CHAIN_ID = block.chainid;\n _CACHED_THIS = address(this);\n erc20Wallets[_token] = _wallet;\n verifier = _verifier;\n }\n\n /**\n * @dev update verifier address\n */\n function updateVerifier(address _verifier) external onlyOwner {\n require(_verifier != address(0), \"TokenClaim: address can not be zero\");\n verifier = _verifier;\n emit EventVerifierUpdated(_verifier);\n }\n\n /**\n * @dev update pause state\n */\n function pause() external onlyOwner {\n _pause();\n }\n\n /**\n * @dev update unpause state\n */\n function unpause() external onlyOwner {\n _unpause();\n }\n\n /**\n * @dev update ERC20 wallet\n */\n function updateERC20Wallet(address erc20, address wallet) external onlyOwner {\n require(erc20Wallets[erc20] != wallet, \"TokenClaim: ERC20 wallet not changed\");\n erc20Wallets[erc20] = wallet;\n emit EventERC20Wallet(erc20, wallet);\n }\n\n /**\n * @dev claim CEC with signature\n * @param account address which eligible to claim, only for event log\n * @param token address of token\n * @param vals array of amount, bit, signTime, saltNonce\n * @param signature signature of claim\n */\n\n function claim(\n address account,\n address token,\n uint256[4] calldata vals, // amount, bit, signTime, saltNonce\n bytes calldata signature\n ) external signatureValid(signature) timeValid(vals[2]) nonReentrant whenNotPaused {\n require(erc20Wallets[token] != address(0), \"TokenClaim: token is not supported\");\n require(vals[0] > 0, \"TokenClaim: amount is zero\");\n uint256 current = claimedBitMap[account][token];\n require(current & vals[1] == 0, \"TokenClaim: condition check failed\");\n address user = _msgSender();\n bytes32 criteriaMessageHash = getMessageHash(\n user,\n account,\n token,\n _CACHED_THIS,\n _CACHED_CHAIN_ID,\n vals\n );\n checkSigner(verifier, criteriaMessageHash, signature);\n _useSignature(signature);\n claimedBitMap[account][token] = current | vals[1];\n IERC20(token).safeTransferFrom(erc20Wallets[token], user, vals[0]);\n emit EventTokenClaimed(user, token, account, vals[0], vals[1]);\n }\n\n function getMessageHash(\n address _user,\n address _account,\n address _token,\n address _contract,\n uint256 _chainId,\n uint256[4] calldata _vals\n ) public pure returns (bytes32) {\n bytes memory encoded = abi.encodePacked(\n _user,\n _account,\n _token,\n _contract,\n _chainId\n );\n for (uint256 i = 0; i < _vals.length; i++) {\n encoded = bytes.concat(encoded, abi.encodePacked(_vals[i]));\n }\n return keccak256(encoded);\n }\n}\n"
},
"contracts/core/ClaimHistory.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\ncontract ClaimHistory {\n mapping(address user=> mapping(uint256 index => uint256 val)) private claimedBitMap;\n\n function isClaimed(address user, uint256 index) public view returns (bool) {\n uint256 claimedWordIndex = index / 256;\n uint256 claimedBitIndex = index % 256;\n uint256 claimedWord = claimedBitMap[user][claimedWordIndex];\n uint256 mask = (1 << claimedBitIndex);\n return claimedWord & mask == mask;\n }\n\n function setClaimed(address user, uint256 index) internal {\n uint256 claimedWordIndex = index / 256;\n uint256 claimedBitIndex = index % 256;\n claimedBitMap[user][claimedWordIndex] = claimedBitMap[user][claimedWordIndex] | (1 << claimedBitIndex);\n }\n \n}\n"
}, },
"contracts/core/HasSignature.sol": { "contracts/core/HasSignature.sol": {
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\nimport {ECDSA} from \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract HasSignature is Ownable {\n mapping(bytes signature => bool status) private _usedSignatures;\n\n function checkSigner(\n address signer,\n bytes32 hash,\n bytes memory signature\n ) public pure {\n bytes32 ethSignedMessageHash = ECDSA.toEthSignedMessageHash(hash);\n\n address recovered = ECDSA.recover(ethSignedMessageHash, signature);\n require(recovered == signer, \"invalid signature\");\n }\n\n modifier signatureValid(bytes calldata signature) {\n require(\n !_usedSignatures[signature],\n \"signature used. please send another transaction with new signature\"\n );\n _;\n }\n\n function _useSignature(bytes calldata signature) internal {\n if (!_usedSignatures[signature]) {\n _usedSignatures[signature] = true;\n }\n }\n}\n" "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\nimport {ECDSA} from \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract HasSignature is Ownable {\n mapping(bytes signature => bool status) private _usedSignatures;\n\n function checkSigner(\n address signer,\n bytes32 hash,\n bytes memory signature\n ) public pure {\n bytes32 ethSignedMessageHash = ECDSA.toEthSignedMessageHash(hash);\n\n address recovered = ECDSA.recover(ethSignedMessageHash, signature);\n require(recovered == signer, \"invalid signature\");\n }\n\n modifier signatureValid(bytes calldata signature) {\n require(\n !_usedSignatures[signature],\n \"signature used. please send another transaction with new signature\"\n );\n _;\n }\n\n function _useSignature(bytes calldata signature) internal {\n if (!_usedSignatures[signature]) {\n _usedSignatures[signature] = true;\n }\n }\n}\n"

View File

@ -17,10 +17,16 @@
"json": "assets/contracts/GameItemMall.json", "json": "assets/contracts/GameItemMall.json",
"address": "0xaE08adb5278B107D2501e7c61907e41FEf3887D7" "address": "0xaE08adb5278B107D2501e7c61907e41FEf3887D7"
}, },
{
"name": "TestCEC",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0xe34c5ea0c3083d11a735dc0609533b92130319f5"
},
{ {
"name": "TokenClaim", "name": "TokenClaim",
"type": "logic", "type": "logic",
"json": "assets/contracts/TokenClaim.json", "json": "assets/contracts/TokenClaim.json",
"address": "0x2C7221588D4FBac2585D71618CD540e74c7413B8" "address": "0xC95bDFAaFBf79b435e4d2bF8d77842fc19e6fE56"
} }
] ]

40
test/testBitTest.ts Normal file
View File

@ -0,0 +1,40 @@
import { expect } from 'chai'
import hre from "hardhat";
import {
getBytes,
solidityPackedKeccak256,
} from 'ethers'
import {
loadFixture,
} from "@nomicfoundation/hardhat-toolbox/network-helpers";
describe('TestBit', function() {
async function deployOneContract() {
// Contracts are deployed using the first signer/account by default
const [owner, otherAccount] = await hre.ethers.getSigners();
const verifier = owner.address;
const TestBit = await hre.ethers.getContractFactory("TestBit");
const testBit = await TestBit.deploy( );
const chainId = hre.network.config.chainId
return { testBit, owner, otherAccount, verifier, chainId };
}
describe("Deployment", function () {
it('should deploy TestBit', async function() {
const { testBit } = await loadFixture(deployOneContract);
const idx = '0';
const initVal = 0;
await testBit.setVal(idx, initVal);
const nextVal = 0 << 1
await expect(testBit.checkVal(idx, nextVal))
.to.emit(testBit, "ValueChecked")
.withArgs(0, nextVal | initVal);
});
})
})

View File

@ -47,17 +47,18 @@ describe('TokenClaim', function() {
it('should claim token success', async function() { it('should claim token success', async function() {
const { claimer, ft, owner, chainId, otherAccount } = await loadFixture(deployOneContract); const { claimer, ft, owner, chainId, otherAccount } = await loadFixture(deployOneContract);
const amount = 100; const amount = 100;
// @ts-ignore
// await ft.connect(owner).approve(claimer.target, '10000000000000000000000');
const nonce = (Math.random() * 1000) | 0; const nonce = (Math.random() * 1000) | 0;
const now = (Date.now() / 1000) | 0; const now = (Date.now() / 1000) | 0;
let localMsgHash = solidityPackedKeccak256(["address","address", "address", "uint256", "address", "uint256", "uint256", "uint256"], const bit = 1;
[otherAccount.address, otherAccount.address, ft.target, amount, claimer.target, chainId, now, nonce]); 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 signature = await owner.signMessage(getBytes(localMsgHash));
const vals = [amount, bit, now, nonce];
// @ts-ignore // @ts-ignore
await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, amount, now, nonce, signature)) await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature))
.to.emit(claimer, "EventTokenClaimed") .to.emit(claimer, "EventTokenClaimed")
.withArgs(otherAccount.address, ft.target, otherAccount.address, amount, nonce); .withArgs(otherAccount.address, ft.target, otherAccount.address, amount, bit);
expect(await ft.balanceOf(otherAccount.address)).to.equal(amount); expect(await ft.balanceOf(otherAccount.address)).to.equal(amount);
}); });
@ -66,13 +67,15 @@ describe('TokenClaim', function() {
const amount = 100; const amount = 100;
const nonce = (Math.random() * 1000) | 0; const nonce = (Math.random() * 1000) | 0;
const now = (Date.now() / 1000) | 0; const now = (Date.now() / 1000) | 0;
let localMsgHash = solidityPackedKeccak256(["address","address", "address", "uint256", "address", "uint256", "uint256", "uint256"], const bit = 1;
[otherAccount.address, otherAccount.address, ft.target, amount, claimer.target, chainId, now, nonce]); 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 signature = await owner.signMessage(getBytes(localMsgHash));
const vals = [amount, bit, now, nonce];
//@ts-ignore //@ts-ignore
await claimer.connect(otherAccount).claim(otherAccount.address, ft.target, amount, now, nonce, signature) await claimer.connect(otherAccount).claim(otherAccount.address, ft.target, vals, signature)
// @ts-ignore // @ts-ignore
await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, amount, now, nonce, signature)).to.be.revertedWith("signature used. please send another transaction with new signature"); 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() { it('should revert claim token for timeout', async function() {
@ -82,11 +85,13 @@ describe('TokenClaim', function() {
// await ft.connect(owner).approve(claimer.target, '10000000000000000000000'); // await ft.connect(owner).approve(claimer.target, '10000000000000000000000');
const nonce = (Math.random() * 1000) | 0; const nonce = (Math.random() * 1000) | 0;
const now = (Date.now() / 1000 - 3601) | 0; const now = (Date.now() / 1000 - 3601) | 0;
let localMsgHash = solidityPackedKeccak256(["address","address", "address", "uint256", "address", "uint256", "uint256", "uint256"], const bit = 1;
[otherAccount.address, otherAccount.address, ft.target, amount, claimer.target, chainId, now, nonce]); 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 signature = await owner.signMessage(getBytes(localMsgHash));
const vals = [amount, bit, now, nonce];
// @ts-ignore // @ts-ignore
await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, amount, now, nonce, signature)).to.be.revertedWith("expired, please send another transaction with new signature"); 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() { it('should revert claim token for token not support', async function() {
@ -95,11 +100,41 @@ describe('TokenClaim', function() {
const amount = 100; const amount = 100;
const nonce = (Math.random() * 1000) | 0; const nonce = (Math.random() * 1000) | 0;
const now = (Date.now() / 1000) | 0; const now = (Date.now() / 1000) | 0;
let localMsgHash = solidityPackedKeccak256(["address","address", "address", "uint256", "address", "uint256", "uint256", "uint256"], const bit = 1;
[otherAccount.address, otherAccount.address, ft.target, amount, claimer.target, chainId, now, nonce]); 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 signature = await owner.signMessage(getBytes(localMsgHash));
const vals = [amount, bit, now, nonce];
// @ts-ignore // @ts-ignore
await expect(claimer.connect(otherAccount).claim(otherAccount.address, ft.target, amount, now, nonce, signature)).to.be.revertedWith("TokenClaimer: token is not supported"); 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");
}); });
}) })
}) })