update contract for token claim

This commit is contained in:
CounterFire2023 2024-08-19 15:13:18 +08:00
parent 1018027f2c
commit 7d9d6ce482
7 changed files with 78 additions and 54 deletions

File diff suppressed because one or more lines are too long

View File

@ -23,13 +23,7 @@ contract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {
event EventERC20Wallet(address erc20, address wallet); event EventERC20Wallet(address erc20, address wallet);
event EventVerifierUpdated(address indexed verifier); event EventVerifierUpdated(address indexed verifier);
event EventTokenClaimed( event EventTokenClaimed(address indexed user, address indexed token, address passport, uint256 amount, uint256 nonce);
address indexed user,
address indexed token,
address passport,
uint256 amount,
uint256 nonce
);
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;
@ -42,7 +36,7 @@ contract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {
* @dev update verifier address * @dev update verifier address
*/ */
function updateVerifier(address _verifier) external onlyOwner { function updateVerifier(address _verifier) external onlyOwner {
require(_verifier != address(0), "TokenClaimer: address can not be zero"); require(_verifier != address(0), "TokenClaim: address can not be zero");
verifier = _verifier; verifier = _verifier;
emit EventVerifierUpdated(_verifier); emit EventVerifierUpdated(_verifier);
} }
@ -65,30 +59,35 @@ contract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {
* @dev update ERC20 wallet * @dev update ERC20 wallet
*/ */
function updateERC20Wallet(address erc20, address wallet) external onlyOwner { function updateERC20Wallet(address erc20, address wallet) external onlyOwner {
require(erc20Wallets[erc20] != wallet, "TokenClaimer: ERC20 wallet not changed"); require(erc20Wallets[erc20] != wallet, "TokenClaim: ERC20 wallet not changed");
erc20Wallets[erc20] = wallet; erc20Wallets[erc20] = wallet;
emit EventERC20Wallet(erc20, wallet); emit EventERC20Wallet(erc20, wallet);
} }
/** /**
* @dev claim CEC with signature * @dev claim CEC with signature
* @param account address which eligible to claim, only for event log
* @param token address of token
* @param amount amount of token
* @param signTime time of signature
* @param saltNonce nonce of signature
* @param signature signature of claim
*/ */
function claim( function claim(
address passport, address account,
address token, address token,
uint256 amount, uint256 amount,
uint256 signTime, uint256 signTime,
uint256 saltNonce, uint256 saltNonce,
bytes calldata signature bytes calldata signature
) external signatureValid(signature) timeValid(signTime) nonReentrant whenNotPaused { ) external signatureValid(signature) timeValid(signTime) nonReentrant whenNotPaused {
require(passport != address(0), "TokenClaimer: passport address can not be zero"); require(erc20Wallets[token] != address(0), "TokenClaim: token is not supported");
require(erc20Wallets[token] != address(0), "TokenClaimer: token is not supported"); require(amount > 0, "TokenClaim: amount is zero");
require(amount > 0, "TokenClaimer: amount is zero");
address user = _msgSender(); address user = _msgSender();
bytes32 criteriaMessageHash = getMessageHash( bytes32 criteriaMessageHash = getMessageHash(
user, user,
passport, account,
token, token,
amount, amount,
_CACHED_THIS, _CACHED_THIS,
@ -98,14 +97,14 @@ contract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {
); );
checkSigner(verifier, criteriaMessageHash, signature); checkSigner(verifier, criteriaMessageHash, signature);
_useSignature(signature); _useSignature(signature);
claimedAmount[user][token] += amount; claimedAmount[account][token] += amount;
IERC20(token).safeTransferFrom(erc20Wallets[token], user, amount); IERC20(token).safeTransferFrom(erc20Wallets[token], user, amount);
emit EventTokenClaimed(user, token, passport, amount, saltNonce); emit EventTokenClaimed(user, token, account, amount, saltNonce);
} }
function getMessageHash( function getMessageHash(
address _user, address _user,
address _passport, address _account,
address _token, address _token,
uint256 _amount, uint256 _amount,
address _contract, address _contract,
@ -115,7 +114,7 @@ contract TokenClaim is HasSignature, ReentrancyGuard, Pausable, TimeChecker {
) public pure returns (bytes32) { ) public pure returns (bytes32) {
bytes memory encoded = abi.encodePacked( bytes memory encoded = abi.encodePacked(
_user, _user,
_passport, _account,
_token, _token,
_amount, _amount,
_contract, _contract,

File diff suppressed because one or more lines are too long

View File

@ -38,7 +38,7 @@
"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(\n address indexed user, \n address indexed token, \n address passport, \n uint256 amount, \n uint256 nonce\n );\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), \"TokenClaimer: 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, \"TokenClaimer: ERC20 wallet not changed\");\n erc20Wallets[erc20] = wallet;\n emit EventERC20Wallet(erc20, wallet);\n }\n\n /**\n * @dev claim CEC with signature\n */\n\n function claim(\n address passport,\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(passport != address(0), \"TokenClaimer: passport address can not be zero\");\n require(erc20Wallets[token] != address(0), \"TokenClaimer: token is not supported\");\n require(amount > 0, \"TokenClaimer: amount is zero\");\n address user = _msgSender();\n bytes32 criteriaMessageHash = getMessageHash(\n user,\n passport,\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[user][token] += amount;\n IERC20(token).safeTransferFrom(erc20Wallets[token], user, amount);\n emit EventTokenClaimed(user, token, passport, amount, saltNonce);\n }\n\n function getMessageHash(\n address _user,\n address _passport,\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 _passport,\n _token,\n _amount,\n _contract,\n _chainId,\n _signTime,\n _saltNonce\n );\n return keccak256(encoded);\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 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"
}, },
"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

@ -21,6 +21,6 @@
"name": "TokenClaim", "name": "TokenClaim",
"type": "logic", "type": "logic",
"json": "assets/contracts/TokenClaim.json", "json": "assets/contracts/TokenClaim.json",
"address": "0xc058411B15E544291765F15B13c88582b7bceaD0" "address": "0x2C7221588D4FBac2585D71618CD540e74c7413B8"
} }
] ]

View File

@ -20,6 +20,7 @@
"deploy:gameitemmall": "hardhat deploy --tags GameItemMall --network imtbl_test --reset", "deploy:gameitemmall": "hardhat deploy --tags GameItemMall --network imtbl_test --reset",
"solhint": "solhint --config ./.solhint.json", "solhint": "solhint --config ./.solhint.json",
"show_verify_list": "npx hardhat verify --list-networks", "show_verify_list": "npx hardhat verify --list-networks",
"convert-abi": "npx hardhat run scripts/convert-abi.js",
"verify_sample": "npx hardhat verify --network bsc_test --constructor-args ./verify/tokenclaim.js 0xee0044BF2ACEf7C3D7f6781d8f5DC4d2Dd1CE64c" "verify_sample": "npx hardhat verify --network bsc_test --constructor-args ./verify/tokenclaim.js 0xee0044BF2ACEf7C3D7f6781d8f5DC4d2Dd1CE64c"
}, },
"author": "", "author": "",

16
scripts/convert-abi.js Normal file
View File

@ -0,0 +1,16 @@
/**
* npx hardhat run scripts/convert-abi.js
*/
async function main() {
const jsonAbi = require("../artifacts/contracts/activity/TokenClaim.sol/TokenClaim.json").abi;
const iface = new ethers.Interface(jsonAbi);
console.log(iface.format(true));
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});