修改tokenclaim合约
This commit is contained in:
parent
7d9d6ce482
commit
ffe2788c6d
File diff suppressed because one or more lines are too long
26
contracts-dbg/Test1/TokenClaimTest.sol
Normal file
26
contracts-dbg/Test1/TokenClaimTest.sol
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
13
contracts-dbg/Test1/dbg.contract.json
Normal file
13
contracts-dbg/Test1/dbg.contract.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"entryPoint": "TokenClaimTest",
|
||||||
|
"solc": "0.8.26",
|
||||||
|
"sourceDirs": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"breakOnEntry": false,
|
||||||
|
"fork": {
|
||||||
|
"enable": false,
|
||||||
|
"url": "",
|
||||||
|
"blockNumber": 0
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
contracts/core/ClaimHistory.sol
Normal file
21
contracts/core/ClaimHistory.sol
Normal 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
13
dbg.project.json
Normal 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
@ -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"
|
@ -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
40
test/testBitTest.ts
Normal 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);
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
})
|
@ -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");
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user