增加一个用于claim usdt的合约

This commit is contained in:
CounterFire2023 2024-01-12 15:07:18 +08:00
parent 89efe5253f
commit f8e7069439
12 changed files with 19366 additions and 63 deletions

View File

@ -38317,7 +38317,7 @@
} }
}, },
"schemaVersion": "3.4.16", "schemaVersion": "3.4.16",
"updatedAt": "2024-01-10T06:03:43.853Z", "updatedAt": "2024-01-12T05:44:22.513Z",
"networkType": "ethereum", "networkType": "ethereum",
"devdoc": { "devdoc": {
"kind": "dev", "kind": "dev",

File diff suppressed because one or more lines are too long

View File

@ -17022,7 +17022,7 @@
} }
}, },
"schemaVersion": "3.4.16", "schemaVersion": "3.4.16",
"updatedAt": "2023-12-27T06:11:16.457Z", "updatedAt": "2024-01-12T05:44:22.582Z",
"networkType": "ethereum", "networkType": "ethereum",
"devdoc": { "devdoc": {
"kind": "dev", "kind": "dev",

View File

@ -3259,7 +3259,7 @@
} }
}, },
"schemaVersion": "3.4.16", "schemaVersion": "3.4.16",
"updatedAt": "2024-01-10T06:03:43.872Z", "updatedAt": "2024-01-12T05:44:22.587Z",
"networkType": "ethereum", "networkType": "ethereum",
"devdoc": { "devdoc": {
"kind": "dev", "kind": "dev",

View File

@ -33015,7 +33015,7 @@
} }
}, },
"schemaVersion": "3.4.16", "schemaVersion": "3.4.16",
"updatedAt": "2024-01-10T06:03:43.865Z", "updatedAt": "2024-01-12T05:44:22.527Z",
"networkType": "ethereum", "networkType": "ethereum",
"devdoc": { "devdoc": {
"kind": "dev", "kind": "dev",

View File

@ -9715,7 +9715,7 @@
} }
}, },
"schemaVersion": "3.4.16", "schemaVersion": "3.4.16",
"updatedAt": "2024-01-10T06:03:43.845Z", "updatedAt": "2024-01-12T05:44:22.504Z",
"networkType": "ethereum", "networkType": "ethereum",
"devdoc": { "devdoc": {
"kind": "dev", "kind": "dev",

View File

@ -0,0 +1,120 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../core/HasSignature.sol";
import "../utils/TimeChecker.sol";
/**
* @title ClaimToken
* @dev for user to claim token
*/
contract ClaimToken is HasSignature, TimeChecker {
using SafeERC20 for IERC20;
uint256 private immutable _CACHED_CHAIN_ID;
address private immutable _CACHED_THIS;
address private verifier;
address private paymentAddress;
mapping(address => bool) public tokenSupported;
mapping(address => uint256) public claimHistory;
struct TestStuct {
address user;
address nft;
uint256 tokenId;
uint64 start;
uint64 stakeTime;
}
event TestEvent (
address indexed user,
TestStuct info
);
event TokenClaimed(
address indexed tokenAddress,
address indexed to,
uint256 indexed nonce,
uint256 amount
);
event TokenSupportUpdated(address indexed tokenAddress, bool support);
event VerifierUpdated(address indexed verifier);
event PaymentAddressUpdated(address indexed paymentAddress);
constructor() {
_CACHED_CHAIN_ID = block.chainid;
_CACHED_THIS = address(this);
}
function updateTokenSupport(address _token, bool _support) external onlyOwner {
tokenSupported[_token] = _support;
emit TokenSupportUpdated(_token, _support);
}
/**
* @dev update verifier address
*/
function updateVerifier(address _verifier) external onlyOwner {
require(_verifier != address(0), "ClaimToken: address can not be zero");
verifier = _verifier;
emit VerifierUpdated(_verifier);
}
/**
* @dev update payment address
*/
function updatePaymentAddress(address _paymentAddress) external onlyOwner {
require(
_paymentAddress != address(0),
"ClaimToken: payment address can not be zero"
);
paymentAddress = _paymentAddress;
emit PaymentAddressUpdated(_paymentAddress);
}
function claim(
address _token,
uint256 _amount,
uint256 _startTime,
uint256 _saltNonce,
bytes calldata _signature
) external signatureValid(_signature) timeValid(_startTime) {
require(tokenSupported[_token], "ClaimToken: unsupported token");
address to = _msgSender();
bytes32 criteriaMessageHash = getMessageHash(
to,
_token,
_CACHED_THIS,
_CACHED_CHAIN_ID,
_amount,
_startTime,
_saltNonce
);
checkSigner(verifier, criteriaMessageHash, _signature);
IERC20(_token).safeTransferFrom(paymentAddress, to, _amount);
claimHistory[to] = _amount;
_useSignature(_signature);
emit TokenClaimed(_token, to, _saltNonce, _amount);
}
function getMessageHash(
address _to,
address _address,
address _contract,
uint256 _chainId,
uint256 _amount,
uint256 _startTime,
uint256 _saltNonce
) public pure returns (bytes32) {
bytes memory encoded = abi.encodePacked(
_to,
_address,
_contract,
_chainId,
_amount,
_startTime,
_saltNonce
);
return keccak256(encoded);
}
}

View File

@ -0,0 +1,61 @@
const base = require("../scripts/base");
const NftBuner = artifacts.require("logic/NftBuner");
const Badge = artifacts.require("tokens/erc721/BEBadge");
const NFTSbt = artifacts.require("tokens/erc721/NFTSbt");
const ClaimToken = artifacts.require("activity/ClaimToken");
const FT = artifacts.require("tokens/erc20/FT");
module.exports = async function (deployer, network, accounts) {
const config = require(`../config/config_${network}`);
let cfgs = base.loadData({ network });
// await deployer.deploy(NftBuner);
// const bunerInstance = await NftBuner.deployed();
// if (bunerInstance) {
// console.log("NftBuner successfully deployed.");
// console.log("address: " + bunerInstance.address);
// }
// base.updateArray({
// name: "NftBuner",
// type: "logic",
// json: "assets/contracts/NftBuner.json",
// address: bunerInstance.address,
// network,
// });
// const gacha = cfgs.find((c) => c.name === "Gacha").address
// const gachaInstance = await Badge.at(gacha);
// await gachaInstance.setBurnRole(bunerInstance.address);
// console.log("gacha setBurnRole successfully deployed.");
// const candy = cfgs.find((c) => c.name === "Candy").address
// const candyInstance = await Badge.at(candy);
// await candyInstance.setBurnRole(bunerInstance.address);
// console.log("candy setBurnRole successfully deployed.");
// const explorer = cfgs.find((c) => c.name === "Explorer").address
// const explorerInstance = await NFTSbt.at(explorer);
// await explorerInstance.setBurnRole(bunerInstance.address);
// console.log("explorer setBurnRole successfully deployed.");
await deployer.deploy(ClaimToken);
const claimInstance = await ClaimToken.deployed();
if (claimInstance) {
console.log("ClaimToken successfully deployed.");
console.log("address: " + claimInstance.address);
}
base.updateArray({
name: "ClaimToken",
type: "logic",
json: "assets/contracts/ClaimToken.json",
address: claimInstance.address,
network,
});
const usdt = cfgs.find((c) => c.name === "BEUSDT").address
const usdtInstance = await FT.at(usdt);
await claimInstance.updateTokenSupport(usdtInstance.address, true);
console.log("updateTokenSupport successfully deployed.");
await claimInstance.updateVerifier(config.admins.admin);
console.log("updateVerifier successfully deployed.");
await claimInstance.updatePaymentAddress(config.admins.admin);
console.log("updatePaymentAddress successfully deployed.");
};

View File

@ -1,34 +0,0 @@
const base = require("../scripts/base");
const NftBuner = artifacts.require("logic/NftBuner");
const Badge = artifacts.require("tokens/erc721/BEBadge");
const NFTSbt = artifacts.require("tokens/erc721/NFTSbt");
module.exports = async function (deployer, network, accounts) {
await deployer.deploy(NftBuner);
const bunerInstance = await NftBuner.deployed();
if (bunerInstance) {
console.log("NftBuner successfully deployed.");
console.log("address: " + bunerInstance.address);
}
base.updateArray({
name: "NftBuner",
type: "logic",
json: "assets/contracts/NftBuner.json",
address: bunerInstance.address,
network,
});
let cfgs = base.loadData({ network });
const gacha = cfgs.find((c) => c.name === "Gacha").address
const gachaInstance = await Badge.at(gacha);
await gachaInstance.setBurnRole(bunerInstance.address);
console.log("gacha setBurnRole successfully deployed.");
const candy = cfgs.find((c) => c.name === "Candy").address
const candyInstance = await Badge.at(candy);
await candyInstance.setBurnRole(bunerInstance.address);
console.log("candy setBurnRole successfully deployed.");
const explorer = cfgs.find((c) => c.name === "Explorer").address
const explorerInstance = await NFTSbt.at(explorer);
await explorerInstance.setBurnRole(bunerInstance.address);
console.log("explorer setBurnRole successfully deployed.");
};

View File

@ -154,5 +154,11 @@
"type": "logic", "type": "logic",
"json": "assets/contracts/NftBuner.json", "json": "assets/contracts/NftBuner.json",
"address": "0xca5525178b0826A7C0835F0b7a383C802e10b6c7" "address": "0xca5525178b0826A7C0835F0b7a383C802e10b6c7"
},
{
"name": "ClaimToken",
"type": "logic",
"json": "assets/contracts/ClaimToken.json",
"address": "0x2119A65A2cEE90B4E0184DBC4A7aDF7093730919"
} }
] ]

View File

@ -17,30 +17,6 @@
"json": "assets/contracts/NftDistributor.json", "json": "assets/contracts/NftDistributor.json",
"address": "0xf520767FDBE4443F89F37714EE1BeEC0BDF05B79" "address": "0xf520767FDBE4443F89F37714EE1BeEC0BDF05B79"
}, },
{
"name": "CEC",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0x012A2C3C6f7F198A52E2820aEB48208d453EEa17"
},
{
"name": "CEG",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0x931985167bD678bA850156fc717443c34C856424"
},
{
"name": "BEUSDC",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0x4a931bfa7331009b0973c615b6D0B4FCCd5A89bb"
},
{
"name": "BEUSDT",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0xb48c8Fa8Bc892f2F48A5Ea3C5dD3335aFECB9b0c"
},
{ {
"name": "HERO", "name": "HERO",
"type": "erc721", "type": "erc721",
@ -136,5 +112,35 @@
"type": "logic", "type": "logic",
"json": "assets/contracts/GoldBricksFactory.json", "json": "assets/contracts/GoldBricksFactory.json",
"address": "0x6F6BfF298e182882091B8D0FF40208d09aE96B62" "address": "0x6F6BfF298e182882091B8D0FF40208d09aE96B62"
},
{
"name": "Explorer",
"type": "erc721",
"json": "assets/contracts/NFTSbt.json",
"address": "0x2D8BE6BF0baA74e0A907016679CaE9190e80dD0A"
},
{
"name": "CEC",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
},
{
"name": "CEG",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0xD833215cBcc3f914bD1C9ece3EE7BF8B14f841bb"
},
{
"name": "BEUSDC",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0x9561C133DD8580860B6b7E504bC5Aa500f0f06a7"
},
{
"name": "BEUSDT",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0xe982E462b094850F12AF94d21D470e21bE9D0E9C"
} }
] ]

59
test/claimtoken.test.js Normal file
View File

@ -0,0 +1,59 @@
const ClaimToken = artifacts.require("ClaimToken");
const FT = artifacts.require("FT");
const {
BN,
constants,
expectEvent,
expectRevert,
} = require("@openzeppelin/test-helpers");
contract("ClaimToken", (accounts) => {
let tokenClaimer;
let usdt;
const owner = accounts[0];
const user = accounts[1];
const executor = accounts[2];
beforeEach(async () => {
usdt = await FT.new("USDT", "USDT", 0);
tokenClaimer = await ClaimToken.new();
await tokenClaimer.updateTokenSupport(usdt.address, true);
await tokenClaimer.updateVerifier(executor);
await tokenClaimer.updatePaymentAddress(owner);
});
it("should cliam token", async () => {
await usdt.mint(owner, web3.utils.toWei("1000"));
await usdt.approve(tokenClaimer.address, web3.utils.toWei("1000"), { from: owner });
const balanceOfOwner = await usdt.balanceOf(owner);
console.log('balanceOfOwner: ', balanceOfOwner.toString())
const allowance = await usdt.allowance(owner, tokenClaimer.address);
console.log('allowance: ', allowance.toString())
const token = usdt.address;
const amount = web3.utils.toWei("100");
console.log(amount)
const chainId = await web3.eth.getChainId();
console.log(chainId)
const address = tokenClaimer.address;
const startTime = Date.now() / 1000 | 0;
const saltNonce = (Math.random() * 1000) | 0;
let signStr = web3.utils.soliditySha3.apply(this,
[user, token, address, chainId, amount, startTime, saltNonce]);
let signature = await web3.eth.sign(signStr, executor);
signature = signature.replace(/00$/, "1b").replace(/01$/, "1c");
const receipt = await tokenClaimer.claim(
token,
amount,
startTime,
saltNonce,
signature,
{ from: user }
);
const balanceOfUser = await usdt.balanceOf(user);
assert.equal(balanceOfUser, amount, "Incorrect user balance");
});
});