├── .gitignore ├── LICENSE ├── Readme.md ├── audits ├── THORWallet_Staking_Distributor_audit_report.pdf └── THORWallet_audit_report.pdf ├── contracts-for-verification ├── batchtransfer │ ├── IERC20.sol │ └── batchtransfer.sol ├── faucet │ ├── IERC20.sol │ └── faucet.sol ├── staking │ ├── Address.sol │ ├── Context.sol │ ├── IERC20.sol │ ├── Multicall.sol │ ├── Ownable.sol │ ├── ReentrancyGuard.sol │ ├── SafeERC20.sol │ └── staking.sol ├── tgt │ ├── Address.sol │ ├── Context.sol │ ├── Counters.sol │ ├── ECDSA.sol │ ├── ERC777.sol │ ├── IERC1820Registry.sol │ ├── IERC20.sol │ ├── IERC777.sol │ ├── IERC777Recipient.sol │ ├── IERC777Sender.sol │ ├── draft-EIP712.sol │ └── tgt.sol └── vesting │ ├── IERC20.sol │ └── vesting.sol ├── contracts ├── TGTStaking.sol ├── batchtransfer.sol ├── distributor.sol ├── faucet.sol ├── libraries │ └── base64.sol ├── mocks │ ├── MockTGT.sol │ └── USDC.sol ├── staking.sol ├── stakingV2.sol ├── tgt.sol ├── twanft.sol └── vesting.sol ├── hardhat.config.js ├── package.json ├── staking-contracts ├── GooseDefi_MasterChef.sol ├── GooseDefi_MasterChefV2.sol ├── PancakeSwap_MasterChef.sol └── SushiSwap_MasterChefV2.sol ├── supply-curve.ods ├── test ├── batchtransfer-test.js ├── distributor-test.js ├── faucet-test.js ├── staking-test.js ├── stakingv2-test.js ├── tgt-staking.js ├── tgt-test.js └── utils │ ├── ERC20.allowance.js │ ├── ERC20.behavior.js │ └── minting-blocks.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | #Hardhat files 3 | cache 4 | artifacts 5 | .idea 6 | .DS_Store 7 | .env -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # TGT Contracts 2 | 3 | ## Audits 4 | 5 | * [TGT Contract & Vesting Contract Audit by 21Analytics](audits/THORWallet_audit_report.pdf) 6 | (The findings in the report were resolved and approved by 21Analytics) 7 | * [Staking Contract & Distributor Contract Audit by 21Analytics](audits/THORWallet_Staking_Distributor_audit_report.pdf) 8 | (The findings in the report were resolved and approved by 21Analytics, see [PR #38](https://github.com/THORWallet/smartcontract/pull/38)) 9 | 10 | ## THORWallet Governance Token (TGT) Contract 11 | 12 | The contract is an ERC20 contract with the following additions: 13 | 14 | * [ERC20 Permit](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/draft-IERC20Permit.sol) 15 | in combination with [EIP712](https://eips.ethereum.org/EIPS/eip-712). The wallet user can give a permit to an otherone 16 | to operate on the token. Obviously, ECDSA from OpenZeppelin is used, which also includes 17 | my [signature malleability](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1622) fix. 18 | * ERC677 with an addition to call contracts if an approval was given. Thus, this contract provides the following two 19 | functions. In combination with the ERC20 permit, a wallet user does not need to have Ethers to transfer tokens. The 20 | user can provide a permit, that the wallet operator can approve, and the wallet operator can then call on behalf of 21 | the user e.g., the staking contract. 22 | * transferAndCall(address to, uint value, bytes calldata data) 23 | * transferFromAndCall(address sender, address to, uint value, bytes calldata data) 24 | 25 | ## Token Emit 26 | 27 | The token emitting function can be found in [supply-curve.ods](supply-curve.ods). It first emits fast, then get slower 28 | until it reaches the max of 1b tokens. The emitting is done every month within an ERC20 transfer. Every year the amount 29 | of tokens that can be emitted is lowered according to the emitting function. 30 | 31 | ## Facts 32 | 33 | * Max Supply: 1b 34 | * Init Supply: 460m 35 | * Decimals: 18 36 | * Symbol: TGT 37 | * Token Name: THORWallet Governance Token 38 | * Vesting for investors and team, with cliff and linear release of tokens 39 | * Contract addresses staking: 40 | * ~~STAKING_TESTNET_CONTRACT_1 = '0xf1C26043d920fE3459E0BFe9776AC42c448137f1';~~ 41 | * ~~STAKING_TESTNET_CONTRACT_2 = '0xf6e5c60acb61c7ae7f2dde5b4d69889c9c52b387';~~ 42 | * ~~STAKING_MAINNET_CONTRACT = '0x6d6f07425a37b7bb0fae70acd11b6b9314116249';~~ 43 | * STAKING_MAINNET_CONTRACT = '0x77f400a7af20d22F387503dbA3979bA28d8aF48b'; 44 | * Contract addresses TGT: 45 | * TGT_TESTNET_CONTRACT_1 = '0x73d6e26896981798526b6ead48d0fab76e205974'; 46 | * TGT_TESTNET_CONTRACT_2 = '0x108a850856db3f85d0269a2693d896b394c80325'; 47 | * TGT_MAINNET_CONTRACT = '0x108a850856Db3f85d0269a2693D896B394C80325'; 48 | * Contract addresses Vesting: 49 | * VESTING_TESTNET_CONTRACT_1 = '0x303df20dcfda5bc9b6871f5ce783535fecdc1129'; 50 | * VESTING_TESTNET_CONTRACT_2 = '0x68dd83dfaad47fbb804aeb96034220c7a4d28ee5'; 51 | * VESTING_MAINNET_CONTRACT = '0x68dd83dfaad47fbb804aeb96034220c7a4d28ee5'; 52 | * Faucet address 53 | * FAUCET_TESTNET = '0x2dCac0362cF1Ac672BE9496bb9EBb2C00A3b181B'; () 54 | * FAUCET_MAINNET = '0x57b0391d98A3c2B338D7025acaC869E96C996903'; (initialized with: 0x108a850856Db3f85d0269a2693D896B394C80325, 0x69102B434be1D245961E7a1114b6e49a2d1283f2, 1000000000000000000) 55 | * live date in TGT contract: 1628165847 56 | * NFT contract: '0x53D917d66EcFec3eF379434b0Ad481E4DdEDcF66' 57 | * NFT_TESTNET = '0xB7ca508c83defd59eEc051003Ba1A97dDFF36b66' 58 | 59 | ## Installation and Running Tests 60 | 61 | Run the following commands on the freshly cloned repository: 62 | 63 | ``` 64 | yarn 65 | yarn test 66 | ``` 67 | -------------------------------------------------------------------------------- /audits/THORWallet_Staking_Distributor_audit_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THORWallet/smartcontract/6dea82087e2ff229ffc4ab1c5359fa6337b2adf6/audits/THORWallet_Staking_Distributor_audit_report.pdf -------------------------------------------------------------------------------- /audits/THORWallet_audit_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THORWallet/smartcontract/6dea82087e2ff229ffc4ab1c5359fa6337b2adf6/audits/THORWallet_audit_report.pdf -------------------------------------------------------------------------------- /contracts-for-verification/batchtransfer/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC20 standard as defined in the EIP. 7 | */ 8 | interface IERC20 { 9 | /** 10 | * @dev Returns the amount of tokens in existence. 11 | */ 12 | function totalSupply() external view returns (uint256); 13 | 14 | /** 15 | * @dev Returns the amount of tokens owned by `account`. 16 | */ 17 | function balanceOf(address account) external view returns (uint256); 18 | 19 | /** 20 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 21 | * 22 | * Returns a boolean value indicating whether the operation succeeded. 23 | * 24 | * Emits a {Transfer} event. 25 | */ 26 | function transfer(address recipient, uint256 amount) external returns (bool); 27 | 28 | /** 29 | * @dev Returns the remaining number of tokens that `spender` will be 30 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 31 | * zero by default. 32 | * 33 | * This value changes when {approve} or {transferFrom} are called. 34 | */ 35 | function allowance(address owner, address spender) external view returns (uint256); 36 | 37 | /** 38 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 39 | * 40 | * Returns a boolean value indicating whether the operation succeeded. 41 | * 42 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 43 | * that someone may use both the old and the new allowance by unfortunate 44 | * transaction ordering. One possible solution to mitigate this race 45 | * condition is to first reduce the spender's allowance to 0 and set the 46 | * desired value afterwards: 47 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 48 | * 49 | * Emits an {Approval} event. 50 | */ 51 | function approve(address spender, uint256 amount) external returns (bool); 52 | 53 | /** 54 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 55 | * allowance mechanism. `amount` is then deducted from the caller's 56 | * allowance. 57 | * 58 | * Returns a boolean value indicating whether the operation succeeded. 59 | * 60 | * Emits a {Transfer} event. 61 | */ 62 | function transferFrom( 63 | address sender, 64 | address recipient, 65 | uint256 amount 66 | ) external returns (bool); 67 | 68 | /** 69 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 70 | * another (`to`). 71 | * 72 | * Note that `value` may be zero. 73 | */ 74 | event Transfer(address indexed from, address indexed to, uint256 value); 75 | 76 | /** 77 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 78 | * a call to {approve}. `value` is the new allowance. 79 | */ 80 | event Approval(address indexed owner, address indexed spender, uint256 value); 81 | } 82 | -------------------------------------------------------------------------------- /contracts-for-verification/batchtransfer/batchtransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.10; 3 | 4 | import "./IERC20.sol"; 5 | 6 | contract BatchTransfer { 7 | 8 | address private owner; 9 | constructor() { 10 | owner = msg.sender; 11 | } 12 | 13 | function batchTransferDirect(IERC20 _erc20Contract, address[] calldata _to, uint256 _value) external { 14 | require(msg.sender == owner, "not owner"); 15 | for (uint i=0; i<_to.length; i++) { 16 | _erc20Contract.transfer(_to[i], _value); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts-for-verification/faucet/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC20 standard as defined in the EIP. 7 | */ 8 | interface IERC20 { 9 | /** 10 | * @dev Returns the amount of tokens in existence. 11 | */ 12 | function totalSupply() external view returns (uint256); 13 | 14 | /** 15 | * @dev Returns the amount of tokens owned by `account`. 16 | */ 17 | function balanceOf(address account) external view returns (uint256); 18 | 19 | /** 20 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 21 | * 22 | * Returns a boolean value indicating whether the operation succeeded. 23 | * 24 | * Emits a {Transfer} event. 25 | */ 26 | function transfer(address recipient, uint256 amount) external returns (bool); 27 | 28 | /** 29 | * @dev Returns the remaining number of tokens that `spender` will be 30 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 31 | * zero by default. 32 | * 33 | * This value changes when {approve} or {transferFrom} are called. 34 | */ 35 | function allowance(address owner, address spender) external view returns (uint256); 36 | 37 | /** 38 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 39 | * 40 | * Returns a boolean value indicating whether the operation succeeded. 41 | * 42 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 43 | * that someone may use both the old and the new allowance by unfortunate 44 | * transaction ordering. One possible solution to mitigate this race 45 | * condition is to first reduce the spender's allowance to 0 and set the 46 | * desired value afterwards: 47 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 48 | * 49 | * Emits an {Approval} event. 50 | */ 51 | function approve(address spender, uint256 amount) external returns (bool); 52 | 53 | /** 54 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 55 | * allowance mechanism. `amount` is then deducted from the caller's 56 | * allowance. 57 | * 58 | * Returns a boolean value indicating whether the operation succeeded. 59 | * 60 | * Emits a {Transfer} event. 61 | */ 62 | function transferFrom( 63 | address sender, 64 | address recipient, 65 | uint256 amount 66 | ) external returns (bool); 67 | 68 | /** 69 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 70 | * another (`to`). 71 | * 72 | * Note that `value` may be zero. 73 | */ 74 | event Transfer(address indexed from, address indexed to, uint256 value); 75 | 76 | /** 77 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 78 | * a call to {approve}. `value` is the new allowance. 79 | */ 80 | event Approval(address indexed owner, address indexed spender, uint256 value); 81 | } 82 | -------------------------------------------------------------------------------- /contracts-for-verification/faucet/faucet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.10; 3 | 4 | import "./IERC20.sol"; 5 | 6 | contract Faucet { 7 | //once an address has claimed, it cannot claim anymore 8 | mapping(address => bool) private _used; 9 | //the ERC20 to be spent 10 | IERC20 private _contractAddress; 11 | //the owner of the generous token provider for this faucet 12 | address private _tokenOwner; 13 | //how much tokens we want to spend per claim 14 | uint256 private _tokensPerClaim; 15 | 16 | event Claim(address claimer, uint256 amount); 17 | 18 | constructor(address contractAddress, address tokenOwner, uint256 tokensPerClaim) { 19 | _contractAddress = IERC20(contractAddress); 20 | _tokenOwner = tokenOwner; 21 | _tokensPerClaim = tokensPerClaim; 22 | } 23 | 24 | function claim() external { 25 | require(!_used[msg.sender], "Same address cannot claim twice"); 26 | //send tokens to the one who called this contract 27 | _contractAddress.transferFrom(_tokenOwner, msg.sender, _tokensPerClaim); 28 | //mark the address of the one who called this contract, 29 | //so this address cannot claim again 30 | _used[msg.sender] = true; 31 | emit Claim(msg.sender, _tokensPerClaim); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts-for-verification/staking/Address.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Collection of functions related to the address type 7 | */ 8 | library Address { 9 | /** 10 | * @dev Returns true if `account` is a contract. 11 | * 12 | * [IMPORTANT] 13 | * ==== 14 | * It is unsafe to assume that an address for which this function returns 15 | * false is an externally-owned account (EOA) and not a contract. 16 | * 17 | * Among others, `isContract` will return false for the following 18 | * types of addresses: 19 | * 20 | * - an externally-owned account 21 | * - a contract in construction 22 | * - an address where a contract will be created 23 | * - an address where a contract lived, but was destroyed 24 | * ==== 25 | */ 26 | function isContract(address account) internal view returns (bool) { 27 | // This method relies on extcodesize, which returns 0 for contracts in 28 | // construction, since the code is only stored at the end of the 29 | // constructor execution. 30 | 31 | uint256 size; 32 | assembly { 33 | size := extcodesize(account) 34 | } 35 | return size > 0; 36 | } 37 | 38 | /** 39 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 40 | * `recipient`, forwarding all available gas and reverting on errors. 41 | * 42 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 43 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 44 | * imposed by `transfer`, making them unable to receive funds via 45 | * `transfer`. {sendValue} removes this limitation. 46 | * 47 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 48 | * 49 | * IMPORTANT: because control is transferred to `recipient`, care must be 50 | * taken to not create reentrancy vulnerabilities. Consider using 51 | * {ReentrancyGuard} or the 52 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 53 | */ 54 | function sendValue(address payable recipient, uint256 amount) internal { 55 | require(address(this).balance >= amount, "Address: insufficient balance"); 56 | 57 | (bool success, ) = recipient.call{value: amount}(""); 58 | require(success, "Address: unable to send value, recipient may have reverted"); 59 | } 60 | 61 | /** 62 | * @dev Performs a Solidity function call using a low level `call`. A 63 | * plain `call` is an unsafe replacement for a function call: use this 64 | * function instead. 65 | * 66 | * If `target` reverts with a revert reason, it is bubbled up by this 67 | * function (like regular Solidity function calls). 68 | * 69 | * Returns the raw returned data. To convert to the expected return value, 70 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 71 | * 72 | * Requirements: 73 | * 74 | * - `target` must be a contract. 75 | * - calling `target` with `data` must not revert. 76 | * 77 | * _Available since v3.1._ 78 | */ 79 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 80 | return functionCall(target, data, "Address: low-level call failed"); 81 | } 82 | 83 | /** 84 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 85 | * `errorMessage` as a fallback revert reason when `target` reverts. 86 | * 87 | * _Available since v3.1._ 88 | */ 89 | function functionCall( 90 | address target, 91 | bytes memory data, 92 | string memory errorMessage 93 | ) internal returns (bytes memory) { 94 | return functionCallWithValue(target, data, 0, errorMessage); 95 | } 96 | 97 | /** 98 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 99 | * but also transferring `value` wei to `target`. 100 | * 101 | * Requirements: 102 | * 103 | * - the calling contract must have an ETH balance of at least `value`. 104 | * - the called Solidity function must be `payable`. 105 | * 106 | * _Available since v3.1._ 107 | */ 108 | function functionCallWithValue( 109 | address target, 110 | bytes memory data, 111 | uint256 value 112 | ) internal returns (bytes memory) { 113 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 114 | } 115 | 116 | /** 117 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 118 | * with `errorMessage` as a fallback revert reason when `target` reverts. 119 | * 120 | * _Available since v3.1._ 121 | */ 122 | function functionCallWithValue( 123 | address target, 124 | bytes memory data, 125 | uint256 value, 126 | string memory errorMessage 127 | ) internal returns (bytes memory) { 128 | require(address(this).balance >= value, "Address: insufficient balance for call"); 129 | require(isContract(target), "Address: call to non-contract"); 130 | 131 | (bool success, bytes memory returndata) = target.call{value: value}(data); 132 | return _verifyCallResult(success, returndata, errorMessage); 133 | } 134 | 135 | /** 136 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 137 | * but performing a static call. 138 | * 139 | * _Available since v3.3._ 140 | */ 141 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 142 | return functionStaticCall(target, data, "Address: low-level static call failed"); 143 | } 144 | 145 | /** 146 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 147 | * but performing a static call. 148 | * 149 | * _Available since v3.3._ 150 | */ 151 | function functionStaticCall( 152 | address target, 153 | bytes memory data, 154 | string memory errorMessage 155 | ) internal view returns (bytes memory) { 156 | require(isContract(target), "Address: static call to non-contract"); 157 | 158 | (bool success, bytes memory returndata) = target.staticcall(data); 159 | return _verifyCallResult(success, returndata, errorMessage); 160 | } 161 | 162 | /** 163 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 164 | * but performing a delegate call. 165 | * 166 | * _Available since v3.4._ 167 | */ 168 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { 169 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); 170 | } 171 | 172 | /** 173 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 174 | * but performing a delegate call. 175 | * 176 | * _Available since v3.4._ 177 | */ 178 | function functionDelegateCall( 179 | address target, 180 | bytes memory data, 181 | string memory errorMessage 182 | ) internal returns (bytes memory) { 183 | require(isContract(target), "Address: delegate call to non-contract"); 184 | 185 | (bool success, bytes memory returndata) = target.delegatecall(data); 186 | return _verifyCallResult(success, returndata, errorMessage); 187 | } 188 | 189 | function _verifyCallResult( 190 | bool success, 191 | bytes memory returndata, 192 | string memory errorMessage 193 | ) private pure returns (bytes memory) { 194 | if (success) { 195 | return returndata; 196 | } else { 197 | // Look for revert reason and bubble it up if present 198 | if (returndata.length > 0) { 199 | // The easiest way to bubble the revert reason is using memory via assembly 200 | 201 | assembly { 202 | let returndata_size := mload(returndata) 203 | revert(add(32, returndata), returndata_size) 204 | } 205 | } else { 206 | revert(errorMessage); 207 | } 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /contracts-for-verification/staking/Context.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /* 6 | * @dev Provides information about the current execution context, including the 7 | * sender of the transaction and its data. While these are generally available 8 | * via msg.sender and msg.data, they should not be accessed in such a direct 9 | * manner, since when dealing with meta-transactions the account sending and 10 | * paying for execution may not be the actual sender (as far as an application 11 | * is concerned). 12 | * 13 | * This contract is only required for intermediate, library-like contracts. 14 | */ 15 | abstract contract Context { 16 | function _msgSender() internal view virtual returns (address) { 17 | return msg.sender; 18 | } 19 | 20 | function _msgData() internal view virtual returns (bytes calldata) { 21 | return msg.data; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts-for-verification/staking/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC20 standard as defined in the EIP. 7 | */ 8 | interface IERC20 { 9 | /** 10 | * @dev Returns the amount of tokens in existence. 11 | */ 12 | function totalSupply() external view returns (uint256); 13 | 14 | /** 15 | * @dev Returns the amount of tokens owned by `account`. 16 | */ 17 | function balanceOf(address account) external view returns (uint256); 18 | 19 | /** 20 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 21 | * 22 | * Returns a boolean value indicating whether the operation succeeded. 23 | * 24 | * Emits a {Transfer} event. 25 | */ 26 | function transfer(address recipient, uint256 amount) external returns (bool); 27 | 28 | /** 29 | * @dev Returns the remaining number of tokens that `spender` will be 30 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 31 | * zero by default. 32 | * 33 | * This value changes when {approve} or {transferFrom} are called. 34 | */ 35 | function allowance(address owner, address spender) external view returns (uint256); 36 | 37 | /** 38 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 39 | * 40 | * Returns a boolean value indicating whether the operation succeeded. 41 | * 42 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 43 | * that someone may use both the old and the new allowance by unfortunate 44 | * transaction ordering. One possible solution to mitigate this race 45 | * condition is to first reduce the spender's allowance to 0 and set the 46 | * desired value afterwards: 47 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 48 | * 49 | * Emits an {Approval} event. 50 | */ 51 | function approve(address spender, uint256 amount) external returns (bool); 52 | 53 | /** 54 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 55 | * allowance mechanism. `amount` is then deducted from the caller's 56 | * allowance. 57 | * 58 | * Returns a boolean value indicating whether the operation succeeded. 59 | * 60 | * Emits a {Transfer} event. 61 | */ 62 | function transferFrom( 63 | address sender, 64 | address recipient, 65 | uint256 amount 66 | ) external returns (bool); 67 | 68 | /** 69 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 70 | * another (`to`). 71 | * 72 | * Note that `value` may be zero. 73 | */ 74 | event Transfer(address indexed from, address indexed to, uint256 value); 75 | 76 | /** 77 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 78 | * a call to {approve}. `value` is the new allowance. 79 | */ 80 | event Approval(address indexed owner, address indexed spender, uint256 value); 81 | } 82 | -------------------------------------------------------------------------------- /contracts-for-verification/staking/Multicall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./Address.sol"; 6 | 7 | /** 8 | * @dev Provides a function to batch together multiple calls in a single external call. 9 | * 10 | * _Available since v4.1._ 11 | */ 12 | abstract contract Multicall { 13 | /** 14 | * @dev Receives and executes a batch of function calls on this contract. 15 | */ 16 | function multicall(bytes[] calldata data) external returns (bytes[] memory results) { 17 | results = new bytes[](data.length); 18 | for (uint256 i = 0; i < data.length; i++) { 19 | results[i] = Address.functionDelegateCall(address(this), data[i]); 20 | } 21 | return results; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts-for-verification/staking/Ownable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./Context.sol"; 6 | 7 | /** 8 | * @dev Contract module which provides a basic access control mechanism, where 9 | * there is an account (an owner) that can be granted exclusive access to 10 | * specific functions. 11 | * 12 | * By default, the owner account will be the one that deploys the contract. This 13 | * can later be changed with {transferOwnership}. 14 | * 15 | * This module is used through inheritance. It will make available the modifier 16 | * `onlyOwner`, which can be applied to your functions to restrict their use to 17 | * the owner. 18 | */ 19 | abstract contract Ownable is Context { 20 | address private _owner; 21 | 22 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 23 | 24 | /** 25 | * @dev Initializes the contract setting the deployer as the initial owner. 26 | */ 27 | constructor() { 28 | _setOwner(_msgSender()); 29 | } 30 | 31 | /** 32 | * @dev Returns the address of the current owner. 33 | */ 34 | function owner() public view virtual returns (address) { 35 | return _owner; 36 | } 37 | 38 | /** 39 | * @dev Throws if called by any account other than the owner. 40 | */ 41 | modifier onlyOwner() { 42 | require(owner() == _msgSender(), "Ownable: caller is not the owner"); 43 | _; 44 | } 45 | 46 | /** 47 | * @dev Leaves the contract without owner. It will not be possible to call 48 | * `onlyOwner` functions anymore. Can only be called by the current owner. 49 | * 50 | * NOTE: Renouncing ownership will leave the contract without an owner, 51 | * thereby removing any functionality that is only available to the owner. 52 | */ 53 | function renounceOwnership() public virtual onlyOwner { 54 | _setOwner(address(0)); 55 | } 56 | 57 | /** 58 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 59 | * Can only be called by the current owner. 60 | */ 61 | function transferOwnership(address newOwner) public virtual onlyOwner { 62 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 63 | _setOwner(newOwner); 64 | } 65 | 66 | function _setOwner(address newOwner) private { 67 | address oldOwner = _owner; 68 | _owner = newOwner; 69 | emit OwnershipTransferred(oldOwner, newOwner); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /contracts-for-verification/staking/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Contract module that helps prevent reentrant calls to a function. 7 | * 8 | * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier 9 | * available, which can be applied to functions to make sure there are no nested 10 | * (reentrant) calls to them. 11 | * 12 | * Note that because there is a single `nonReentrant` guard, functions marked as 13 | * `nonReentrant` may not call one another. This can be worked around by making 14 | * those functions `private`, and then adding `external` `nonReentrant` entry 15 | * points to them. 16 | * 17 | * TIP: If you would like to learn more about reentrancy and alternative ways 18 | * to protect against it, check out our blog post 19 | * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. 20 | */ 21 | abstract contract ReentrancyGuard { 22 | // Booleans are more expensive than uint256 or any type that takes up a full 23 | // word because each write operation emits an extra SLOAD to first read the 24 | // slot's contents, replace the bits taken up by the boolean, and then write 25 | // back. This is the compiler's defense against contract upgrades and 26 | // pointer aliasing, and it cannot be disabled. 27 | 28 | // The values being non-zero value makes deployment a bit more expensive, 29 | // but in exchange the refund on every call to nonReentrant will be lower in 30 | // amount. Since refunds are capped to a percentage of the total 31 | // transaction's gas, it is best to keep them low in cases like this one, to 32 | // increase the likelihood of the full refund coming into effect. 33 | uint256 private constant _NOT_ENTERED = 1; 34 | uint256 private constant _ENTERED = 2; 35 | 36 | uint256 private _status; 37 | 38 | constructor() { 39 | _status = _NOT_ENTERED; 40 | } 41 | 42 | /** 43 | * @dev Prevents a contract from calling itself, directly or indirectly. 44 | * Calling a `nonReentrant` function from another `nonReentrant` 45 | * function is not supported. It is possible to prevent this from happening 46 | * by making the `nonReentrant` function external, and make it call a 47 | * `private` function that does the actual work. 48 | */ 49 | modifier nonReentrant() { 50 | // On the first call to nonReentrant, _notEntered will be true 51 | require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); 52 | 53 | // Any calls to nonReentrant after this point will fail 54 | _status = _ENTERED; 55 | 56 | _; 57 | 58 | // By storing the original value once again, a refund is triggered (see 59 | // https://eips.ethereum.org/EIPS/eip-2200) 60 | _status = _NOT_ENTERED; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /contracts-for-verification/staking/SafeERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./IERC20.sol"; 6 | import "./Address.sol"; 7 | 8 | /** 9 | * @title SafeERC20 10 | * @dev Wrappers around ERC20 operations that throw on failure (when the token 11 | * contract returns false). Tokens that return no value (and instead revert or 12 | * throw on failure) are also supported, non-reverting calls are assumed to be 13 | * successful. 14 | * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, 15 | * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. 16 | */ 17 | library SafeERC20 { 18 | using Address for address; 19 | 20 | function safeTransfer( 21 | IERC20 token, 22 | address to, 23 | uint256 value 24 | ) internal { 25 | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 26 | } 27 | 28 | function safeTransferFrom( 29 | IERC20 token, 30 | address from, 31 | address to, 32 | uint256 value 33 | ) internal { 34 | _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); 35 | } 36 | 37 | /** 38 | * @dev Deprecated. This function has issues similar to the ones found in 39 | * {IERC20-approve}, and its usage is discouraged. 40 | * 41 | * Whenever possible, use {safeIncreaseAllowance} and 42 | * {safeDecreaseAllowance} instead. 43 | */ 44 | function safeApprove( 45 | IERC20 token, 46 | address spender, 47 | uint256 value 48 | ) internal { 49 | // safeApprove should only be called when setting an initial allowance, 50 | // or when resetting it to zero. To increase and decrease it, use 51 | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 52 | require( 53 | (value == 0) || (token.allowance(address(this), spender) == 0), 54 | "SafeERC20: approve from non-zero to non-zero allowance" 55 | ); 56 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); 57 | } 58 | 59 | function safeIncreaseAllowance( 60 | IERC20 token, 61 | address spender, 62 | uint256 value 63 | ) internal { 64 | uint256 newAllowance = token.allowance(address(this), spender) + value; 65 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 66 | } 67 | 68 | function safeDecreaseAllowance( 69 | IERC20 token, 70 | address spender, 71 | uint256 value 72 | ) internal { 73 | unchecked { 74 | uint256 oldAllowance = token.allowance(address(this), spender); 75 | require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); 76 | uint256 newAllowance = oldAllowance - value; 77 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 78 | } 79 | } 80 | 81 | /** 82 | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement 83 | * on the return value: the return value is optional (but if data is returned, it must not be false). 84 | * @param token The token targeted by the call. 85 | * @param data The call data (encoded using abi.encode or one of its variants). 86 | */ 87 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 88 | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since 89 | // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that 90 | // the target address contains contract code and also asserts for success in the low-level call. 91 | 92 | bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); 93 | if (returndata.length > 0) { 94 | // Return data is optional 95 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /contracts-for-verification/staking/staking.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // combined from 4 | // https://github.com/Thorstarter/thorstarter-contracts/blob/main/contracts/Staking.sol 5 | // and: 6 | // https://github.com/goosedefi/goose-contracts/blob/master/contracts/MasterChefV2.sol 7 | // which was audited 8 | 9 | pragma solidity ^0.8.4; 10 | 11 | import "./IERC20.sol"; 12 | import "./SafeERC20.sol"; 13 | import "./Ownable.sol"; 14 | import "./Multicall.sol"; 15 | import "./ReentrancyGuard.sol"; 16 | 17 | contract Staking is Ownable, Multicall, ReentrancyGuard { 18 | using SafeERC20 for IERC20; 19 | 20 | /// @notice Info of each Staking user. 21 | /// `amount` LP token amount the user has provided. 22 | /// `rewardOffset` The amount of token which needs to be subtracted at the next harvesting event. 23 | struct UserInfo { 24 | uint256 amount; 25 | uint256 rewardOffset; 26 | } 27 | 28 | /// @notice Info of each Staking pool. 29 | /// `lpToken` The address of LP token contract. 30 | /// `allocPoint` The amount of allocation points assigned to the pool. 31 | /// Also known as the amount of token to distribute per block. 32 | struct PoolInfo { 33 | IERC20 lpToken; 34 | uint256 accRewardPerShare; 35 | uint256 lastRewardBlock; 36 | uint256 allocPoint; 37 | } 38 | 39 | // The amount of rewardTokens entitled to a user but is pending to be distributed is: 40 | // 41 | // pending reward = (user.amount * pool.accRewardPerShare) - user.rewardOffset 42 | // 43 | // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens: 44 | // 1. The pool's `accRewardPerShare` (and `lastRewardBlock`) gets updated. 45 | // 2. User receives the pending reward sent to his/her address. 46 | // 3. User's `amount` gets updated. 47 | // 4. User's `rewardOffset` gets updated. 48 | 49 | /// @notice Address of token contract. 50 | IERC20 public rewardToken; 51 | address public rewardOwner; 52 | 53 | /// @notice Info of each Staking pool. 54 | PoolInfo[] public poolInfo; 55 | 56 | /// @notice Info of each user that stakes LP tokens. 57 | mapping (uint256 => mapping (address => UserInfo)) public userInfo; 58 | /// @dev Total allocation points. Must be the sum of all allocation points in all pools. 59 | uint256 public totalAllocPoint = 0; 60 | 61 | uint256 public rewardPerBlock = 0; 62 | uint256 private constant ACC_PRECISION = 1e12; 63 | 64 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount, address indexed to); 65 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to); 66 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to); 67 | event Harvest(address indexed user, uint256 indexed pid, uint256 amount); 68 | event LogPoolAddition(uint256 indexed pid, uint256 allocPoint, IERC20 indexed lpToken); 69 | event LogSetPool(uint256 indexed pid, uint256 allocPoint); 70 | event LogUpdatePool(uint256 indexed pid, uint256 lastRewardBlock, uint256 lpSupply, uint256 accRewardPerShare); 71 | 72 | /// @param _rewardToken The reward token contract address. 73 | constructor(IERC20 _rewardToken, address _rewardOwner, uint256 _rewardPerBlock) Ownable() { 74 | rewardToken = _rewardToken; 75 | rewardOwner = _rewardOwner; 76 | rewardPerBlock = _rewardPerBlock; 77 | } 78 | 79 | /// @notice Sets the reward token. 80 | function setRewardToken(IERC20 _rewardToken) public onlyOwner { 81 | rewardToken = _rewardToken; 82 | } 83 | 84 | /// @notice Sets the reward owner. 85 | function setRewardOwner(address _rewardOwner) public onlyOwner { 86 | rewardOwner = _rewardOwner; 87 | } 88 | 89 | /// @notice Adjusts the reward per block. 90 | function setRewardsPerBlock(uint256 _rewardPerBlock) public onlyOwner { 91 | rewardPerBlock = _rewardPerBlock; 92 | } 93 | 94 | /// @notice Returns the number of Staking pools. 95 | function poolLength() public view returns (uint256 pools) { 96 | pools = poolInfo.length; 97 | } 98 | 99 | mapping(IERC20 => bool) public poolExistence; 100 | 101 | /// @notice Add a new LP to the pool. Can only be called by the owner. 102 | /// DO NOT add the same LP token more than once. Rewards will be messed up if you do. 103 | /// @param _allocPoint AP of the new pool. 104 | /// @param _lpToken Address of the LP ERC-20 token. 105 | function addPool(uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate) public onlyOwner { 106 | require(!poolExistence[_lpToken], "Staking: duplicated pool"); 107 | if (_withUpdate) { 108 | massUpdatePools(); 109 | } 110 | totalAllocPoint = totalAllocPoint + _allocPoint; 111 | 112 | poolExistence[_lpToken] = true; 113 | poolInfo.push(PoolInfo({ 114 | lpToken : _lpToken, 115 | allocPoint: _allocPoint, 116 | lastRewardBlock: block.number, 117 | accRewardPerShare: 0 118 | })); 119 | 120 | emit LogPoolAddition(poolInfo.length - 1, _allocPoint, _lpToken); 121 | } 122 | 123 | /// @notice Update the given pool's token allocation point. Can only be called by the owner. 124 | /// @param _pid The index of the pool. See `poolInfo`. 125 | /// @param _allocPoint New AP of the pool. 126 | function set(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner { 127 | if (_withUpdate) { 128 | massUpdatePools(); 129 | } 130 | totalAllocPoint = totalAllocPoint - poolInfo[_pid].allocPoint + _allocPoint; 131 | poolInfo[_pid].allocPoint = _allocPoint; 132 | 133 | emit LogSetPool(_pid, _allocPoint); 134 | } 135 | 136 | /// @notice View function to see pending token reward on frontend. 137 | /// @param _pid The index of the pool. See `poolInfo`. 138 | /// @param _user Address of user. 139 | /// @return pending token reward for a given user. 140 | function pendingRewards(uint256 _pid, address _user) external view returns (uint256) { 141 | PoolInfo memory pool = poolInfo[_pid]; 142 | UserInfo memory user = userInfo[_pid][_user]; 143 | uint256 accRewardPerShare = pool.accRewardPerShare; 144 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 145 | if (block.number > pool.lastRewardBlock && lpSupply != 0) { 146 | uint256 blocks = block.number - pool.lastRewardBlock; 147 | uint256 reward = (blocks * rewardPerBlock * pool.allocPoint) / totalAllocPoint; 148 | accRewardPerShare = accRewardPerShare + ((reward * ACC_PRECISION) / lpSupply); 149 | } 150 | uint256 accumulatedReward = (user.amount * accRewardPerShare) / ACC_PRECISION; 151 | return accumulatedReward - user.rewardOffset; 152 | } 153 | 154 | // Update reward variables for all pools. Be careful of gas spending! 155 | function massUpdatePools() public { 156 | for (uint256 i = 0; i < poolInfo.length; ++i) { 157 | updatePool(i); 158 | } 159 | } 160 | 161 | function massUpdatePoolsByIds(uint256[] calldata pids) external { 162 | for (uint256 i = 0; i < pids.length; ++i) { 163 | updatePool(pids[i]); 164 | } 165 | } 166 | 167 | /// @notice Update reward variables of the given pool. 168 | /// @param pid The index of the pool. See `poolInfo`. 169 | function updatePool(uint256 pid) public { 170 | PoolInfo storage pool = poolInfo[pid]; 171 | if (block.number <= pool.lastRewardBlock) { 172 | return; 173 | } 174 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 175 | if (lpSupply == 0 || pool.allocPoint == 0) { 176 | pool.lastRewardBlock = block.number; 177 | return; 178 | } 179 | uint256 blocks = block.number - pool.lastRewardBlock; 180 | uint256 reward = (blocks * rewardPerBlock * pool.allocPoint) / totalAllocPoint; 181 | pool.accRewardPerShare = pool.accRewardPerShare + ((reward * ACC_PRECISION) / lpSupply); 182 | pool.lastRewardBlock = block.number; 183 | 184 | emit LogUpdatePool(pid, pool.lastRewardBlock, lpSupply, pool.accRewardPerShare); 185 | } 186 | 187 | /// @notice Deposit LP tokens to Staking for reward token allocation. 188 | /// @param pid The index of the pool. See `poolInfo`. 189 | /// @param amount LP token amount to deposit. 190 | /// @param to The receiver of `amount` deposit benefit. 191 | function deposit(uint256 pid, uint256 amount, address to) public nonReentrant { 192 | updatePool(pid); 193 | PoolInfo memory pool = poolInfo[pid]; 194 | UserInfo storage user = userInfo[pid][msg.sender]; 195 | 196 | // harvest 197 | uint256 accumulatedReward = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 198 | uint256 pendingReward = accumulatedReward - user.rewardOffset; 199 | 200 | if (pendingReward > 0) { 201 | rewardToken.safeTransferFrom(rewardOwner, to, pendingReward); 202 | } 203 | 204 | if (amount > 0) { 205 | pool.lpToken.safeTransferFrom(address(msg.sender), address(this), amount); 206 | user.amount = user.amount + amount; 207 | } 208 | user.rewardOffset = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 209 | 210 | emit Deposit(msg.sender, pid, amount, to); 211 | } 212 | 213 | /// @notice Withdraw LP tokens from Staking. 214 | /// @param pid The index of the pool. See `poolInfo`. 215 | /// @param amount LP token amount to withdraw. 216 | /// @param to Receiver of the LP tokens. 217 | function withdraw(uint256 pid, uint256 amount, address to) public nonReentrant { 218 | updatePool(pid); 219 | PoolInfo memory pool = poolInfo[pid]; 220 | UserInfo storage user = userInfo[pid][msg.sender]; 221 | require(user.amount >= amount, "withdraw: not good"); 222 | 223 | // harvest 224 | uint256 accumulatedReward = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 225 | uint256 pendingReward = accumulatedReward - user.rewardOffset; 226 | if (pendingReward > 0) { 227 | rewardToken.safeTransferFrom(rewardOwner, to, pendingReward); 228 | } 229 | 230 | if (amount > 0) { 231 | user.amount = user.amount - amount; 232 | pool.lpToken.safeTransfer(to, amount); 233 | } 234 | user.rewardOffset = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 235 | 236 | emit Withdraw(msg.sender, pid, amount, to); 237 | } 238 | 239 | /// @notice Harvest proceeds for transaction sender to `to`. 240 | /// @param pid The index of the pool. See `poolInfo`. 241 | /// @param to Receiver of token rewards. 242 | function harvest(uint256 pid, address to) public { 243 | updatePool(pid); 244 | PoolInfo memory pool = poolInfo[pid]; 245 | UserInfo storage user = userInfo[pid][msg.sender]; 246 | 247 | uint256 accumulatedReward = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 248 | uint256 pendingReward = accumulatedReward - user.rewardOffset; 249 | user.rewardOffset = accumulatedReward; 250 | if (pendingReward > 0) { 251 | rewardToken.safeTransferFrom(rewardOwner, to, pendingReward); 252 | } 253 | 254 | emit Harvest(msg.sender, pid, pendingReward); 255 | } 256 | 257 | /// @notice Withdraw without caring about rewards. EMERGENCY ONLY. 258 | /// @param pid The index of the pool. See `poolInfo`. 259 | /// @param to Receiver of the LP tokens. 260 | function emergencyWithdraw(uint256 pid, address to) public { 261 | PoolInfo memory pool = poolInfo[pid]; 262 | UserInfo storage user = userInfo[pid][msg.sender]; 263 | 264 | uint256 amount = user.amount; 265 | 266 | user.amount = 0; 267 | user.rewardOffset = 0; 268 | pool.lpToken.safeTransfer(to, amount); 269 | 270 | emit EmergencyWithdraw(msg.sender, pid, amount, to); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/Address.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Collection of functions related to the address type 7 | */ 8 | library Address { 9 | /** 10 | * @dev Returns true if `account` is a contract. 11 | * 12 | * [IMPORTANT] 13 | * ==== 14 | * It is unsafe to assume that an address for which this function returns 15 | * false is an externally-owned account (EOA) and not a contract. 16 | * 17 | * Among others, `isContract` will return false for the following 18 | * types of addresses: 19 | * 20 | * - an externally-owned account 21 | * - a contract in construction 22 | * - an address where a contract will be created 23 | * - an address where a contract lived, but was destroyed 24 | * ==== 25 | */ 26 | function isContract(address account) internal view returns (bool) { 27 | // This method relies on extcodesize, which returns 0 for contracts in 28 | // construction, since the code is only stored at the end of the 29 | // constructor execution. 30 | 31 | uint256 size; 32 | assembly { 33 | size := extcodesize(account) 34 | } 35 | return size > 0; 36 | } 37 | 38 | /** 39 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 40 | * `recipient`, forwarding all available gas and reverting on errors. 41 | * 42 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 43 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 44 | * imposed by `transfer`, making them unable to receive funds via 45 | * `transfer`. {sendValue} removes this limitation. 46 | * 47 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 48 | * 49 | * IMPORTANT: because control is transferred to `recipient`, care must be 50 | * taken to not create reentrancy vulnerabilities. Consider using 51 | * {ReentrancyGuard} or the 52 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 53 | */ 54 | function sendValue(address payable recipient, uint256 amount) internal { 55 | require(address(this).balance >= amount, "Address: insufficient balance"); 56 | 57 | (bool success, ) = recipient.call{value: amount}(""); 58 | require(success, "Address: unable to send value, recipient may have reverted"); 59 | } 60 | 61 | /** 62 | * @dev Performs a Solidity function call using a low level `call`. A 63 | * plain `call` is an unsafe replacement for a function call: use this 64 | * function instead. 65 | * 66 | * If `target` reverts with a revert reason, it is bubbled up by this 67 | * function (like regular Solidity function calls). 68 | * 69 | * Returns the raw returned data. To convert to the expected return value, 70 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 71 | * 72 | * Requirements: 73 | * 74 | * - `target` must be a contract. 75 | * - calling `target` with `data` must not revert. 76 | * 77 | * _Available since v3.1._ 78 | */ 79 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 80 | return functionCall(target, data, "Address: low-level call failed"); 81 | } 82 | 83 | /** 84 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 85 | * `errorMessage` as a fallback revert reason when `target` reverts. 86 | * 87 | * _Available since v3.1._ 88 | */ 89 | function functionCall( 90 | address target, 91 | bytes memory data, 92 | string memory errorMessage 93 | ) internal returns (bytes memory) { 94 | return functionCallWithValue(target, data, 0, errorMessage); 95 | } 96 | 97 | /** 98 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 99 | * but also transferring `value` wei to `target`. 100 | * 101 | * Requirements: 102 | * 103 | * - the calling contract must have an ETH balance of at least `value`. 104 | * - the called Solidity function must be `payable`. 105 | * 106 | * _Available since v3.1._ 107 | */ 108 | function functionCallWithValue( 109 | address target, 110 | bytes memory data, 111 | uint256 value 112 | ) internal returns (bytes memory) { 113 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 114 | } 115 | 116 | /** 117 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 118 | * with `errorMessage` as a fallback revert reason when `target` reverts. 119 | * 120 | * _Available since v3.1._ 121 | */ 122 | function functionCallWithValue( 123 | address target, 124 | bytes memory data, 125 | uint256 value, 126 | string memory errorMessage 127 | ) internal returns (bytes memory) { 128 | require(address(this).balance >= value, "Address: insufficient balance for call"); 129 | require(isContract(target), "Address: call to non-contract"); 130 | 131 | (bool success, bytes memory returndata) = target.call{value: value}(data); 132 | return _verifyCallResult(success, returndata, errorMessage); 133 | } 134 | 135 | /** 136 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 137 | * but performing a static call. 138 | * 139 | * _Available since v3.3._ 140 | */ 141 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 142 | return functionStaticCall(target, data, "Address: low-level static call failed"); 143 | } 144 | 145 | /** 146 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 147 | * but performing a static call. 148 | * 149 | * _Available since v3.3._ 150 | */ 151 | function functionStaticCall( 152 | address target, 153 | bytes memory data, 154 | string memory errorMessage 155 | ) internal view returns (bytes memory) { 156 | require(isContract(target), "Address: static call to non-contract"); 157 | 158 | (bool success, bytes memory returndata) = target.staticcall(data); 159 | return _verifyCallResult(success, returndata, errorMessage); 160 | } 161 | 162 | /** 163 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 164 | * but performing a delegate call. 165 | * 166 | * _Available since v3.4._ 167 | */ 168 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { 169 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); 170 | } 171 | 172 | /** 173 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 174 | * but performing a delegate call. 175 | * 176 | * _Available since v3.4._ 177 | */ 178 | function functionDelegateCall( 179 | address target, 180 | bytes memory data, 181 | string memory errorMessage 182 | ) internal returns (bytes memory) { 183 | require(isContract(target), "Address: delegate call to non-contract"); 184 | 185 | (bool success, bytes memory returndata) = target.delegatecall(data); 186 | return _verifyCallResult(success, returndata, errorMessage); 187 | } 188 | 189 | function _verifyCallResult( 190 | bool success, 191 | bytes memory returndata, 192 | string memory errorMessage 193 | ) private pure returns (bytes memory) { 194 | if (success) { 195 | return returndata; 196 | } else { 197 | // Look for revert reason and bubble it up if present 198 | if (returndata.length > 0) { 199 | // The easiest way to bubble the revert reason is using memory via assembly 200 | 201 | assembly { 202 | let returndata_size := mload(returndata) 203 | revert(add(32, returndata), returndata_size) 204 | } 205 | } else { 206 | revert(errorMessage); 207 | } 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/Context.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /* 6 | * @dev Provides information about the current execution context, including the 7 | * sender of the transaction and its data. While these are generally available 8 | * via msg.sender and msg.data, they should not be accessed in such a direct 9 | * manner, since when dealing with meta-transactions the account sending and 10 | * paying for execution may not be the actual sender (as far as an application 11 | * is concerned). 12 | * 13 | * This contract is only required for intermediate, library-like contracts. 14 | */ 15 | abstract contract Context { 16 | function _msgSender() internal view virtual returns (address) { 17 | return msg.sender; 18 | } 19 | 20 | function _msgData() internal view virtual returns (bytes calldata) { 21 | return msg.data; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/Counters.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @title Counters 7 | * @author Matt Condon (@shrugs) 8 | * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number 9 | * of elements in a mapping, issuing ERC721 ids, or counting request ids. 10 | * 11 | * Include with `using Counters for Counters.Counter;` 12 | */ 13 | library Counters { 14 | struct Counter { 15 | // This variable should never be directly accessed by users of the library: interactions must be restricted to 16 | // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add 17 | // this feature: see https://github.com/ethereum/solidity/issues/4637 18 | uint256 _value; // default: 0 19 | } 20 | 21 | function current(Counter storage counter) internal view returns (uint256) { 22 | return counter._value; 23 | } 24 | 25 | function increment(Counter storage counter) internal { 26 | unchecked { 27 | counter._value += 1; 28 | } 29 | } 30 | 31 | function decrement(Counter storage counter) internal { 32 | uint256 value = counter._value; 33 | require(value > 0, "Counter: decrement overflow"); 34 | unchecked { 35 | counter._value = value - 1; 36 | } 37 | } 38 | 39 | function reset(Counter storage counter) internal { 40 | counter._value = 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/ECDSA.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. 7 | * 8 | * These functions can be used to verify that a message was signed by the holder 9 | * of the private keys of a given address. 10 | */ 11 | library ECDSA { 12 | /** 13 | * @dev Returns the address that signed a hashed message (`hash`) with 14 | * `signature`. This address can then be used for verification purposes. 15 | * 16 | * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: 17 | * this function rejects them by requiring the `s` value to be in the lower 18 | * half order, and the `v` value to be either 27 or 28. 19 | * 20 | * IMPORTANT: `hash` _must_ be the result of a hash operation for the 21 | * verification to be secure: it is possible to craft signatures that 22 | * recover to arbitrary addresses for non-hashed data. A safe way to ensure 23 | * this is by receiving a hash of the original message (which may otherwise 24 | * be too long), and then calling {toEthSignedMessageHash} on it. 25 | * 26 | * Documentation for signature generation: 27 | * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] 28 | * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] 29 | */ 30 | function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { 31 | // Check the signature length 32 | // - case 65: r,s,v signature (standard) 33 | // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ 34 | if (signature.length == 65) { 35 | bytes32 r; 36 | bytes32 s; 37 | uint8 v; 38 | // ecrecover takes the signature parameters, and the only way to get them 39 | // currently is to use assembly. 40 | assembly { 41 | r := mload(add(signature, 0x20)) 42 | s := mload(add(signature, 0x40)) 43 | v := byte(0, mload(add(signature, 0x60))) 44 | } 45 | return recover(hash, v, r, s); 46 | } else if (signature.length == 64) { 47 | bytes32 r; 48 | bytes32 vs; 49 | // ecrecover takes the signature parameters, and the only way to get them 50 | // currently is to use assembly. 51 | assembly { 52 | r := mload(add(signature, 0x20)) 53 | vs := mload(add(signature, 0x40)) 54 | } 55 | return recover(hash, r, vs); 56 | } else { 57 | revert("ECDSA: invalid signature length"); 58 | } 59 | } 60 | 61 | /** 62 | * @dev Overload of {ECDSA-recover} that receives the `r` and `vs` short-signature fields separately. 63 | * 64 | * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] 65 | * 66 | * _Available since v4.2._ 67 | */ 68 | function recover( 69 | bytes32 hash, 70 | bytes32 r, 71 | bytes32 vs 72 | ) internal pure returns (address) { 73 | bytes32 s; 74 | uint8 v; 75 | assembly { 76 | s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) 77 | v := add(shr(255, vs), 27) 78 | } 79 | return recover(hash, v, r, s); 80 | } 81 | 82 | /** 83 | * @dev Overload of {ECDSA-recover} that receives the `v`, `r` and `s` signature fields separately. 84 | */ 85 | function recover( 86 | bytes32 hash, 87 | uint8 v, 88 | bytes32 r, 89 | bytes32 s 90 | ) internal pure returns (address) { 91 | // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature 92 | // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines 93 | // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most 94 | // signatures from current libraries generate a unique signature with an s-value in the lower half order. 95 | // 96 | // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value 97 | // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or 98 | // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept 99 | // these malleable signatures as well. 100 | require( 101 | uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, 102 | "ECDSA: invalid signature 's' value" 103 | ); 104 | require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); 105 | 106 | // If the signature is valid (and not malleable), return the signer address 107 | address signer = ecrecover(hash, v, r, s); 108 | require(signer != address(0), "ECDSA: invalid signature"); 109 | 110 | return signer; 111 | } 112 | 113 | /** 114 | * @dev Returns an Ethereum Signed Message, created from a `hash`. This 115 | * produces hash corresponding to the one signed with the 116 | * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] 117 | * JSON-RPC method as part of EIP-191. 118 | * 119 | * See {recover}. 120 | */ 121 | function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { 122 | // 32 is the length in bytes of hash, 123 | // enforced by the type signature above 124 | return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); 125 | } 126 | 127 | /** 128 | * @dev Returns an Ethereum Signed Typed Data, created from a 129 | * `domainSeparator` and a `structHash`. This produces hash corresponding 130 | * to the one signed with the 131 | * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] 132 | * JSON-RPC method as part of EIP-712. 133 | * 134 | * See {recover}. 135 | */ 136 | function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { 137 | return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/IERC1820Registry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the global ERC1820 Registry, as defined in the 7 | * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register 8 | * implementers for interfaces in this registry, as well as query support. 9 | * 10 | * Implementers may be shared by multiple accounts, and can also implement more 11 | * than a single interface for each account. Contracts can implement interfaces 12 | * for themselves, but externally-owned accounts (EOA) must delegate this to a 13 | * contract. 14 | * 15 | * {IERC165} interfaces can also be queried via the registry. 16 | * 17 | * For an in-depth explanation and source code analysis, see the EIP text. 18 | */ 19 | interface IERC1820Registry { 20 | /** 21 | * @dev Sets `newManager` as the manager for `account`. A manager of an 22 | * account is able to set interface implementers for it. 23 | * 24 | * By default, each account is its own manager. Passing a value of `0x0` in 25 | * `newManager` will reset the manager to this initial state. 26 | * 27 | * Emits a {ManagerChanged} event. 28 | * 29 | * Requirements: 30 | * 31 | * - the caller must be the current manager for `account`. 32 | */ 33 | function setManager(address account, address newManager) external; 34 | 35 | /** 36 | * @dev Returns the manager for `account`. 37 | * 38 | * See {setManager}. 39 | */ 40 | function getManager(address account) external view returns (address); 41 | 42 | /** 43 | * @dev Sets the `implementer` contract as ``account``'s implementer for 44 | * `interfaceHash`. 45 | * 46 | * `account` being the zero address is an alias for the caller's address. 47 | * The zero address can also be used in `implementer` to remove an old one. 48 | * 49 | * See {interfaceHash} to learn how these are created. 50 | * 51 | * Emits an {InterfaceImplementerSet} event. 52 | * 53 | * Requirements: 54 | * 55 | * - the caller must be the current manager for `account`. 56 | * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not 57 | * end in 28 zeroes). 58 | * - `implementer` must implement {IERC1820Implementer} and return true when 59 | * queried for support, unless `implementer` is the caller. See 60 | * {IERC1820Implementer-canImplementInterfaceForAddress}. 61 | */ 62 | function setInterfaceImplementer( 63 | address account, 64 | bytes32 _interfaceHash, 65 | address implementer 66 | ) external; 67 | 68 | /** 69 | * @dev Returns the implementer of `interfaceHash` for `account`. If no such 70 | * implementer is registered, returns the zero address. 71 | * 72 | * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 73 | * zeroes), `account` will be queried for support of it. 74 | * 75 | * `account` being the zero address is an alias for the caller's address. 76 | */ 77 | function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address); 78 | 79 | /** 80 | * @dev Returns the interface hash for an `interfaceName`, as defined in the 81 | * corresponding 82 | * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. 83 | */ 84 | function interfaceHash(string calldata interfaceName) external pure returns (bytes32); 85 | 86 | /** 87 | * @notice Updates the cache with whether the contract implements an ERC165 interface or not. 88 | * @param account Address of the contract for which to update the cache. 89 | * @param interfaceId ERC165 interface for which to update the cache. 90 | */ 91 | function updateERC165Cache(address account, bytes4 interfaceId) external; 92 | 93 | /** 94 | * @notice Checks whether a contract implements an ERC165 interface or not. 95 | * If the result is not cached a direct lookup on the contract address is performed. 96 | * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling 97 | * {updateERC165Cache} with the contract address. 98 | * @param account Address of the contract to check. 99 | * @param interfaceId ERC165 interface to check. 100 | * @return True if `account` implements `interfaceId`, false otherwise. 101 | */ 102 | function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); 103 | 104 | /** 105 | * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. 106 | * @param account Address of the contract to check. 107 | * @param interfaceId ERC165 interface to check. 108 | * @return True if `account` implements `interfaceId`, false otherwise. 109 | */ 110 | function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); 111 | 112 | event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); 113 | 114 | event ManagerChanged(address indexed account, address indexed newManager); 115 | } 116 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC20 standard as defined in the EIP. 7 | */ 8 | interface IERC20 { 9 | /** 10 | * @dev Returns the amount of tokens in existence. 11 | */ 12 | function totalSupply() external view returns (uint256); 13 | 14 | /** 15 | * @dev Returns the amount of tokens owned by `account`. 16 | */ 17 | function balanceOf(address account) external view returns (uint256); 18 | 19 | /** 20 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 21 | * 22 | * Returns a boolean value indicating whether the operation succeeded. 23 | * 24 | * Emits a {Transfer} event. 25 | */ 26 | function transfer(address recipient, uint256 amount) external returns (bool); 27 | 28 | /** 29 | * @dev Returns the remaining number of tokens that `spender` will be 30 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 31 | * zero by default. 32 | * 33 | * This value changes when {approve} or {transferFrom} are called. 34 | */ 35 | function allowance(address owner, address spender) external view returns (uint256); 36 | 37 | /** 38 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 39 | * 40 | * Returns a boolean value indicating whether the operation succeeded. 41 | * 42 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 43 | * that someone may use both the old and the new allowance by unfortunate 44 | * transaction ordering. One possible solution to mitigate this race 45 | * condition is to first reduce the spender's allowance to 0 and set the 46 | * desired value afterwards: 47 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 48 | * 49 | * Emits an {Approval} event. 50 | */ 51 | function approve(address spender, uint256 amount) external returns (bool); 52 | 53 | /** 54 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 55 | * allowance mechanism. `amount` is then deducted from the caller's 56 | * allowance. 57 | * 58 | * Returns a boolean value indicating whether the operation succeeded. 59 | * 60 | * Emits a {Transfer} event. 61 | */ 62 | function transferFrom( 63 | address sender, 64 | address recipient, 65 | uint256 amount 66 | ) external returns (bool); 67 | 68 | /** 69 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 70 | * another (`to`). 71 | * 72 | * Note that `value` may be zero. 73 | */ 74 | event Transfer(address indexed from, address indexed to, uint256 value); 75 | 76 | /** 77 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 78 | * a call to {approve}. `value` is the new allowance. 79 | */ 80 | event Approval(address indexed owner, address indexed spender, uint256 value); 81 | } 82 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/IERC777.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC777Token standard as defined in the EIP. 7 | * 8 | * This contract uses the 9 | * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let 10 | * token holders and recipients react to token movements by using setting implementers 11 | * for the associated interfaces in said registry. See {IERC1820Registry} and 12 | * {ERC1820Implementer}. 13 | */ 14 | interface IERC777 { 15 | /** 16 | * @dev Returns the name of the token. 17 | */ 18 | function name() external view returns (string memory); 19 | 20 | /** 21 | * @dev Returns the symbol of the token, usually a shorter version of the 22 | * name. 23 | */ 24 | function symbol() external view returns (string memory); 25 | 26 | /** 27 | * @dev Returns the smallest part of the token that is not divisible. This 28 | * means all token operations (creation, movement and destruction) must have 29 | * amounts that are a multiple of this number. 30 | * 31 | * For most token contracts, this value will equal 1. 32 | */ 33 | function granularity() external view returns (uint256); 34 | 35 | /** 36 | * @dev Returns the amount of tokens in existence. 37 | */ 38 | function totalSupply() external view returns (uint256); 39 | 40 | /** 41 | * @dev Returns the amount of tokens owned by an account (`owner`). 42 | */ 43 | function balanceOf(address owner) external view returns (uint256); 44 | 45 | /** 46 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 47 | * 48 | * If send or receive hooks are registered for the caller and `recipient`, 49 | * the corresponding functions will be called with `data` and empty 50 | * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. 51 | * 52 | * Emits a {Sent} event. 53 | * 54 | * Requirements 55 | * 56 | * - the caller must have at least `amount` tokens. 57 | * - `recipient` cannot be the zero address. 58 | * - if `recipient` is a contract, it must implement the {IERC777Recipient} 59 | * interface. 60 | */ 61 | function send( 62 | address recipient, 63 | uint256 amount, 64 | bytes calldata data 65 | ) external; 66 | 67 | /** 68 | * @dev Destroys `amount` tokens from the caller's account, reducing the 69 | * total supply. 70 | * 71 | * If a send hook is registered for the caller, the corresponding function 72 | * will be called with `data` and empty `operatorData`. See {IERC777Sender}. 73 | * 74 | * Emits a {Burned} event. 75 | * 76 | * Requirements 77 | * 78 | * - the caller must have at least `amount` tokens. 79 | */ 80 | function burn(uint256 amount, bytes calldata data) external; 81 | 82 | /** 83 | * @dev Returns true if an account is an operator of `tokenHolder`. 84 | * Operators can send and burn tokens on behalf of their owners. All 85 | * accounts are their own operator. 86 | * 87 | * See {operatorSend} and {operatorBurn}. 88 | */ 89 | function isOperatorFor(address operator, address tokenHolder) external view returns (bool); 90 | 91 | /** 92 | * @dev Make an account an operator of the caller. 93 | * 94 | * See {isOperatorFor}. 95 | * 96 | * Emits an {AuthorizedOperator} event. 97 | * 98 | * Requirements 99 | * 100 | * - `operator` cannot be calling address. 101 | */ 102 | function authorizeOperator(address operator) external; 103 | 104 | /** 105 | * @dev Revoke an account's operator status for the caller. 106 | * 107 | * See {isOperatorFor} and {defaultOperators}. 108 | * 109 | * Emits a {RevokedOperator} event. 110 | * 111 | * Requirements 112 | * 113 | * - `operator` cannot be calling address. 114 | */ 115 | function revokeOperator(address operator) external; 116 | 117 | /** 118 | * @dev Returns the list of default operators. These accounts are operators 119 | * for all token holders, even if {authorizeOperator} was never called on 120 | * them. 121 | * 122 | * This list is immutable, but individual holders may revoke these via 123 | * {revokeOperator}, in which case {isOperatorFor} will return false. 124 | */ 125 | function defaultOperators() external view returns (address[] memory); 126 | 127 | /** 128 | * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must 129 | * be an operator of `sender`. 130 | * 131 | * If send or receive hooks are registered for `sender` and `recipient`, 132 | * the corresponding functions will be called with `data` and 133 | * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. 134 | * 135 | * Emits a {Sent} event. 136 | * 137 | * Requirements 138 | * 139 | * - `sender` cannot be the zero address. 140 | * - `sender` must have at least `amount` tokens. 141 | * - the caller must be an operator for `sender`. 142 | * - `recipient` cannot be the zero address. 143 | * - if `recipient` is a contract, it must implement the {IERC777Recipient} 144 | * interface. 145 | */ 146 | function operatorSend( 147 | address sender, 148 | address recipient, 149 | uint256 amount, 150 | bytes calldata data, 151 | bytes calldata operatorData 152 | ) external; 153 | 154 | /** 155 | * @dev Destroys `amount` tokens from `account`, reducing the total supply. 156 | * The caller must be an operator of `account`. 157 | * 158 | * If a send hook is registered for `account`, the corresponding function 159 | * will be called with `data` and `operatorData`. See {IERC777Sender}. 160 | * 161 | * Emits a {Burned} event. 162 | * 163 | * Requirements 164 | * 165 | * - `account` cannot be the zero address. 166 | * - `account` must have at least `amount` tokens. 167 | * - the caller must be an operator for `account`. 168 | */ 169 | function operatorBurn( 170 | address account, 171 | uint256 amount, 172 | bytes calldata data, 173 | bytes calldata operatorData 174 | ) external; 175 | 176 | event Sent( 177 | address indexed operator, 178 | address indexed from, 179 | address indexed to, 180 | uint256 amount, 181 | bytes data, 182 | bytes operatorData 183 | ); 184 | 185 | event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); 186 | 187 | event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); 188 | 189 | event AuthorizedOperator(address indexed operator, address indexed tokenHolder); 190 | 191 | event RevokedOperator(address indexed operator, address indexed tokenHolder); 192 | } 193 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/IERC777Recipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. 7 | * 8 | * Accounts can be notified of {IERC777} tokens being sent to them by having a 9 | * contract implement this interface (contract holders can be their own 10 | * implementer) and registering it on the 11 | * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. 12 | * 13 | * See {IERC1820Registry} and {ERC1820Implementer}. 14 | */ 15 | interface IERC777Recipient { 16 | /** 17 | * @dev Called by an {IERC777} token contract whenever tokens are being 18 | * moved or created into a registered account (`to`). The type of operation 19 | * is conveyed by `from` being the zero address or not. 20 | * 21 | * This call occurs _after_ the token contract's state is updated, so 22 | * {IERC777-balanceOf}, etc., can be used to query the post-operation state. 23 | * 24 | * This function may revert to prevent the operation from being executed. 25 | */ 26 | function tokensReceived( 27 | address operator, 28 | address from, 29 | address to, 30 | uint256 amount, 31 | bytes calldata userData, 32 | bytes calldata operatorData 33 | ) external; 34 | } 35 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/IERC777Sender.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC777TokensSender standard as defined in the EIP. 7 | * 8 | * {IERC777} Token holders can be notified of operations performed on their 9 | * tokens by having a contract implement this interface (contract holders can be 10 | * their own implementer) and registering it on the 11 | * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. 12 | * 13 | * See {IERC1820Registry} and {ERC1820Implementer}. 14 | */ 15 | interface IERC777Sender { 16 | /** 17 | * @dev Called by an {IERC777} token contract whenever a registered holder's 18 | * (`from`) tokens are about to be moved or destroyed. The type of operation 19 | * is conveyed by `to` being the zero address or not. 20 | * 21 | * This call occurs _before_ the token contract's state is updated, so 22 | * {IERC777-balanceOf}, etc., can be used to query the pre-operation state. 23 | * 24 | * This function may revert to prevent the operation from being executed. 25 | */ 26 | function tokensToSend( 27 | address operator, 28 | address from, 29 | address to, 30 | uint256 amount, 31 | bytes calldata userData, 32 | bytes calldata operatorData 33 | ) external; 34 | } 35 | -------------------------------------------------------------------------------- /contracts-for-verification/tgt/draft-EIP712.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./ECDSA.sol"; 6 | 7 | /** 8 | * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. 9 | * 10 | * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, 11 | * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding 12 | * they need in their contracts using a combination of `abi.encode` and `keccak256`. 13 | * 14 | * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding 15 | * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA 16 | * ({_hashTypedDataV4}). 17 | * 18 | * The implementation of the domain separator was designed to be as efficient as possible while still properly updating 19 | * the chain id to protect against replay attacks on an eventual fork of the chain. 20 | * 21 | * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method 22 | * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. 23 | * 24 | * _Available since v3.4._ 25 | */ 26 | abstract contract EIP712 { 27 | /* solhint-disable var-name-mixedcase */ 28 | // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to 29 | // invalidate the cached domain separator if the chain id changes. 30 | bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; 31 | uint256 private immutable _CACHED_CHAIN_ID; 32 | 33 | bytes32 private immutable _HASHED_NAME; 34 | bytes32 private immutable _HASHED_VERSION; 35 | bytes32 private immutable _TYPE_HASH; 36 | 37 | /* solhint-enable var-name-mixedcase */ 38 | 39 | /** 40 | * @dev Initializes the domain separator and parameter caches. 41 | * 42 | * The meaning of `name` and `version` is specified in 43 | * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: 44 | * 45 | * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. 46 | * - `version`: the current major version of the signing domain. 47 | * 48 | * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart 49 | * contract upgrade]. 50 | */ 51 | constructor(string memory name, string memory version) { 52 | bytes32 hashedName = keccak256(bytes(name)); 53 | bytes32 hashedVersion = keccak256(bytes(version)); 54 | bytes32 typeHash = keccak256( 55 | "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" 56 | ); 57 | _HASHED_NAME = hashedName; 58 | _HASHED_VERSION = hashedVersion; 59 | _CACHED_CHAIN_ID = block.chainid; 60 | _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); 61 | _TYPE_HASH = typeHash; 62 | } 63 | 64 | /** 65 | * @dev Returns the domain separator for the current chain. 66 | */ 67 | function _domainSeparatorV4() internal view returns (bytes32) { 68 | if (block.chainid == _CACHED_CHAIN_ID) { 69 | return _CACHED_DOMAIN_SEPARATOR; 70 | } else { 71 | return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); 72 | } 73 | } 74 | 75 | function _buildDomainSeparator( 76 | bytes32 typeHash, 77 | bytes32 nameHash, 78 | bytes32 versionHash 79 | ) private view returns (bytes32) { 80 | return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); 81 | } 82 | 83 | /** 84 | * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this 85 | * function returns the hash of the fully encoded EIP712 message for this domain. 86 | * 87 | * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: 88 | * 89 | * ```solidity 90 | * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( 91 | * keccak256("Mail(address to,string contents)"), 92 | * mailTo, 93 | * keccak256(bytes(mailContents)) 94 | * ))); 95 | * address signer = ECDSA.recover(digest, signature); 96 | * ``` 97 | */ 98 | function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { 99 | return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /contracts-for-verification/vesting/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /** 6 | * @dev Interface of the ERC20 standard as defined in the EIP. 7 | */ 8 | interface IERC20 { 9 | /** 10 | * @dev Returns the amount of tokens in existence. 11 | */ 12 | function totalSupply() external view returns (uint256); 13 | 14 | /** 15 | * @dev Returns the amount of tokens owned by `account`. 16 | */ 17 | function balanceOf(address account) external view returns (uint256); 18 | 19 | /** 20 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 21 | * 22 | * Returns a boolean value indicating whether the operation succeeded. 23 | * 24 | * Emits a {Transfer} event. 25 | */ 26 | function transfer(address recipient, uint256 amount) external returns (bool); 27 | 28 | /** 29 | * @dev Returns the remaining number of tokens that `spender` will be 30 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 31 | * zero by default. 32 | * 33 | * This value changes when {approve} or {transferFrom} are called. 34 | */ 35 | function allowance(address owner, address spender) external view returns (uint256); 36 | 37 | /** 38 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 39 | * 40 | * Returns a boolean value indicating whether the operation succeeded. 41 | * 42 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 43 | * that someone may use both the old and the new allowance by unfortunate 44 | * transaction ordering. One possible solution to mitigate this race 45 | * condition is to first reduce the spender's allowance to 0 and set the 46 | * desired value afterwards: 47 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 48 | * 49 | * Emits an {Approval} event. 50 | */ 51 | function approve(address spender, uint256 amount) external returns (bool); 52 | 53 | /** 54 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 55 | * allowance mechanism. `amount` is then deducted from the caller's 56 | * allowance. 57 | * 58 | * Returns a boolean value indicating whether the operation succeeded. 59 | * 60 | * Emits a {Transfer} event. 61 | */ 62 | function transferFrom( 63 | address sender, 64 | address recipient, 65 | uint256 amount 66 | ) external returns (bool); 67 | 68 | /** 69 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 70 | * another (`to`). 71 | * 72 | * Note that `value` may be zero. 73 | */ 74 | event Transfer(address indexed from, address indexed to, uint256 value); 75 | 76 | /** 77 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 78 | * a call to {approve}. `value` is the new allowance. 79 | */ 80 | event Approval(address indexed owner, address indexed spender, uint256 value); 81 | } 82 | -------------------------------------------------------------------------------- /contracts-for-verification/vesting/vesting.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MPL 2 | 3 | pragma solidity ~0.8.4; 4 | 5 | import "./IERC20.sol"; 6 | 7 | interface ITGTERC20Metadata is IERC20 { 8 | function name() external view returns (string memory); 9 | function symbol() external view returns (string memory); 10 | function decimals() external view returns (uint8); 11 | function live() external view returns (uint64); 12 | } 13 | 14 | contract Vesting { 15 | ITGTERC20Metadata private _tgtContract; 16 | address private _owner; 17 | uint256 private _vestedBalance; 18 | 19 | mapping(address => VestingParams) private _vesting; 20 | 21 | struct VestingParams { 22 | //96bit are enough: max value is 1000000000000000000000000000 23 | //96bit are: 79228162514264337593543950336 24 | uint96 vestingAmount; 25 | //64bit for timestamp in seconds lasts 584 billion years 26 | uint64 vestingDuration; 27 | //how much vested funds were already claimed 28 | uint96 vestingClaimed; 29 | } 30 | 31 | event Vested(address indexed account, uint96 amount, uint64 vestingDuration); 32 | 33 | modifier onlyOwner(){ 34 | require(msg.sender == _owner, "Vesting: not the owner"); 35 | _; 36 | } 37 | 38 | constructor(address tgtContract) { 39 | _owner = msg.sender; 40 | _tgtContract = ITGTERC20Metadata(tgtContract); 41 | } 42 | 43 | function transferOwner(address newOwner) public virtual onlyOwner { 44 | require(newOwner != address(0), "Vesting: transfer owner the zero address"); 45 | require(newOwner != address(this), "Vesting: transfer owner to this contract"); 46 | 47 | _owner = newOwner; 48 | } 49 | 50 | function vest(address[] calldata accounts, uint96[] calldata amounts, 51 | uint64[] calldata vestingDurations) public virtual onlyOwner { 52 | require(accounts.length == amounts.length, "Vesting: accounts and amounts length must match"); 53 | require(amounts.length == vestingDurations.length, "Vesting: amounts and vestingDurations length must match"); 54 | 55 | for(uint256 i=0;i _tgtContract.live(), 'Vesting: timestamp now or in the past?'); 98 | require(_tgtContract.live() != 0, "Vesting: contract not live yet"); 99 | require(to != address(0), "Vesting: transfer from the zero address"); 100 | require(to != address(this), "Vesting: sender is this contract"); 101 | require(to != address(_tgtContract), "Vesting: sender is _tgtContract contract"); 102 | 103 | VestingParams storage v = _vesting[msg.sender]; 104 | 105 | require(amount <= claimableAmount(v), "TGT: cannot transfer vested funds"); 106 | 107 | v.vestingClaimed += amount; 108 | _vestedBalance -= amount; 109 | _tgtContract.transfer(to, amount); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /contracts/batchtransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | //interface IERC677Receiver { 7 | // function onTokenTransfer(address sender, uint256 value, bytes memory data) external; 8 | //} 9 | 10 | contract BatchTransfer { 11 | 12 | address private owner; 13 | constructor() { 14 | owner = msg.sender; 15 | } 16 | 17 | //gas cost for 50 fresh addresses: 1416965 gas 18 | function batchTransferDirect(IERC20 _erc20Contract, address[] calldata _to, uint256 _value) external { 19 | require(msg.sender == owner, "not owner"); 20 | for (uint i=0; i<_to.length; i++) { 21 | _erc20Contract.transfer(_to[i], _value); 22 | } 23 | } 24 | 25 | //gas cost for 50 fresh addresses: 1439876 gas 26 | /*function onTokenTransfer(address, uint256 _value, bytes memory _data) external { 27 | require(_data.length % 20 == 0); 28 | uint256 len = _data.length / 20; 29 | require(_value % len == 0); 30 | uint256 tokensToSend = _value/len; 31 | 32 | for(uint256 i=0; i uint32) private amountClaimedMap; 15 | 16 | constructor(address token_, address spender_, bytes32 merkleRoot_) { 17 | token = token_; 18 | owner = msg.sender; 19 | spender = spender_; 20 | merkleRoot = merkleRoot_; 21 | } 22 | 23 | function changeSpender(address newSpender) public virtual { 24 | require(msg.sender == owner, "Distributor: not the owner"); 25 | require(newSpender != address(0), "Distributor: change spender the zero address"); 26 | spender = newSpender; 27 | } 28 | 29 | function updateMerkleRoot(bytes32 merkleRoot_) public { 30 | require(msg.sender == owner, 'Distributor: only owner can update merkle root'); 31 | merkleRoot = merkleRoot_; 32 | } 33 | 34 | function transferOwnership(address newOwner) public virtual { 35 | require(msg.sender == owner, "Distributor: not the owner"); 36 | require(newOwner != address(0), "Distributor: transfer owner the zero address"); 37 | require(newOwner != address(this), "Distributor: transfer owner to this contract"); 38 | owner = newOwner; 39 | } 40 | 41 | // Note: The claimed amounts in the SC are stored without decimals to reduce gas costs. 42 | // This implies that 1 in the amountClaimedMap = 10**18 TGT 43 | function amountClaimed(address account) public view returns (uint32) { 44 | return amountClaimedMap[account]; 45 | } 46 | 47 | function claimableAmount(address account, uint32 amount, bytes32[] calldata merkleProof) external view returns (uint32) { 48 | // Verify the merkle proof. 49 | bytes32 node = keccak256(abi.encodePacked(account, amount)); 50 | require(MerkleProof.verify(merkleProof, merkleRoot, node), 'Distributor: Invalid proof.'); 51 | 52 | return amount - amountClaimed(account); 53 | } 54 | 55 | function claim(address account, uint32 amount, bytes32[] calldata merkleProof) external { 56 | uint32 amountClaimed = amountClaimed(account); 57 | require(amountClaimed < amount, 'Distributor: no more TGT to claim.'); 58 | 59 | // Verify the merkle proof. 60 | bytes32 node = keccak256(abi.encodePacked(account, amount)); 61 | require(MerkleProof.verify(merkleProof, merkleRoot, node), 'Distributor: Invalid proof.'); 62 | 63 | // amount to be transferred 64 | uint256 diff = uint256(amount - amountClaimed) * (10**18); 65 | 66 | // Mark it claimed and send the token. 67 | amountClaimedMap[account] = amount; 68 | require(IERC20(token).transferFrom(spender, account, diff), 'Distributor: Transfer failed.'); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /contracts/faucet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | contract Faucet { 7 | //once an address has claimed, it cannot claim anymore 8 | mapping(address => bool) private _used; 9 | //the ERC20 to be spent 10 | IERC20 private _contractAddress; 11 | //the owner of the generous token provider for this faucet 12 | address private _tokenOwner; 13 | //how much tokens we want to spend per claim 14 | uint256 private _tokensPerClaim; 15 | 16 | event Claim(address claimer, uint256 amount); 17 | 18 | constructor(address contractAddress, address tokenOwner, uint256 tokensPerClaim) { 19 | _contractAddress = IERC20(contractAddress); 20 | _tokenOwner = tokenOwner; 21 | _tokensPerClaim = tokensPerClaim; 22 | } 23 | 24 | function claim() external { 25 | require(!_used[msg.sender], "Same address cannot claim twice"); 26 | //send tokens to the one who called this contract 27 | _contractAddress.transferFrom(_tokenOwner, msg.sender, _tokensPerClaim); 28 | //mark the address of the one who called this contract, 29 | //so this address cannot claim again 30 | _used[msg.sender] = true; 31 | emit Claim(msg.sender, _tokensPerClaim); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/libraries/base64.sol: -------------------------------------------------------------------------------- 1 | // https://gist.github.com/farzaa/f13f5d9bda13af68cc96b54851345832 2 | /** 3 | *Submitted for verification at Etherscan.io on 2021-09-05 4 | */ 5 | 6 | // SPDX-License-Identifier: MIT 7 | 8 | pragma solidity ^0.8.4; 9 | 10 | /// [MIT License] 11 | /// @title Base64 12 | /// @notice Provides a function for encoding some bytes in base64 13 | /// @author Brecht Devos 14 | library Base64 { 15 | bytes internal constant TABLE = 16 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 17 | 18 | /// @notice Encodes some bytes to the base64 representation 19 | function encode(bytes memory data) internal pure returns (string memory) { 20 | uint256 len = data.length; 21 | if (len == 0) return ''; 22 | 23 | // multiply by 4/3 rounded up 24 | uint256 encodedLen = 4 * ((len + 2) / 3); 25 | 26 | // Add some extra buffer at the end 27 | bytes memory result = new bytes(encodedLen + 32); 28 | 29 | bytes memory table = TABLE; 30 | 31 | assembly { 32 | let tablePtr := add(table, 1) 33 | let resultPtr := add(result, 32) 34 | 35 | for { 36 | let i := 0 37 | } lt(i, len) { 38 | 39 | } { 40 | i := add(i, 3) 41 | let input := and(mload(add(data, i)), 0xffffff) 42 | 43 | let out := mload(add(tablePtr, and(shr(18, input), 0x3F))) 44 | out := shl(8, out) 45 | out := add( 46 | out, 47 | and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF) 48 | ) 49 | out := shl(8, out) 50 | out := add( 51 | out, 52 | and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF) 53 | ) 54 | out := shl(8, out) 55 | out := add( 56 | out, 57 | and(mload(add(tablePtr, and(input, 0x3F))), 0xFF) 58 | ) 59 | out := shl(224, out) 60 | 61 | mstore(resultPtr, out) 62 | 63 | resultPtr := add(resultPtr, 4) 64 | } 65 | 66 | switch mod(len, 3) 67 | case 1 { 68 | mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) 69 | } 70 | case 2 { 71 | mstore(sub(resultPtr, 1), shl(248, 0x3d)) 72 | } 73 | 74 | mstore(result, encodedLen) 75 | } 76 | 77 | return string(result); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /contracts/mocks/USDC.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | 8 | contract USDC is ERC20, ERC20Burnable, Ownable { 9 | constructor() ERC20("USDC", "USDC") {} 10 | 11 | function mint(address to, uint256 amount) public onlyOwner { 12 | _mint(to, amount); 13 | } 14 | 15 | function decimals() public view virtual override returns (uint8) { 16 | return 6; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/staking.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // combined from 4 | // https://github.com/Thorstarter/thorstarter-contracts/blob/main/contracts/Staking.sol 5 | // and: 6 | // https://github.com/goosedefi/goose-contracts/blob/master/contracts/MasterChefV2.sol 7 | // which was audited 8 | 9 | pragma solidity ^0.8.21; 10 | 11 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 12 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 13 | import "@openzeppelin/contracts/access/Ownable.sol"; 14 | import "@openzeppelin/contracts/utils/Multicall.sol"; 15 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 16 | 17 | contract Staking is Ownable, Multicall, ReentrancyGuard { 18 | using SafeERC20 for IERC20; 19 | 20 | /// @notice Info of each Staking user. 21 | /// `amount` LP token amount the user has provided. 22 | /// `rewardOffset` The amount of token which needs to be subtracted at the next harvesting event. 23 | struct UserInfo { 24 | uint256 amount; 25 | uint256 rewardOffset; 26 | } 27 | 28 | /// @notice Info of each Staking pool. 29 | /// `lpToken` The address of LP token contract. 30 | /// `allocPoint` The amount of allocation points assigned to the pool. 31 | /// Also known as the amount of token to distribute per block. 32 | struct PoolInfo { 33 | IERC20 lpToken; 34 | uint256 accRewardPerShare; 35 | uint256 lastRewardBlock; 36 | uint256 allocPoint; 37 | } 38 | 39 | // The amount of rewardTokens entitled to a user but is pending to be distributed is: 40 | // 41 | // pending reward = (user.amount * pool.accRewardPerShare) - user.rewardOffset 42 | // 43 | // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens: 44 | // 1. The pool's `accRewardPerShare` (and `lastRewardBlock`) gets updated. 45 | // 2. User receives the pending reward sent to his/her address. 46 | // 3. User's `amount` gets updated. 47 | // 4. User's `rewardOffset` gets updated. 48 | 49 | /// @notice Address of token contract. 50 | IERC20 public rewardToken; 51 | address public rewardOwner; 52 | 53 | /// @notice Info of each Staking pool. 54 | PoolInfo[] public poolInfo; 55 | 56 | /// @notice Info of each user that stakes LP tokens. 57 | mapping (uint256 => mapping (address => UserInfo)) public userInfo; 58 | /// @dev Total allocation points. Must be the sum of all allocation points in all pools. 59 | uint256 public totalAllocPoint = 0; 60 | 61 | uint256 public rewardPerBlock = 0; 62 | uint256 private constant ACC_PRECISION = 1e12; 63 | 64 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount, address indexed to); 65 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to); 66 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount, address indexed to); 67 | event Harvest(address indexed user, uint256 indexed pid, uint256 amount); 68 | event LogPoolAddition(uint256 indexed pid, uint256 allocPoint, IERC20 indexed lpToken); 69 | event LogSetPool(uint256 indexed pid, uint256 allocPoint); 70 | event LogUpdatePool(uint256 indexed pid, uint256 lastRewardBlock, uint256 lpSupply, uint256 accRewardPerShare); 71 | 72 | /// @param _rewardToken The reward token contract address. 73 | constructor(IERC20 _rewardToken, address _rewardOwner, uint256 _rewardPerBlock) Ownable() { 74 | rewardToken = _rewardToken; 75 | rewardOwner = _rewardOwner; 76 | rewardPerBlock = _rewardPerBlock; 77 | } 78 | 79 | /// @notice Sets the reward token. 80 | function setRewardToken(IERC20 _rewardToken) public onlyOwner { 81 | rewardToken = _rewardToken; 82 | } 83 | 84 | /// @notice Sets the reward owner. 85 | function setRewardOwner(address _rewardOwner) public onlyOwner { 86 | rewardOwner = _rewardOwner; 87 | } 88 | 89 | /// @notice Adjusts the reward per block. 90 | function setRewardsPerBlock(uint256 _rewardPerBlock) public onlyOwner { 91 | rewardPerBlock = _rewardPerBlock; 92 | } 93 | 94 | /// @notice Returns the number of Staking pools. 95 | function poolLength() public view returns (uint256 pools) { 96 | pools = poolInfo.length; 97 | } 98 | 99 | mapping(IERC20 => bool) public poolExistence; 100 | 101 | /// @notice Add a new LP to the pool. Can only be called by the owner. 102 | /// DO NOT add the same LP token more than once. Rewards will be messed up if you do. 103 | /// @param _allocPoint AP of the new pool. 104 | /// @param _lpToken Address of the LP ERC-20 token. 105 | function addPool(uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate) public onlyOwner { 106 | require(!poolExistence[_lpToken], "Staking: duplicated pool"); 107 | if (_withUpdate) { 108 | massUpdatePools(); 109 | } 110 | totalAllocPoint = totalAllocPoint + _allocPoint; 111 | 112 | poolExistence[_lpToken] = true; 113 | poolInfo.push(PoolInfo({ 114 | lpToken : _lpToken, 115 | allocPoint: _allocPoint, 116 | lastRewardBlock: block.number, 117 | accRewardPerShare: 0 118 | })); 119 | 120 | emit LogPoolAddition(poolInfo.length - 1, _allocPoint, _lpToken); 121 | } 122 | 123 | /// @notice Update the given pool's token allocation point. Can only be called by the owner. 124 | /// @param _pid The index of the pool. See `poolInfo`. 125 | /// @param _allocPoint New AP of the pool. 126 | function set(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner { 127 | if (_withUpdate) { 128 | massUpdatePools(); 129 | } 130 | totalAllocPoint = totalAllocPoint - poolInfo[_pid].allocPoint + _allocPoint; 131 | poolInfo[_pid].allocPoint = _allocPoint; 132 | 133 | emit LogSetPool(_pid, _allocPoint); 134 | } 135 | 136 | /// @notice View function to see pending token reward on frontend. 137 | /// @param _pid The index of the pool. See `poolInfo`. 138 | /// @param _user Address of user. 139 | /// @return pending token reward for a given user. 140 | function pendingRewards(uint256 _pid, address _user) external view returns (uint256) { 141 | PoolInfo memory pool = poolInfo[_pid]; 142 | UserInfo memory user = userInfo[_pid][_user]; 143 | uint256 accRewardPerShare = pool.accRewardPerShare; 144 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 145 | if (block.number > pool.lastRewardBlock && lpSupply != 0) { 146 | uint256 blocks = block.number - pool.lastRewardBlock; 147 | uint256 reward = (blocks * rewardPerBlock * pool.allocPoint) / totalAllocPoint; 148 | accRewardPerShare = accRewardPerShare + ((reward * ACC_PRECISION) / lpSupply); 149 | } 150 | uint256 accumulatedReward = (user.amount * accRewardPerShare) / ACC_PRECISION; 151 | return accumulatedReward - user.rewardOffset; 152 | } 153 | 154 | // Update reward variables for all pools. Be careful of gas spending! 155 | function massUpdatePools() public { 156 | for (uint256 i = 0; i < poolInfo.length; ++i) { 157 | updatePool(i); 158 | } 159 | } 160 | 161 | function massUpdatePoolsByIds(uint256[] calldata pids) external { 162 | for (uint256 i = 0; i < pids.length; ++i) { 163 | updatePool(pids[i]); 164 | } 165 | } 166 | 167 | /// @notice Update reward variables of the given pool. 168 | /// @param pid The index of the pool. See `poolInfo`. 169 | function updatePool(uint256 pid) public { 170 | PoolInfo storage pool = poolInfo[pid]; 171 | if (block.number <= pool.lastRewardBlock) { 172 | return; 173 | } 174 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 175 | if (lpSupply == 0 || pool.allocPoint == 0) { 176 | pool.lastRewardBlock = block.number; 177 | return; 178 | } 179 | uint256 blocks = block.number - pool.lastRewardBlock; 180 | uint256 reward = (blocks * rewardPerBlock * pool.allocPoint) / totalAllocPoint; 181 | pool.accRewardPerShare = pool.accRewardPerShare + ((reward * ACC_PRECISION) / lpSupply); 182 | pool.lastRewardBlock = block.number; 183 | 184 | emit LogUpdatePool(pid, pool.lastRewardBlock, lpSupply, pool.accRewardPerShare); 185 | } 186 | 187 | /// @notice Deposit LP tokens to Staking for reward token allocation. 188 | /// @param pid The index of the pool. See `poolInfo`. 189 | /// @param amount LP token amount to deposit. 190 | /// @param to The receiver of `amount` deposit benefit. 191 | function deposit(uint256 pid, uint256 amount, address to) public nonReentrant { 192 | updatePool(pid); 193 | PoolInfo memory pool = poolInfo[pid]; 194 | UserInfo storage user = userInfo[pid][msg.sender]; 195 | 196 | // harvest 197 | uint256 accumulatedReward = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 198 | uint256 pendingReward = accumulatedReward - user.rewardOffset; 199 | 200 | if (pendingReward > 0) { 201 | rewardToken.safeTransferFrom(rewardOwner, to, pendingReward); 202 | } 203 | 204 | if (amount > 0) { 205 | pool.lpToken.safeTransferFrom(address(msg.sender), address(this), amount); 206 | user.amount = user.amount + amount; 207 | } 208 | user.rewardOffset = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 209 | 210 | emit Deposit(msg.sender, pid, amount, to); 211 | } 212 | 213 | /// @notice Withdraw LP tokens from Staking. 214 | /// @param pid The index of the pool. See `poolInfo`. 215 | /// @param amount LP token amount to withdraw. 216 | /// @param to Receiver of the LP tokens. 217 | function withdraw(uint256 pid, uint256 amount, address to) public nonReentrant { 218 | updatePool(pid); 219 | PoolInfo memory pool = poolInfo[pid]; 220 | UserInfo storage user = userInfo[pid][msg.sender]; 221 | require(user.amount >= amount, "withdraw: not good"); 222 | 223 | // harvest 224 | uint256 accumulatedReward = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 225 | uint256 pendingReward = accumulatedReward - user.rewardOffset; 226 | if (pendingReward > 0) { 227 | rewardToken.safeTransferFrom(rewardOwner, to, pendingReward); 228 | } 229 | 230 | if (amount > 0) { 231 | user.amount = user.amount - amount; 232 | pool.lpToken.safeTransfer(to, amount); 233 | } 234 | user.rewardOffset = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 235 | 236 | emit Withdraw(msg.sender, pid, amount, to); 237 | } 238 | 239 | /// @notice Harvest proceeds for transaction sender to `to`. 240 | /// @param pid The index of the pool. See `poolInfo`. 241 | /// @param to Receiver of token rewards. 242 | function harvest(uint256 pid, address to) public { 243 | updatePool(pid); 244 | PoolInfo memory pool = poolInfo[pid]; 245 | UserInfo storage user = userInfo[pid][msg.sender]; 246 | 247 | uint256 accumulatedReward = (user.amount * pool.accRewardPerShare) / ACC_PRECISION; 248 | uint256 pendingReward = accumulatedReward - user.rewardOffset; 249 | user.rewardOffset = accumulatedReward; 250 | if (pendingReward > 0) { 251 | rewardToken.safeTransferFrom(rewardOwner, to, pendingReward); 252 | } 253 | 254 | emit Harvest(msg.sender, pid, pendingReward); 255 | } 256 | 257 | /// @notice Withdraw without caring about rewards. EMERGENCY ONLY. 258 | /// @param pid The index of the pool. See `poolInfo`. 259 | /// @param to Receiver of the LP tokens. 260 | function emergencyWithdraw(uint256 pid, address to) public { 261 | PoolInfo memory pool = poolInfo[pid]; 262 | UserInfo storage user = userInfo[pid][msg.sender]; 263 | 264 | uint256 amount = user.amount; 265 | 266 | user.amount = 0; 267 | user.rewardOffset = 0; 268 | pool.lpToken.safeTransfer(to, amount); 269 | 270 | emit EmergencyWithdraw(msg.sender, pid, amount, to); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /contracts/stakingV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // combined from 4 | // https://github.com/Thorstarter/thorstarter-contracts/blob/main/contracts/Staking.sol 5 | // and: 6 | // https://github.com/goosedefi/goose-contracts/blob/master/contracts/MasterChefV2.sol 7 | // which was audited 8 | 9 | pragma solidity ^0.8.21; 10 | 11 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 12 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 13 | import "@openzeppelin/contracts/access/Ownable.sol"; 14 | import "@openzeppelin/contracts/utils/Multicall.sol"; 15 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 16 | import "hardhat/console.sol"; 17 | 18 | contract StakingV2 is Ownable, Multicall, ReentrancyGuard { 19 | using SafeERC20 for IERC20; 20 | 21 | /// @notice Info of each Staking user. 22 | /// `amount` LP token amount the user has provided. 23 | /// `rewardOffset` The amount of token which needs to be subtracted at the next harvesting event. 24 | struct UserInfo { 25 | uint256 amount; 26 | mapping(address rewardToken => uint256) rewardDebt; 27 | } 28 | 29 | /// @notice Info of Staking pool. 30 | /// `lpToken` The address of LP token contract. 31 | /// `allocPoint` The amount of allocation points assigned to the pool. 32 | /// Also known as the amount of token to distribute per block. 33 | IERC20 public lpToken; 34 | uint256 public lastRewardBlock; 35 | uint256 public allocPoint; 36 | 37 | struct RewardInfo { 38 | IERC20 rewardToken; 39 | address rewardOwner; 40 | uint256 rewardPerBlock; 41 | uint256 accRewardPerShare; 42 | } 43 | 44 | // The amount of rewardTokens entitled to a user but is pending to be distributed is: 45 | // 46 | // pending reward = (user.amount * pool.accRewardPerShare) - user.rewardOffset 47 | // 48 | // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens: 49 | // 1. The pool's `accRewardPerShare` (and `lastRewardBlock`) gets updated. 50 | // 2. User receives the pending reward sent to his/her address. 51 | // 3. User's `amount` gets updated. 52 | // 4. User's `rewardOffset` gets updated. 53 | 54 | // @notice Info of each reward token. 55 | RewardInfo[] public rewards; 56 | 57 | /// @notice Info of each user that stakes LP tokens. 58 | mapping(address account => UserInfo) public userInfo; 59 | /// @dev Total allocation points. Must be the sum of all allocation points in all pools. 60 | uint256 public totalAllocPoint = 0; 61 | 62 | uint256 private constant ACC_PRECISION = 1e12; 63 | 64 | event Deposit(address indexed user, uint256 amount, address indexed to); 65 | event Withdraw(address indexed user, uint256 amount, address indexed to); 66 | event EmergencyWithdraw(address indexed user, uint256 amount, address indexed to); 67 | event Harvest(address indexed user, address indexed rewardToken, uint256 amount); 68 | event LogPoolAddition(uint256 allocPoint, IERC20 indexed lpToken); 69 | event LogSetPool(uint256 allocPoint); 70 | event LogUpdatePool(uint256 lastRewardBlock, uint256 lpSupply, uint256 accRewardPerShare); 71 | 72 | /// @param _rewardTokens The reward token contract address which will be distributed with rewardOwner and rewardPerBlock. 73 | constructor(RewardInfo[] memory _rewardTokens) Ownable() { 74 | for (uint256 i = 0; i < _rewardTokens.length; i++) { 75 | rewards.push(_rewardTokens[i]); 76 | } 77 | } 78 | 79 | /// @notice Sets the reward token. 80 | function setRewardInfo(RewardInfo memory _rewardInfo) public onlyOwner { 81 | for (uint256 i = 0; i < rewards.length; i++) { 82 | if (rewards[i].rewardToken == _rewardInfo.rewardToken) { 83 | rewards[i] = _rewardInfo; 84 | } 85 | } 86 | } 87 | 88 | /// @notice Add a new LP to the pool. Can only be called by the owner. 89 | /// DO NOT add the same LP token more than once. Rewards will be messed up if you do. 90 | /// @param _allocPoint AP of the new pool. 91 | /// @param _lpToken Address of the LP ERC-20 token. 92 | function setPool(uint256 _allocPoint, IERC20 _lpToken) public onlyOwner { 93 | totalAllocPoint = totalAllocPoint + _allocPoint; 94 | lpToken = _lpToken; 95 | allocPoint = _allocPoint; 96 | lastRewardBlock = block.number; 97 | 98 | emit LogPoolAddition(_allocPoint, _lpToken); 99 | } 100 | 101 | /// @notice Update the given pool's token allocation point. Can only be called by the owner. 102 | /// @param _allocPoint New AP of the pool. 103 | function set(uint256 _allocPoint) public onlyOwner { 104 | totalAllocPoint = totalAllocPoint - allocPoint + _allocPoint; 105 | allocPoint = _allocPoint; 106 | 107 | emit LogSetPool(_allocPoint); 108 | } 109 | 110 | /// @notice View function to see pending token reward on frontend. 111 | /// @param _user Address of user. 112 | /// @return pending token reward for a given user. 113 | function pendingRewards(address _user, address _rewardToken) external view returns (uint256) { 114 | UserInfo storage user = userInfo[_user]; 115 | RewardInfo memory rewardInfo; 116 | for (uint256 i = 0; i < rewards.length; i++) { 117 | if (address(rewards[i].rewardToken) == _rewardToken) { 118 | rewardInfo = rewards[i]; 119 | } 120 | } 121 | uint256 lpSupply = lpToken.balanceOf(address(this)); 122 | if (block.number > lastRewardBlock && lpSupply != 0) { 123 | uint256 blocks = block.number - lastRewardBlock; 124 | uint256 reward = (blocks * rewardInfo.rewardPerBlock * allocPoint) / totalAllocPoint; 125 | rewardInfo.accRewardPerShare = rewardInfo.accRewardPerShare + ((reward * ACC_PRECISION) / lpSupply); 126 | } 127 | uint256 accumulatedReward = (user.amount * rewardInfo.accRewardPerShare) / ACC_PRECISION; 128 | return accumulatedReward - user.rewardDebt[_rewardToken]; 129 | } 130 | 131 | /// @notice Update reward variables of the given pool. 132 | function updatePool() public { 133 | console.log("updatePool"); 134 | console.log("block.number", block.number); 135 | console.log("lastRewardBlock", lastRewardBlock); 136 | if (block.number <= lastRewardBlock) { 137 | return; 138 | } 139 | uint256 lpSupply = lpToken.balanceOf(address(this)); 140 | if (lpSupply == 0 || allocPoint == 0) { 141 | lastRewardBlock = block.number; 142 | return; 143 | } 144 | for (uint256 i = 0; i < rewards.length; i++) { 145 | RewardInfo storage rewardInfo = rewards[i]; 146 | uint256 blocks = block.number - lastRewardBlock; 147 | uint256 reward = (blocks * rewardInfo.rewardPerBlock * allocPoint) / totalAllocPoint; 148 | rewardInfo.accRewardPerShare = rewardInfo.accRewardPerShare + ((reward * ACC_PRECISION) / lpSupply); 149 | lastRewardBlock = block.number; 150 | console.log("LogUpdatePool", lastRewardBlock, lpSupply, rewardInfo.accRewardPerShare); 151 | emit LogUpdatePool(lastRewardBlock, lpSupply, rewardInfo.accRewardPerShare); 152 | } 153 | } 154 | 155 | /// @notice Deposit LP tokens to Staking for reward token allocation. 156 | /// @param amount LP token amount to deposit. 157 | /// @param to The receiver of `amount` deposit benefit. 158 | function deposit(uint256 amount, address to) public nonReentrant { 159 | updatePool(); 160 | UserInfo storage user = userInfo[msg.sender]; 161 | for (uint256 i = 0; i < rewards.length; i++) { 162 | IERC20 rewardToken = rewards[i].rewardToken; 163 | // harvest 164 | uint256 accumulatedReward = (user.amount * rewards[i].accRewardPerShare) / ACC_PRECISION; 165 | uint256 pendingReward = accumulatedReward - user.rewardDebt[address(rewardToken)]; 166 | 167 | if (pendingReward > 0) { 168 | rewardToken.safeTransferFrom(rewards[i].rewardOwner, to, pendingReward); 169 | } 170 | 171 | user.rewardDebt[address(rewardToken)] = (user.amount * rewards[i].accRewardPerShare) / ACC_PRECISION; 172 | } 173 | 174 | if (amount > 0) { 175 | lpToken.safeTransferFrom(address(msg.sender), address(this), amount); 176 | user.amount = user.amount + amount; 177 | } 178 | 179 | emit Deposit(msg.sender, amount, to); 180 | } 181 | 182 | /// @notice Withdraw LP tokens from Staking. 183 | /// @param amount LP token amount to withdraw. 184 | /// @param to Receiver of the LP tokens. 185 | function withdraw(uint256 amount, address to) public nonReentrant { 186 | updatePool(); 187 | UserInfo storage user = userInfo[msg.sender]; 188 | require(user.amount >= amount, "withdraw: not good"); 189 | for (uint256 i = 0; i < rewards.length; i++) { 190 | IERC20 rewardToken = rewards[i].rewardToken; 191 | // harvest 192 | uint256 accumulatedReward = (user.amount * rewards[i].accRewardPerShare) / ACC_PRECISION; 193 | uint256 pendingReward = accumulatedReward - user.rewardDebt[address(rewardToken)]; 194 | if (pendingReward > 0) { 195 | rewardToken.safeTransferFrom(rewards[i].rewardOwner, to, pendingReward); 196 | } 197 | 198 | user.rewardDebt[address(rewardToken)] = (user.amount * rewards[i].accRewardPerShare) / ACC_PRECISION; 199 | } 200 | 201 | if (amount > 0) { 202 | user.amount = user.amount - amount; 203 | lpToken.safeTransfer(to, amount); 204 | } 205 | 206 | emit Withdraw(msg.sender, amount, to); 207 | } 208 | 209 | /// @notice Harvest proceeds for transaction sender to `to`. 210 | /// @param to Receiver of token rewards. 211 | function harvest(address to) public { 212 | updatePool(); 213 | UserInfo storage user = userInfo[msg.sender]; 214 | for (uint256 i = 0; i < rewards.length; i++) { 215 | IERC20 rewardToken = rewards[i].rewardToken; 216 | console.log("rewardToken: %s", address(rewardToken)); 217 | console.log("user.amount: %s", user.amount); 218 | console.log("rewards[i].accRewardPerShare: %s", rewards[i].accRewardPerShare); 219 | uint256 accumulatedReward = (user.amount * rewards[i].accRewardPerShare) / ACC_PRECISION; 220 | uint256 pendingReward = accumulatedReward - user.rewardDebt[address(rewardToken)]; 221 | user.rewardDebt[address(rewardToken)] = accumulatedReward; 222 | console.log("pendingReward: %s", pendingReward); 223 | if (pendingReward > 0) { 224 | rewardToken.safeTransferFrom(rewards[i].rewardOwner, to, pendingReward); 225 | } 226 | emit Harvest(msg.sender, address(rewardToken), pendingReward); 227 | } 228 | } 229 | 230 | /// @notice Withdraw without caring about rewards. EMERGENCY ONLY. 231 | /// @param to Receiver of the LP tokens. 232 | function emergencyWithdraw(address to) public { 233 | UserInfo storage user = userInfo[msg.sender]; 234 | 235 | uint256 amount = user.amount; 236 | 237 | user.amount = 0; 238 | for (uint256 i = 0; i < rewards.length; i++) { 239 | user.rewardDebt[address(rewards[i].rewardToken)] = 0; 240 | } 241 | lpToken.safeTransfer(to, amount); 242 | emit EmergencyWithdraw(msg.sender, amount, to); 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /contracts/twanft.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.8; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import {Base64} from './libraries/base64.sol'; 8 | 9 | contract TWNFT is ERC721Enumerable, Ownable { 10 | //This token address will be checked if enough tokens are available 11 | address public requireTokenAddress; 12 | 13 | //After this date, public minting of soldiers will be possible. Before this date, only the owner and generals can be minted. 14 | uint256 private constant START_TIMESTAMP = 1633521600; //06.10.2021, 12:00 GMT (14:00 CEST) 15 | 16 | //the address that minted an NFT cannot mint it again. You would need to send your tokens to another address to mint again 17 | mapping(address => bool) addressMinted; 18 | //for investors, we can whitelist addresses in order they can mint generals 19 | mapping(address => bool) whitelist; 20 | 21 | string private constant COMMANDER = "bafybeick7bj677i3fhq5upoc2vplu4gmgmgdpacbtahx6f4uqkgl7dattq"; //rank 1 22 | string private constant GENERAL = "bafybeidzfegfpn24lpjkf235tdhqvk26b2bsr7ye7sj2ggsodxk4rs25ju"; //rank 2 23 | string private constant SOLDIER = "bafybeiceynivdpcrnu633gmcyp36x4lw7avehpznbjx5i62a52nqh2lk5i"; //rank 3 24 | string private constant ARMY = "bafybeihn4y3bf364ihww2ogzpqavuryhjaeakvpk6ci7rrkjj4mmsqjpae"; //image for platform 25 | 26 | uint256 private constant COMMANDER_MINT = 5000000 * 1e18; //5,000,000 x 10^18 27 | uint256 private constant GENERAL_MINT = 500000 * 1e18; // 500,000 x 10^18 28 | uint256 private constant SOLDIER_MINT = 1000 * 1e18; // 1,000 x 10^18 29 | 30 | uint256 private constant FIRST_COMMANDER_ID = 1; 31 | uint256 private constant FIRST_GENERAL_ID = 21; 32 | uint256 private constant FIRST_DEV_SOLDIER_ID = 221; 33 | uint256 private constant FIRST_PUB_SOLDIER_ID = 421; 34 | uint256 private constant FIRST_INVALID = 2221; 35 | 36 | uint256 private nextCommanderId = FIRST_COMMANDER_ID; // 1-20 37 | uint256 private nextGeneralId = FIRST_GENERAL_ID; // 21-220 38 | uint256 private nextDevSoldierId = FIRST_DEV_SOLDIER_ID; //221-420 39 | uint256 private nextPubSoldierId = FIRST_PUB_SOLDIER_ID; //421-2220 40 | 41 | bool public releasedTGT; 42 | 43 | string private constant datajson = "data:application/json;base64,"; 44 | string private baseURI = "ipfs://"; 45 | string private description = "THORWallet NFTs consist of 20 Commanders, 200 Generals and 2000 Soldiers that unlock unique premium features in the THORWallet."; 46 | 47 | constructor() ERC721("THORWallet Army", "TWA"){} 48 | 49 | //This is for external application to know the current supply of the ranks 50 | //1: the number of commanders minted. If one commander is added, nextCommanderId will be set to 2, while FIRST_COMMANDER_ID is 1, hence 1 51 | //2: the number of generals minted. If one general is added nextGeneralId will be set to 22, while FIRST_GENERAL_ID is 21, hence 1 52 | //3: the number of dev and pub soldiers. If one dev soldier will be added nextDevSoldierId, will be 222, FIRST_DEV_SOLDIER_ID is 221, hence 1 53 | //here we need to add the pub soldier: if one pub soldier will be added nextPubSoldierId, will be 422, FIRST_PUB_SOLDIER_ID is 421, hence 1 54 | function currentSupply(uint256 _rank) public view returns (uint256) { 55 | if (_rank == 1) { 56 | return nextCommanderId - FIRST_COMMANDER_ID; 57 | } else if (_rank == 2) { 58 | return nextGeneralId - FIRST_GENERAL_ID; 59 | } else if (_rank == 3) { 60 | return (nextDevSoldierId - FIRST_DEV_SOLDIER_ID) + (nextPubSoldierId - FIRST_PUB_SOLDIER_ID); 61 | } else { 62 | revert("currentSupply: invalid rank"); 63 | } 64 | } 65 | 66 | //This is for external application to know the max supply 67 | //Not to be confused with totalSupply, which is the number of minted NFTs 68 | function maxSupply() public pure returns (uint256) { 69 | return FIRST_INVALID - 1; 70 | } 71 | 72 | function addAddressesToWhitelist(address[] memory addrs) onlyOwner public { 73 | for (uint256 i = 0; i < addrs.length; i++) { 74 | whitelist[addrs[i]] = true; 75 | } 76 | } 77 | 78 | function removeAddressFromWhitelist(address addr) onlyOwner public { 79 | whitelist[addr] = false; 80 | } 81 | 82 | function whitelisted(address _address) public view returns (bool) { 83 | return whitelist[_address]; 84 | } 85 | 86 | function minted(address _address) public view returns (bool) { 87 | return addressMinted[_address]; 88 | } 89 | 90 | function setBaseURI(string calldata _baseURI) onlyOwner public { 91 | baseURI = _baseURI; 92 | } 93 | 94 | function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { 95 | require(_exists(tokenId), "ERC721: URI query for nonexistent token"); 96 | 97 | string memory rank; 98 | string memory rankImage; 99 | 100 | if (tokenId < FIRST_GENERAL_ID) { 101 | rank = "Commander"; 102 | rankImage = COMMANDER; 103 | } else if (tokenId < FIRST_DEV_SOLDIER_ID) { 104 | rank = "General"; 105 | rankImage = GENERAL; 106 | } else { 107 | rank = "Soldier"; 108 | rankImage = SOLDIER; 109 | } 110 | 111 | return string( 112 | abi.encodePacked( 113 | datajson, 114 | Base64.encode( 115 | bytes( 116 | abi.encodePacked( 117 | '{"name":"THORWallet - ', 118 | rank, 119 | '","description":"', 120 | description, 121 | '","attributes":[{"trait_type": "Rank", "value": "', 122 | rank, 123 | '"}], "image":"', 124 | baseURI, 125 | rankImage, 126 | '"}' 127 | ) 128 | ) 129 | ) 130 | ) 131 | ); 132 | } 133 | 134 | function getLevel(address _address) public view returns (uint256) { 135 | //Levels: 0=None, 1=Commander, 2=General, 3=Soldier 136 | uint256 addressBalance = balanceOf(_address); 137 | 138 | if (addressBalance == 0) { 139 | return 0; //No NFT held 140 | } 141 | 142 | uint256 lowest = tokenOfOwnerByIndex(_address, 0); 143 | uint256 id; 144 | 145 | for (uint256 i=0; i < addressBalance; i++) { 146 | id = tokenOfOwnerByIndex(_address, i); 147 | if(id < lowest) { 148 | lowest = id; 149 | } 150 | } 151 | 152 | if (lowest >= FIRST_DEV_SOLDIER_ID) { 153 | return 3; // Soldier TokenId 221-2220 -> 2000 Soldiers 154 | } else if (lowest >= FIRST_GENERAL_ID) { 155 | return 2; // General TokenId 21-220 -> 200 Generals 156 | } else { 157 | return 1; // Commander TokenId 1-20 -> 20 Commanders 158 | } 159 | } 160 | 161 | function setRequiredToken(address _tokenAddress) public onlyOwner { 162 | requireTokenAddress = _tokenAddress; 163 | } 164 | 165 | function setReleasedTGT(bool _released) public onlyOwner { 166 | releasedTGT = _released; 167 | } 168 | 169 | //used for external projects, e.g., https://docs.opensea.io/docs/contract-level-metadata 170 | function contractURI() public view returns (string memory) { 171 | return string( 172 | abi.encodePacked( 173 | datajson, Base64.encode( 174 | bytes( 175 | abi.encodePacked( 176 | '{"name": "THORWallet Army", "description": "', 177 | description, 178 | '", "image": "', 179 | baseURI, 180 | ARMY, 181 | '", "external_link": "https://thorwallet.org"}' 182 | ) 183 | ) 184 | ) 185 | ) 186 | ); 187 | } 188 | 189 | function mintWhitelisted() public { 190 | require(whitelist[msg.sender], "Address not Whitelisted"); 191 | require(!addressMinted[msg.sender], "Prior mint by address"); 192 | //check if General limt reached, if we are already at the first dev soldier 193 | require(nextGeneralId < FIRST_DEV_SOLDIER_ID, "General limit reached"); 194 | 195 | uint256 tokenId = nextGeneralId; 196 | nextGeneralId = nextGeneralId + 1; 197 | 198 | _safeMint(msg.sender, tokenId); 199 | 200 | //add msg.sender to list of minters, who already minted (they cannot mint twice) 201 | addressMinted[msg.sender] = true; 202 | } 203 | 204 | //mints multiple NFTs of same rank to provided addresses 205 | function mintByOwner(address[] memory _addresses, uint256 _rank) public onlyOwner { 206 | require(_addresses.length >= 1, "No addresses"); 207 | 208 | uint256 tokenId; 209 | 210 | for (uint256 i=0; i < _addresses.length; i++) { 211 | if (_rank == 1) { 212 | //check if Commander limit reached, if we are already at the first general id 213 | require(nextCommanderId < FIRST_GENERAL_ID, "Commander limit reached"); 214 | tokenId = nextCommanderId; 215 | nextCommanderId = nextCommanderId + 1; 216 | } else if (_rank == 2) { 217 | //check if General limit reached, if we are already at the first dev soldier 218 | require(nextGeneralId < FIRST_DEV_SOLDIER_ID, "General limit reached"); 219 | tokenId = nextGeneralId; 220 | nextGeneralId = nextGeneralId + 1; 221 | } else if (_rank == 3) { 222 | //check if Dev Soldier limit reached, if we are already at the public soldier 223 | require(nextDevSoldierId < FIRST_PUB_SOLDIER_ID, "Dev Soldier limit reached"); 224 | tokenId = nextDevSoldierId; 225 | nextDevSoldierId = nextDevSoldierId + 1; 226 | } else { 227 | revert("mintByOwner: invalid rank"); 228 | } 229 | 230 | _safeMint(_addresses[i], tokenId); 231 | } 232 | } 233 | 234 | function mint() public { 235 | require(block.timestamp >= START_TIMESTAMP, "Claiming not yet started"); 236 | //restricts minting address to 1 nft 237 | require(!addressMinted[msg.sender], "Prior mint by address"); 238 | 239 | //check token quantity 240 | uint256 bal = IERC20(requireTokenAddress).balanceOf(msg.sender); 241 | //check if insufficient balance, if soldier level not reached 242 | require(bal >= SOLDIER_MINT , "Insufficient balance"); 243 | 244 | uint256 tokenId; 245 | 246 | if (bal >= COMMANDER_MINT && releasedTGT) { 247 | //check if Commander limit reached, if we are already at the first general id 248 | require(nextCommanderId < FIRST_GENERAL_ID, "Commander limit reached"); 249 | tokenId = nextCommanderId; 250 | nextCommanderId = nextCommanderId + 1; 251 | 252 | } else if (bal >= GENERAL_MINT && releasedTGT) { 253 | //check if General limit reached, if we are already at the first dev soldier 254 | require(nextGeneralId < FIRST_DEV_SOLDIER_ID, "General limit reached"); 255 | tokenId = nextGeneralId; 256 | nextGeneralId = nextGeneralId + 1; 257 | 258 | } else { 259 | //check if Soldier limit reached, if we are already at the first invalid id 260 | require(nextPubSoldierId < FIRST_INVALID, "Soldier limit reached"); 261 | tokenId = nextPubSoldierId; 262 | nextPubSoldierId = nextPubSoldierId + 1; 263 | } 264 | 265 | _safeMint(msg.sender, tokenId); 266 | 267 | //add msg.sender to list of minters, who already minted (they cannot mint twice) 268 | addressMinted[msg.sender] = true; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /contracts/vesting.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MPL 2 | 3 | pragma solidity ~0.8.4; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | interface ITGTERC20Metadata is IERC20 { 8 | function name() external view returns (string memory); 9 | function symbol() external view returns (string memory); 10 | function decimals() external view returns (uint8); 11 | function live() external view returns (uint64); 12 | } 13 | 14 | contract Vesting { 15 | ITGTERC20Metadata private _tgtContract; 16 | address private _owner; 17 | uint256 private _vestedBalance; 18 | 19 | mapping(address => VestingParams) private _vesting; 20 | 21 | struct VestingParams { 22 | //96bit are enough: max value is 1000000000000000000000000000 23 | //96bit are: 79228162514264337593543950336 24 | uint96 vestingAmount; 25 | //64bit for timestamp in seconds lasts 584 billion years 26 | uint64 vestingDuration; 27 | //how much vested funds were already claimed 28 | uint96 vestingClaimed; 29 | } 30 | 31 | event Vested(address indexed account, uint96 amount, uint64 vestingDuration); 32 | 33 | modifier onlyOwner(){ 34 | require(msg.sender == _owner, "Vesting: not the owner"); 35 | _; 36 | } 37 | 38 | constructor(address tgtContract) { 39 | _owner = msg.sender; 40 | _tgtContract = ITGTERC20Metadata(tgtContract); 41 | } 42 | 43 | function transferOwner(address newOwner) public virtual onlyOwner { 44 | require(newOwner != address(0), "Vesting: transfer owner the zero address"); 45 | require(newOwner != address(this), "Vesting: transfer owner to this contract"); 46 | 47 | _owner = newOwner; 48 | } 49 | 50 | function vest(address[] calldata accounts, uint96[] calldata amounts, 51 | uint64[] calldata vestingDurations) public virtual onlyOwner { 52 | require(accounts.length == amounts.length, "Vesting: accounts and amounts length must match"); 53 | require(amounts.length == vestingDurations.length, "Vesting: amounts and vestingDurations length must match"); 54 | 55 | for(uint256 i=0;i _tgtContract.live(), 'Vesting: timestamp now or in the past?'); 98 | require(_tgtContract.live() != 0, "Vesting: contract not live yet"); 99 | require(to != address(0), "Vesting: transfer from the zero address"); 100 | require(to != address(this), "Vesting: sender is this contract"); 101 | require(to != address(_tgtContract), "Vesting: sender is _tgtContract contract"); 102 | 103 | VestingParams storage v = _vesting[msg.sender]; 104 | 105 | require(amount <= claimableAmount(v), "TGT: cannot transfer vested funds"); 106 | 107 | v.vestingClaimed += amount; 108 | _vestedBalance -= amount; 109 | _tgtContract.transfer(to, amount); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type import('hardhat/config').HardhatUserConfig 3 | */ 4 | 5 | require("@nomiclabs/hardhat-waffle"); 6 | require("hardhat-gas-reporter"); 7 | require('dotenv').config() 8 | 9 | 10 | const {task} = require("hardhat/config"); 11 | 12 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 13 | const accounts = await hre.ethers.getSigners(); 14 | 15 | for (const account of accounts) { 16 | console.log(account.address); 17 | } 18 | }); 19 | 20 | module.exports = { 21 | defaultNetwork: "hardhat", 22 | networks: { 23 | hardhat: { 24 | mining: { 25 | auto: true, // required to be able to run tests correctly 26 | interval: 0 27 | } 28 | } 29 | }, 30 | solidity: { 31 | version: "0.8.21", 32 | settings: { 33 | optimizer: { 34 | enabled: true, 35 | runs: 800 36 | } 37 | } 38 | }, 39 | 40 | gasReporter: { 41 | currency: "USD", 42 | token: "ETH", 43 | gasPrice: 15, 44 | // gasPriceApi: 45 | // "https://api.etherscan.com/api?module=proxy&action=eth_gasPrice&apikey=" + process.env.ETHERSCAN_API_KEY, 46 | enabled: process.env.REPORT_GAS, 47 | excludeContracts: [], 48 | src: "./contracts", 49 | coinmarketcap: process.env.COINMARKETCAP_API_KEY 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tgt-contracts", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "devDependencies": { 6 | "hardhat": "^2.13.1", 7 | "@nomicfoundation/hardhat-network-helpers": "^1.0.8", 8 | "@nomiclabs/hardhat-ethers": "^2.2.3", 9 | "@nomiclabs/hardhat-waffle": "^2.0.6", 10 | "@openzeppelin/contracts": "^4.8.3", 11 | "@openzeppelin/test-helpers": "^0.5.16", 12 | "@prb/math": "^4.0.1", 13 | "ansi-regex": ">=5.0.1", 14 | "chai": "^4.3.4", 15 | "ethereum-waffle": "^3.4.0", 16 | "ethers": "^5.7.2", 17 | "hardhat-gas-reporter": "^1.0.9", 18 | "mem": ">=4.0.0", 19 | "merkletreejs": "^0.2.24", 20 | "dotenv": "^10.0.0" 21 | }, 22 | "scripts": { 23 | "clean": "hardhat clean", 24 | "build": "hardhat compile", 25 | "test": "hardhat test" 26 | }, 27 | "resolutions": { 28 | "glob-parent": "^5.1.2", 29 | "underscore": "^1.12.1", 30 | "node-fetch": "^2.6.1", 31 | "ansi-regex": "^5.0.1", 32 | "yargs-parser": "^13.1.2", 33 | "mem": "^4.0.0", 34 | "lodash": "^4.17.21" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /staking-contracts/GooseDefi_MasterChef.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./libs/IBEP20.sol"; 7 | import "./libs/SafeBEP20.sol"; 8 | import "@openzeppelin/contracts/access/Ownable.sol"; 9 | 10 | import "./EggToken.sol"; 11 | 12 | // MasterChef is the master of Egg. He can make Egg and he is a fair guy. 13 | // 14 | // Note that it's ownable and the owner wields tremendous power. The ownership 15 | // will be transferred to a governance smart contract once EGG is sufficiently 16 | // distributed and the community can show to govern itself. 17 | // 18 | // Have fun reading it. Hopefully it's bug-free. God bless. 19 | contract MasterChef is Ownable { 20 | using SafeMath for uint256; 21 | using SafeBEP20 for IBEP20; 22 | 23 | // Info of each user. 24 | struct UserInfo { 25 | uint256 amount; // How many LP tokens the user has provided. 26 | uint256 rewardDebt; // Reward debt. See explanation below. 27 | // 28 | // We do some fancy math here. Basically, any point in time, the amount of EGGs 29 | // entitled to a user but is pending to be distributed is: 30 | // 31 | // pending reward = (user.amount * pool.accEggPerShare) - user.rewardDebt 32 | // 33 | // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens: 34 | // 1. The pool's `accEggPerShare` (and `lastRewardBlock`) gets updated. 35 | // 2. User receives the pending reward sent to his/her address. 36 | // 3. User's `amount` gets updated. 37 | // 4. User's `rewardDebt` gets updated. 38 | } 39 | 40 | // Info of each pool. 41 | struct PoolInfo { 42 | IBEP20 lpToken; // Address of LP token contract. 43 | uint256 allocPoint; // How many allocation points assigned to this pool. EGGs to distribute per block. 44 | uint256 lastRewardBlock; // Last block number that EGGs distribution occurs. 45 | uint256 accEggPerShare; // Accumulated EGGs per share, times 1e12. See below. 46 | uint16 depositFeeBP; // Deposit fee in basis points 47 | } 48 | 49 | // The EGG TOKEN! 50 | EggToken public egg; 51 | // Dev address. 52 | address public devaddr; 53 | // EGG tokens created per block. 54 | uint256 public eggPerBlock; 55 | // Bonus muliplier for early egg makers. 56 | uint256 public constant BONUS_MULTIPLIER = 1; 57 | // Deposit Fee address 58 | address public feeAddress; 59 | 60 | // Info of each pool. 61 | PoolInfo[] public poolInfo; 62 | // Info of each user that stakes LP tokens. 63 | mapping (uint256 => mapping (address => UserInfo)) public userInfo; 64 | // Total allocation points. Must be the sum of all allocation points in all pools. 65 | uint256 public totalAllocPoint = 0; 66 | // The block number when EGG mining starts. 67 | uint256 public startBlock; 68 | 69 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 70 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 71 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 72 | 73 | constructor( 74 | EggToken _egg, 75 | address _devaddr, 76 | address _feeAddress, 77 | uint256 _eggPerBlock, 78 | uint256 _startBlock 79 | ) public { 80 | egg = _egg; 81 | devaddr = _devaddr; 82 | feeAddress = _feeAddress; 83 | eggPerBlock = _eggPerBlock; 84 | startBlock = _startBlock; 85 | } 86 | 87 | function poolLength() external view returns (uint256) { 88 | return poolInfo.length; 89 | } 90 | 91 | // Add a new lp to the pool. Can only be called by the owner. 92 | // XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do. 93 | function add(uint256 _allocPoint, IBEP20 _lpToken, uint16 _depositFeeBP, bool _withUpdate) public onlyOwner { 94 | require(_depositFeeBP <= 10000, "add: invalid deposit fee basis points"); 95 | if (_withUpdate) { 96 | massUpdatePools(); 97 | } 98 | uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock; 99 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 100 | poolInfo.push(PoolInfo({ 101 | lpToken: _lpToken, 102 | allocPoint: _allocPoint, 103 | lastRewardBlock: lastRewardBlock, 104 | accEggPerShare: 0, 105 | depositFeeBP: _depositFeeBP 106 | })); 107 | } 108 | 109 | // Update the given pool's EGG allocation point and deposit fee. Can only be called by the owner. 110 | function set(uint256 _pid, uint256 _allocPoint, uint16 _depositFeeBP, bool _withUpdate) public onlyOwner { 111 | require(_depositFeeBP <= 10000, "set: invalid deposit fee basis points"); 112 | if (_withUpdate) { 113 | massUpdatePools(); 114 | } 115 | totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); 116 | poolInfo[_pid].allocPoint = _allocPoint; 117 | poolInfo[_pid].depositFeeBP = _depositFeeBP; 118 | } 119 | 120 | // Return reward multiplier over the given _from to _to block. 121 | function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) { 122 | return _to.sub(_from).mul(BONUS_MULTIPLIER); 123 | } 124 | 125 | // View function to see pending EGGs on frontend. 126 | function pendingEgg(uint256 _pid, address _user) external view returns (uint256) { 127 | PoolInfo storage pool = poolInfo[_pid]; 128 | UserInfo storage user = userInfo[_pid][_user]; 129 | uint256 accEggPerShare = pool.accEggPerShare; 130 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 131 | if (block.number > pool.lastRewardBlock && lpSupply != 0) { 132 | uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); 133 | uint256 eggReward = multiplier.mul(eggPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 134 | accEggPerShare = accEggPerShare.add(eggReward.mul(1e12).div(lpSupply)); 135 | } 136 | return user.amount.mul(accEggPerShare).div(1e12).sub(user.rewardDebt); 137 | } 138 | 139 | // Update reward variables for all pools. Be careful of gas spending! 140 | function massUpdatePools() public { 141 | uint256 length = poolInfo.length; 142 | for (uint256 pid = 0; pid < length; ++pid) { 143 | updatePool(pid); 144 | } 145 | } 146 | 147 | // Update reward variables of the given pool to be up-to-date. 148 | function updatePool(uint256 _pid) public { 149 | PoolInfo storage pool = poolInfo[_pid]; 150 | if (block.number <= pool.lastRewardBlock) { 151 | return; 152 | } 153 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 154 | if (lpSupply == 0 || pool.allocPoint == 0) { 155 | pool.lastRewardBlock = block.number; 156 | return; 157 | } 158 | uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); 159 | uint256 eggReward = multiplier.mul(eggPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 160 | egg.mint(devaddr, eggReward.div(10)); 161 | egg.mint(address(this), eggReward); 162 | pool.accEggPerShare = pool.accEggPerShare.add(eggReward.mul(1e12).div(lpSupply)); 163 | pool.lastRewardBlock = block.number; 164 | } 165 | 166 | // Deposit LP tokens to MasterChef for EGG allocation. 167 | function deposit(uint256 _pid, uint256 _amount) public { 168 | PoolInfo storage pool = poolInfo[_pid]; 169 | UserInfo storage user = userInfo[_pid][msg.sender]; 170 | updatePool(_pid); 171 | if (user.amount > 0) { 172 | uint256 pending = user.amount.mul(pool.accEggPerShare).div(1e12).sub(user.rewardDebt); 173 | if(pending > 0) { 174 | safeEggTransfer(msg.sender, pending); 175 | } 176 | } 177 | if(_amount > 0) { 178 | pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount); 179 | if(pool.depositFeeBP > 0){ 180 | uint256 depositFee = _amount.mul(pool.depositFeeBP).div(10000); 181 | pool.lpToken.safeTransfer(feeAddress, depositFee); 182 | user.amount = user.amount.add(_amount).sub(depositFee); 183 | }else{ 184 | user.amount = user.amount.add(_amount); 185 | } 186 | } 187 | user.rewardDebt = user.amount.mul(pool.accEggPerShare).div(1e12); 188 | emit Deposit(msg.sender, _pid, _amount); 189 | } 190 | 191 | // Withdraw LP tokens from MasterChef. 192 | function withdraw(uint256 _pid, uint256 _amount) public { 193 | PoolInfo storage pool = poolInfo[_pid]; 194 | UserInfo storage user = userInfo[_pid][msg.sender]; 195 | require(user.amount >= _amount, "withdraw: not good"); 196 | updatePool(_pid); 197 | uint256 pending = user.amount.mul(pool.accEggPerShare).div(1e12).sub(user.rewardDebt); 198 | if(pending > 0) { 199 | safeEggTransfer(msg.sender, pending); 200 | } 201 | if(_amount > 0) { 202 | user.amount = user.amount.sub(_amount); 203 | pool.lpToken.safeTransfer(address(msg.sender), _amount); 204 | } 205 | user.rewardDebt = user.amount.mul(pool.accEggPerShare).div(1e12); 206 | emit Withdraw(msg.sender, _pid, _amount); 207 | } 208 | 209 | // Withdraw without caring about rewards. EMERGENCY ONLY. 210 | function emergencyWithdraw(uint256 _pid) public { 211 | PoolInfo storage pool = poolInfo[_pid]; 212 | UserInfo storage user = userInfo[_pid][msg.sender]; 213 | uint256 amount = user.amount; 214 | user.amount = 0; 215 | user.rewardDebt = 0; 216 | pool.lpToken.safeTransfer(address(msg.sender), amount); 217 | emit EmergencyWithdraw(msg.sender, _pid, amount); 218 | } 219 | 220 | // Safe egg transfer function, just in case if rounding error causes pool to not have enough EGGs. 221 | function safeEggTransfer(address _to, uint256 _amount) internal { 222 | uint256 eggBal = egg.balanceOf(address(this)); 223 | if (_amount > eggBal) { 224 | egg.transfer(_to, eggBal); 225 | } else { 226 | egg.transfer(_to, _amount); 227 | } 228 | } 229 | 230 | // Update dev address by the previous dev. 231 | function dev(address _devaddr) public { 232 | require(msg.sender == devaddr, "dev: wut?"); 233 | devaddr = _devaddr; 234 | } 235 | 236 | function setFeeAddress(address _feeAddress) public{ 237 | require(msg.sender == feeAddress, "setFeeAddress: FORBIDDEN"); 238 | feeAddress = _feeAddress; 239 | } 240 | 241 | //Pancake has to add hidden dummy pools inorder to alter the emission, here we make it simple and transparent to all. 242 | function updateEmissionRate(uint256 _eggPerBlock) public onlyOwner { 243 | massUpdatePools(); 244 | eggPerBlock = _eggPerBlock; 245 | } 246 | } -------------------------------------------------------------------------------- /staking-contracts/GooseDefi_MasterChefV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./libs/IBEP20.sol"; 7 | import "./libs/SafeBEP20.sol"; 8 | import "@openzeppelin/contracts/access/Ownable.sol"; 9 | import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; 10 | 11 | import "./EggToken.sol"; 12 | 13 | // MasterChef is the master of Egg. He can make Egg and he is a fair guy. 14 | // 15 | // Note that it's ownable and the owner wields tremendous power. The ownership 16 | // will be transferred to a governance smart contract once EGG is sufficiently 17 | // distributed and the community can show to govern itself. 18 | // 19 | // Have fun reading it. Hopefully it's bug-free. God bless. 20 | contract MasterChefV2 is Ownable, ReentrancyGuard { 21 | using SafeMath for uint256; 22 | using SafeBEP20 for IBEP20; 23 | 24 | // Info of each user. 25 | struct UserInfo { 26 | uint256 amount; // How many LP tokens the user has provided. 27 | uint256 rewardDebt; // Reward debt. See explanation below. 28 | // 29 | // We do some fancy math here. Basically, any point in time, the amount of EGGs 30 | // entitled to a user but is pending to be distributed is: 31 | // 32 | // pending reward = (user.amount * pool.accEggPerShare) - user.rewardDebt 33 | // 34 | // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens: 35 | // 1. The pool's `accEggPerShare` (and `lastRewardBlock`) gets updated. 36 | // 2. User receives the pending reward sent to his/her address. 37 | // 3. User's `amount` gets updated. 38 | // 4. User's `rewardDebt` gets updated. 39 | } 40 | 41 | // Info of each pool. 42 | struct PoolInfo { 43 | IBEP20 lpToken; // Address of LP token contract. 44 | uint256 allocPoint; // How many allocation points assigned to this pool. EGGs to distribute per block. 45 | uint256 lastRewardBlock; // Last block number that EGGs distribution occurs. 46 | uint256 accEggPerShare; // Accumulated EGGs per share, times 1e12. See below. 47 | uint16 depositFeeBP; // Deposit fee in basis points 48 | } 49 | 50 | // The EGG TOKEN! 51 | EggToken public egg; 52 | // Dev address. 53 | address public devaddr; 54 | // EGG tokens created per block. 55 | uint256 public eggPerBlock; 56 | // Bonus muliplier for early egg makers. 57 | uint256 public constant BONUS_MULTIPLIER = 1; 58 | // Deposit Fee address 59 | address public feeAddress; 60 | 61 | // Info of each pool. 62 | PoolInfo[] public poolInfo; 63 | // Info of each user that stakes LP tokens. 64 | mapping(uint256 => mapping(address => UserInfo)) public userInfo; 65 | // Total allocation points. Must be the sum of all allocation points in all pools. 66 | uint256 public totalAllocPoint = 0; 67 | // The block number when EGG mining starts. 68 | uint256 public startBlock; 69 | 70 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 71 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 72 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 73 | event SetFeeAddress(address indexed user, address indexed newAddress); 74 | event SetDevAddress(address indexed user, address indexed newAddress); 75 | event UpdateEmissionRate(address indexed user, uint256 goosePerBlock); 76 | 77 | constructor( 78 | EggToken _egg, 79 | address _devaddr, 80 | address _feeAddress, 81 | uint256 _eggPerBlock, 82 | uint256 _startBlock 83 | ) public { 84 | egg = _egg; 85 | devaddr = _devaddr; 86 | feeAddress = _feeAddress; 87 | eggPerBlock = _eggPerBlock; 88 | startBlock = _startBlock; 89 | } 90 | 91 | function poolLength() external view returns (uint256) { 92 | return poolInfo.length; 93 | } 94 | 95 | mapping(IBEP20 => bool) public poolExistence; 96 | modifier nonDuplicated(IBEP20 _lpToken) { 97 | require(poolExistence[_lpToken] == false, "nonDuplicated: duplicated"); 98 | _; 99 | } 100 | 101 | // Add a new lp to the pool. Can only be called by the owner. 102 | function add(uint256 _allocPoint, IBEP20 _lpToken, uint16 _depositFeeBP, bool _withUpdate) public onlyOwner nonDuplicated(_lpToken) { 103 | require(_depositFeeBP <= 10000, "add: invalid deposit fee basis points"); 104 | if (_withUpdate) { 105 | massUpdatePools(); 106 | } 107 | uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock; 108 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 109 | poolExistence[_lpToken] = true; 110 | poolInfo.push(PoolInfo({ 111 | lpToken : _lpToken, 112 | allocPoint : _allocPoint, 113 | lastRewardBlock : lastRewardBlock, 114 | accEggPerShare : 0, 115 | depositFeeBP : _depositFeeBP 116 | })); 117 | } 118 | 119 | // Update the given pool's EGG allocation point and deposit fee. Can only be called by the owner. 120 | function set(uint256 _pid, uint256 _allocPoint, uint16 _depositFeeBP, bool _withUpdate) public onlyOwner { 121 | require(_depositFeeBP <= 10000, "set: invalid deposit fee basis points"); 122 | if (_withUpdate) { 123 | massUpdatePools(); 124 | } 125 | totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); 126 | poolInfo[_pid].allocPoint = _allocPoint; 127 | poolInfo[_pid].depositFeeBP = _depositFeeBP; 128 | } 129 | 130 | // Return reward multiplier over the given _from to _to block. 131 | function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) { 132 | return _to.sub(_from).mul(BONUS_MULTIPLIER); 133 | } 134 | 135 | // View function to see pending EGGs on frontend. 136 | function pendingEgg(uint256 _pid, address _user) external view returns (uint256) { 137 | PoolInfo storage pool = poolInfo[_pid]; 138 | UserInfo storage user = userInfo[_pid][_user]; 139 | uint256 accEggPerShare = pool.accEggPerShare; 140 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 141 | if (block.number > pool.lastRewardBlock && lpSupply != 0) { 142 | uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); 143 | uint256 eggReward = multiplier.mul(eggPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 144 | accEggPerShare = accEggPerShare.add(eggReward.mul(1e12).div(lpSupply)); 145 | } 146 | return user.amount.mul(accEggPerShare).div(1e12).sub(user.rewardDebt); 147 | } 148 | 149 | // Update reward variables for all pools. Be careful of gas spending! 150 | function massUpdatePools() public { 151 | uint256 length = poolInfo.length; 152 | for (uint256 pid = 0; pid < length; ++pid) { 153 | updatePool(pid); 154 | } 155 | } 156 | 157 | // Update reward variables of the given pool to be up-to-date. 158 | function updatePool(uint256 _pid) public { 159 | PoolInfo storage pool = poolInfo[_pid]; 160 | if (block.number <= pool.lastRewardBlock) { 161 | return; 162 | } 163 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 164 | if (lpSupply == 0 || pool.allocPoint == 0) { 165 | pool.lastRewardBlock = block.number; 166 | return; 167 | } 168 | uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); 169 | uint256 eggReward = multiplier.mul(eggPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 170 | egg.mint(devaddr, eggReward.div(10)); 171 | egg.mint(address(this), eggReward); 172 | pool.accEggPerShare = pool.accEggPerShare.add(eggReward.mul(1e12).div(lpSupply)); 173 | pool.lastRewardBlock = block.number; 174 | } 175 | 176 | // Deposit LP tokens to MasterChef for EGG allocation. 177 | function deposit(uint256 _pid, uint256 _amount) public nonReentrant { 178 | PoolInfo storage pool = poolInfo[_pid]; 179 | UserInfo storage user = userInfo[_pid][msg.sender]; 180 | updatePool(_pid); 181 | if (user.amount > 0) { 182 | uint256 pending = user.amount.mul(pool.accEggPerShare).div(1e12).sub(user.rewardDebt); 183 | if (pending > 0) { 184 | safeEggTransfer(msg.sender, pending); 185 | } 186 | } 187 | if (_amount > 0) { 188 | pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount); 189 | if (pool.depositFeeBP > 0) { 190 | uint256 depositFee = _amount.mul(pool.depositFeeBP).div(10000); 191 | pool.lpToken.safeTransfer(feeAddress, depositFee); 192 | user.amount = user.amount.add(_amount).sub(depositFee); 193 | } else { 194 | user.amount = user.amount.add(_amount); 195 | } 196 | } 197 | user.rewardDebt = user.amount.mul(pool.accEggPerShare).div(1e12); 198 | emit Deposit(msg.sender, _pid, _amount); 199 | } 200 | 201 | // Withdraw LP tokens from MasterChef. 202 | function withdraw(uint256 _pid, uint256 _amount) public nonReentrant { 203 | PoolInfo storage pool = poolInfo[_pid]; 204 | UserInfo storage user = userInfo[_pid][msg.sender]; 205 | require(user.amount >= _amount, "withdraw: not good"); 206 | updatePool(_pid); 207 | uint256 pending = user.amount.mul(pool.accEggPerShare).div(1e12).sub(user.rewardDebt); 208 | if (pending > 0) { 209 | safeEggTransfer(msg.sender, pending); 210 | } 211 | if (_amount > 0) { 212 | user.amount = user.amount.sub(_amount); 213 | pool.lpToken.safeTransfer(address(msg.sender), _amount); 214 | } 215 | user.rewardDebt = user.amount.mul(pool.accEggPerShare).div(1e12); 216 | emit Withdraw(msg.sender, _pid, _amount); 217 | } 218 | 219 | // Withdraw without caring about rewards. EMERGENCY ONLY. 220 | function emergencyWithdraw(uint256 _pid) public nonReentrant { 221 | PoolInfo storage pool = poolInfo[_pid]; 222 | UserInfo storage user = userInfo[_pid][msg.sender]; 223 | uint256 amount = user.amount; 224 | user.amount = 0; 225 | user.rewardDebt = 0; 226 | pool.lpToken.safeTransfer(address(msg.sender), amount); 227 | emit EmergencyWithdraw(msg.sender, _pid, amount); 228 | } 229 | 230 | // Safe egg transfer function, just in case if rounding error causes pool to not have enough EGGs. 231 | function safeEggTransfer(address _to, uint256 _amount) internal { 232 | uint256 eggBal = egg.balanceOf(address(this)); 233 | bool transferSuccess = false; 234 | if (_amount > eggBal) { 235 | transferSuccess = egg.transfer(_to, eggBal); 236 | } else { 237 | transferSuccess = egg.transfer(_to, _amount); 238 | } 239 | require(transferSuccess, "safeEggTransfer: transfer failed"); 240 | } 241 | 242 | // Update dev address by the previous dev. 243 | function dev(address _devaddr) public { 244 | require(msg.sender == devaddr, "dev: wut?"); 245 | devaddr = _devaddr; 246 | emit SetDevAddress(msg.sender, _devaddr); 247 | } 248 | 249 | function setFeeAddress(address _feeAddress) public { 250 | require(msg.sender == feeAddress, "setFeeAddress: FORBIDDEN"); 251 | feeAddress = _feeAddress; 252 | emit SetFeeAddress(msg.sender, _feeAddress); 253 | } 254 | 255 | //Pancake has to add hidden dummy pools inorder to alter the emission, here we make it simple and transparent to all. 256 | function updateEmissionRate(uint256 _eggPerBlock) public onlyOwner { 257 | massUpdatePools(); 258 | eggPerBlock = _eggPerBlock; 259 | emit UpdateEmissionRate(msg.sender, _eggPerBlock); 260 | } 261 | } -------------------------------------------------------------------------------- /staking-contracts/PancakeSwap_MasterChef.sol: -------------------------------------------------------------------------------- 1 | /** 2 | *Submitted for verification at BscScan.com on 2020-09-22 3 | */ 4 | 5 | pragma solidity 0.6.12; 6 | 7 | // MasterChef is the master of Cake. He can make Cake and he is a fair guy. 8 | // 9 | // Note that it's ownable and the owner wields tremendous power. The ownership 10 | // will be transferred to a governance smart contract once CAKE is sufficiently 11 | // distributed and the community can show to govern itself. 12 | // 13 | // Have fun reading it. Hopefully it's bug-free. God bless. 14 | contract MasterChef is Ownable { 15 | using SafeMath for uint256; 16 | using SafeBEP20 for IBEP20; 17 | 18 | // Info of each user. 19 | struct UserInfo { 20 | uint256 amount; // How many LP tokens the user has provided. 21 | uint256 rewardDebt; // Reward debt. See explanation below. 22 | // 23 | // We do some fancy math here. Basically, any point in time, the amount of CAKEs 24 | // entitled to a user but is pending to be distributed is: 25 | // 26 | // pending reward = (user.amount * pool.accCakePerShare) - user.rewardDebt 27 | // 28 | // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens: 29 | // 1. The pool's `accCakePerShare` (and `lastRewardBlock`) gets updated. 30 | // 2. User receives the pending reward sent to his/her address. 31 | // 3. User's `amount` gets updated. 32 | // 4. User's `rewardDebt` gets updated. 33 | } 34 | 35 | // Info of each pool. 36 | struct PoolInfo { 37 | IBEP20 lpToken; // Address of LP token contract. 38 | uint256 allocPoint; // How many allocation points assigned to this pool. CAKEs to distribute per block. 39 | uint256 lastRewardBlock; // Last block number that CAKEs distribution occurs. 40 | uint256 accCakePerShare; // Accumulated CAKEs per share, times 1e12. See below. 41 | } 42 | 43 | // The CAKE TOKEN! 44 | CakeToken public cake; 45 | // The SYRUP TOKEN! 46 | SyrupBar public syrup; 47 | // Dev address. 48 | address public devaddr; 49 | // CAKE tokens created per block. 50 | uint256 public cakePerBlock; 51 | // Bonus muliplier for early cake makers. 52 | uint256 public BONUS_MULTIPLIER = 1; 53 | // The migrator contract. It has a lot of power. Can only be set through governance (owner). 54 | IMigratorChef public migrator; 55 | 56 | // Info of each pool. 57 | PoolInfo[] public poolInfo; 58 | // Info of each user that stakes LP tokens. 59 | mapping (uint256 => mapping (address => UserInfo)) public userInfo; 60 | // Total allocation poitns. Must be the sum of all allocation points in all pools. 61 | uint256 public totalAllocPoint = 0; 62 | // The block number when CAKE mining starts. 63 | uint256 public startBlock; 64 | 65 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 66 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 67 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 68 | 69 | constructor( 70 | CakeToken _cake, 71 | SyrupBar _syrup, 72 | address _devaddr, 73 | uint256 _cakePerBlock, 74 | uint256 _startBlock 75 | ) public { 76 | cake = _cake; 77 | syrup = _syrup; 78 | devaddr = _devaddr; 79 | cakePerBlock = _cakePerBlock; 80 | startBlock = _startBlock; 81 | 82 | // staking pool 83 | poolInfo.push(PoolInfo({ 84 | lpToken: _cake, 85 | allocPoint: 1000, 86 | lastRewardBlock: startBlock, 87 | accCakePerShare: 0 88 | })); 89 | 90 | totalAllocPoint = 1000; 91 | 92 | } 93 | 94 | function updateMultiplier(uint256 multiplierNumber) public onlyOwner { 95 | BONUS_MULTIPLIER = multiplierNumber; 96 | } 97 | 98 | function poolLength() external view returns (uint256) { 99 | return poolInfo.length; 100 | } 101 | 102 | // Add a new lp to the pool. Can only be called by the owner. 103 | // XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do. 104 | function add(uint256 _allocPoint, IBEP20 _lpToken, bool _withUpdate) public onlyOwner { 105 | if (_withUpdate) { 106 | massUpdatePools(); 107 | } 108 | uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock; 109 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 110 | poolInfo.push(PoolInfo({ 111 | lpToken: _lpToken, 112 | allocPoint: _allocPoint, 113 | lastRewardBlock: lastRewardBlock, 114 | accCakePerShare: 0 115 | })); 116 | updateStakingPool(); 117 | } 118 | 119 | // Update the given pool's CAKE allocation point. Can only be called by the owner. 120 | function set(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner { 121 | if (_withUpdate) { 122 | massUpdatePools(); 123 | } 124 | totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); 125 | uint256 prevAllocPoint = poolInfo[_pid].allocPoint; 126 | poolInfo[_pid].allocPoint = _allocPoint; 127 | if (prevAllocPoint != _allocPoint) { 128 | updateStakingPool(); 129 | } 130 | } 131 | 132 | function updateStakingPool() internal { 133 | uint256 length = poolInfo.length; 134 | uint256 points = 0; 135 | for (uint256 pid = 1; pid < length; ++pid) { 136 | points = points.add(poolInfo[pid].allocPoint); 137 | } 138 | if (points != 0) { 139 | points = points.div(3); 140 | totalAllocPoint = totalAllocPoint.sub(poolInfo[0].allocPoint).add(points); 141 | poolInfo[0].allocPoint = points; 142 | } 143 | } 144 | 145 | // Set the migrator contract. Can only be called by the owner. 146 | function setMigrator(IMigratorChef _migrator) public onlyOwner { 147 | migrator = _migrator; 148 | } 149 | 150 | // Migrate lp token to another lp contract. Can be called by anyone. We trust that migrator contract is good. 151 | function migrate(uint256 _pid) public { 152 | require(address(migrator) != address(0), "migrate: no migrator"); 153 | PoolInfo storage pool = poolInfo[_pid]; 154 | IBEP20 lpToken = pool.lpToken; 155 | uint256 bal = lpToken.balanceOf(address(this)); 156 | lpToken.safeApprove(address(migrator), bal); 157 | IBEP20 newLpToken = migrator.migrate(lpToken); 158 | require(bal == newLpToken.balanceOf(address(this)), "migrate: bad"); 159 | pool.lpToken = newLpToken; 160 | } 161 | 162 | // Return reward multiplier over the given _from to _to block. 163 | function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) { 164 | return _to.sub(_from).mul(BONUS_MULTIPLIER); 165 | } 166 | 167 | // View function to see pending CAKEs on frontend. 168 | function pendingCake(uint256 _pid, address _user) external view returns (uint256) { 169 | PoolInfo storage pool = poolInfo[_pid]; 170 | UserInfo storage user = userInfo[_pid][_user]; 171 | uint256 accCakePerShare = pool.accCakePerShare; 172 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 173 | if (block.number > pool.lastRewardBlock && lpSupply != 0) { 174 | uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); 175 | uint256 cakeReward = multiplier.mul(cakePerBlock).mul(pool.allocPoint).div(totalAllocPoint); 176 | accCakePerShare = accCakePerShare.add(cakeReward.mul(1e12).div(lpSupply)); 177 | } 178 | return user.amount.mul(accCakePerShare).div(1e12).sub(user.rewardDebt); 179 | } 180 | 181 | // Update reward variables for all pools. Be careful of gas spending! 182 | function massUpdatePools() public { 183 | uint256 length = poolInfo.length; 184 | for (uint256 pid = 0; pid < length; ++pid) { 185 | updatePool(pid); 186 | } 187 | } 188 | 189 | 190 | // Update reward variables of the given pool to be up-to-date. 191 | function updatePool(uint256 _pid) public { 192 | PoolInfo storage pool = poolInfo[_pid]; 193 | if (block.number <= pool.lastRewardBlock) { 194 | return; 195 | } 196 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 197 | if (lpSupply == 0) { 198 | pool.lastRewardBlock = block.number; 199 | return; 200 | } 201 | uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); 202 | uint256 cakeReward = multiplier.mul(cakePerBlock).mul(pool.allocPoint).div(totalAllocPoint); 203 | cake.mint(devaddr, cakeReward.div(10)); 204 | cake.mint(address(syrup), cakeReward); 205 | pool.accCakePerShare = pool.accCakePerShare.add(cakeReward.mul(1e12).div(lpSupply)); 206 | pool.lastRewardBlock = block.number; 207 | } 208 | 209 | // Deposit LP tokens to MasterChef for CAKE allocation. 210 | function deposit(uint256 _pid, uint256 _amount) public { 211 | 212 | require (_pid != 0, 'deposit CAKE by staking'); 213 | 214 | PoolInfo storage pool = poolInfo[_pid]; 215 | UserInfo storage user = userInfo[_pid][msg.sender]; 216 | updatePool(_pid); 217 | if (user.amount > 0) { 218 | uint256 pending = user.amount.mul(pool.accCakePerShare).div(1e12).sub(user.rewardDebt); 219 | if(pending > 0) { 220 | safeCakeTransfer(msg.sender, pending); 221 | } 222 | } 223 | if (_amount > 0) { 224 | pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount); 225 | user.amount = user.amount.add(_amount); 226 | } 227 | user.rewardDebt = user.amount.mul(pool.accCakePerShare).div(1e12); 228 | emit Deposit(msg.sender, _pid, _amount); 229 | } 230 | 231 | // Withdraw LP tokens from MasterChef. 232 | function withdraw(uint256 _pid, uint256 _amount) public { 233 | 234 | require (_pid != 0, 'withdraw CAKE by unstaking'); 235 | 236 | PoolInfo storage pool = poolInfo[_pid]; 237 | UserInfo storage user = userInfo[_pid][msg.sender]; 238 | require(user.amount >= _amount, "withdraw: not good"); 239 | updatePool(_pid); 240 | uint256 pending = user.amount.mul(pool.accCakePerShare).div(1e12).sub(user.rewardDebt); 241 | if(pending > 0) { 242 | safeCakeTransfer(msg.sender, pending); 243 | } 244 | if(_amount > 0) { 245 | user.amount = user.amount.sub(_amount); 246 | pool.lpToken.safeTransfer(address(msg.sender), _amount); 247 | } 248 | user.rewardDebt = user.amount.mul(pool.accCakePerShare).div(1e12); 249 | emit Withdraw(msg.sender, _pid, _amount); 250 | } 251 | 252 | // Stake CAKE tokens to MasterChef 253 | function enterStaking(uint256 _amount) public { 254 | PoolInfo storage pool = poolInfo[0]; 255 | UserInfo storage user = userInfo[0][msg.sender]; 256 | updatePool(0); 257 | if (user.amount > 0) { 258 | uint256 pending = user.amount.mul(pool.accCakePerShare).div(1e12).sub(user.rewardDebt); 259 | if(pending > 0) { 260 | safeCakeTransfer(msg.sender, pending); 261 | } 262 | } 263 | if(_amount > 0) { 264 | pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount); 265 | user.amount = user.amount.add(_amount); 266 | } 267 | user.rewardDebt = user.amount.mul(pool.accCakePerShare).div(1e12); 268 | 269 | syrup.mint(msg.sender, _amount); 270 | emit Deposit(msg.sender, 0, _amount); 271 | } 272 | 273 | // Withdraw CAKE tokens from STAKING. 274 | function leaveStaking(uint256 _amount) public { 275 | PoolInfo storage pool = poolInfo[0]; 276 | UserInfo storage user = userInfo[0][msg.sender]; 277 | require(user.amount >= _amount, "withdraw: not good"); 278 | updatePool(0); 279 | uint256 pending = user.amount.mul(pool.accCakePerShare).div(1e12).sub(user.rewardDebt); 280 | if(pending > 0) { 281 | safeCakeTransfer(msg.sender, pending); 282 | } 283 | if(_amount > 0) { 284 | user.amount = user.amount.sub(_amount); 285 | pool.lpToken.safeTransfer(address(msg.sender), _amount); 286 | } 287 | user.rewardDebt = user.amount.mul(pool.accCakePerShare).div(1e12); 288 | 289 | syrup.burn(msg.sender, _amount); 290 | emit Withdraw(msg.sender, 0, _amount); 291 | } 292 | 293 | // Withdraw without caring about rewards. EMERGENCY ONLY. 294 | function emergencyWithdraw(uint256 _pid) public { 295 | PoolInfo storage pool = poolInfo[_pid]; 296 | UserInfo storage user = userInfo[_pid][msg.sender]; 297 | pool.lpToken.safeTransfer(address(msg.sender), user.amount); 298 | emit EmergencyWithdraw(msg.sender, _pid, user.amount); 299 | user.amount = 0; 300 | user.rewardDebt = 0; 301 | } 302 | 303 | // Safe cake transfer function, just in case if rounding error causes pool to not have enough CAKEs. 304 | function safeCakeTransfer(address _to, uint256 _amount) internal { 305 | syrup.safeCakeTransfer(_to, _amount); 306 | } 307 | 308 | // Update dev address by the previous dev. 309 | function dev(address _devaddr) public { 310 | require(msg.sender == devaddr, "dev: wut?"); 311 | devaddr = _devaddr; 312 | } 313 | } -------------------------------------------------------------------------------- /supply-curve.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THORWallet/smartcontract/6dea82087e2ff229ffc4ab1c5359fa6337b2adf6/supply-curve.ods -------------------------------------------------------------------------------- /test/batchtransfer-test.js: -------------------------------------------------------------------------------- 1 | const hre = require("hardhat"); 2 | const {expect} = require("chai"); 3 | const {expectRevert, BN} = require("@openzeppelin/test-helpers"); 4 | 5 | 6 | let token = undefined; 7 | let batchTransfer = undefined; 8 | let accounts = undefined; 9 | const INIT_SUPPLY = new BN("750000000000000000000000000"); 10 | const ETH1 = new BN("1000000000000000000"); 11 | const ETH2 = new BN("2000000000000000000"); 12 | const ETH5 = new BN("5000000000000000000"); 13 | 14 | describe("BatchTransfer Test", function () { 15 | 16 | beforeEach("Setup TGT and BatchTransfer contracts", async function () { 17 | this.accounts = await hre.ethers.getSigners(); 18 | 19 | const TGT = await ethers.getContractFactory("TGT"); 20 | this.token = await TGT.deploy(); 21 | await this.token.deployed(); 22 | 23 | const BATCHTRANSFER = await ethers.getContractFactory("BatchTransfer"); 24 | this.batchTransfer = await BATCHTRANSFER.deploy(); 25 | await this.batchTransfer.deployed(); 26 | 27 | //set token live 28 | let acc = new Array(this.accounts[0].address); 29 | let amount = new Array(INIT_SUPPLY.toString()); 30 | await this.token.mint(acc, amount); 31 | await this.token.mintFinish(); 32 | }); 33 | 34 | it('Check token', async function () { 35 | expect(await this.token.name()).to.equal("THORWallet Governance Token"); 36 | }); 37 | 38 | it('Send to one address', async function () { 39 | console.log(this.accounts[1].address); 40 | await this.token.transfer(this.batchTransfer.address, ETH1.toString()); 41 | await this.batchTransfer.batchTransferDirect(this.token.address, [this.accounts[1].address], ETH1.toString()); 42 | expect(await this.token.balanceOf(this.accounts[1].address)).to.equal(ETH1.toString()); 43 | }); 44 | 45 | it('Send to 2 addresses', async function () { 46 | await this.token.transfer(this.batchTransfer.address, ETH2.toString()); 47 | await this.batchTransfer.batchTransferDirect(this.token.address, [this.accounts[1].address, this.accounts[2].address], ETH1.toString()); 48 | expect(await this.token.balanceOf(this.accounts[1].address)).to.equal(ETH1.toString()); 49 | expect(await this.token.balanceOf(this.accounts[2].address)).to.equal(ETH1.toString()); 50 | }); 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /test/distributor-test.js: -------------------------------------------------------------------------------- 1 | const hre = require("hardhat"); 2 | const {expect} = require("chai"); 3 | const {expectRevert, BN} = require("@openzeppelin/test-helpers"); 4 | const {MerkleTree} = require("merkletreejs"); 5 | 6 | let initialSupply = new BN("750000000000000000000000000"); 7 | 8 | const rewards = [ 9 | {address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", amount: 100}, 10 | {address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", amount: 101}, 11 | {address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", amount: 102}, 12 | {address: "0x90F79bf6EB2c4f870365E785982E1f101E93b906", amount: 103}, 13 | ] 14 | 15 | const hash = (s) => { 16 | return Buffer.from(hre.ethers.utils.keccak256(s).slice('0x'.length), 'hex') 17 | } 18 | 19 | const toTGTDecimals = (n) => { 20 | return new BN(n).mul(new BN('10').pow(new BN('18'))) 21 | } 22 | 23 | 24 | describe("Distributor", function () { 25 | 26 | beforeEach("Setup", async function () { 27 | this.accounts = await hre.ethers.getSigners(); 28 | const [initialHolder] = this.accounts; 29 | this.initialHolder = initialHolder; 30 | 31 | 32 | const TGT = await hre.ethers.getContractFactory("TGT"); 33 | this.token = await TGT.deploy(); 34 | await this.token.deployed(); 35 | 36 | const Distributor = await hre.ethers.getContractFactory("Distributor"); 37 | this.distributor = await Distributor.deploy(this.token.address, this.initialHolder.address, "0x0000000000000000000000000000000000000000000000000000000000000000" ); 38 | await this.distributor.deployed(); 39 | 40 | let amount = new Array(initialSupply.toString()); 41 | let acc = new Array(initialHolder.address); 42 | await this.token.mint(acc, amount); 43 | await this.token.mintFinish(); 44 | await this.token.connect(initialHolder).approve(this.distributor.address, initialSupply.toString()) 45 | }); 46 | 47 | it('Should be able to update merkle root', async function () { 48 | const merkleLeaves = rewards.map((data) => hash( 49 | hre.ethers.utils.solidityPack(["address", "uint32"], [data.address, data.amount]), 50 | )); 51 | const merkleTree = new MerkleTree(merkleLeaves, hash, {sortPairs: true}); 52 | const root = merkleTree.getRoot().toString('hex') 53 | await this.distributor.updateMerkleRoot("0x" + root) 54 | }); 55 | 56 | it('Should be able to verify claimable amount', async function () { 57 | const [,secondAccount] = this.accounts; 58 | const merkleLeaves = rewards.map((data) => hash( 59 | hre.ethers.utils.solidityPack(["address", "uint32"], [data.address, data.amount]), 60 | )); 61 | const merkleTree = new MerkleTree(merkleLeaves, hash, {sortPairs: true}); 62 | const root = merkleTree.getRoot().toString('hex') 63 | 64 | const {address, amount} = rewards[1] 65 | const proof = merkleTree.getProof(hash( 66 | hre.ethers.utils.solidityPack(["address", "uint32"], [address, amount]), 67 | )).map((p) => '0x' + p.data.toString('hex')); 68 | await this.distributor.updateMerkleRoot("0x" + root) 69 | 70 | const claimable = await this.distributor.connect(secondAccount).claimableAmount(address, amount, proof) 71 | expect(claimable).to.equal(amount); 72 | }); 73 | 74 | it('Should be able to claim', async function () { 75 | const [,secondAccount] = this.accounts; 76 | const merkleLeaves = rewards.map((data) => hash( 77 | hre.ethers.utils.solidityPack(["address", "uint32"], [data.address, data.amount]), 78 | )); 79 | const merkleTree = new MerkleTree(merkleLeaves, hash, {sortPairs: true}); 80 | const root = merkleTree.getRoot().toString('hex') 81 | 82 | const {address, amount} = rewards[1] 83 | const proof = merkleTree.getProof(hash( 84 | hre.ethers.utils.solidityPack(["address", "uint32"], [address, amount]), 85 | )).map((p) => '0x' + p.data.toString('hex')); 86 | await this.distributor.updateMerkleRoot("0x" + root) 87 | 88 | await this.distributor.connect(secondAccount).claim(address, amount, proof) 89 | expect(await this.distributor.amountClaimed(secondAccount.address)).to.equal(amount); 90 | const balance = await this.token.balanceOf(secondAccount.address) 91 | expect(balance.toString()).to.equal(toTGTDecimals(amount).toString()); 92 | }); 93 | 94 | it('Should not be able to claim twice', async function () { 95 | const [,secondAccount] = this.accounts; 96 | const merkleLeaves = rewards.map((data) => hash( 97 | hre.ethers.utils.solidityPack(["address", "uint32"], [data.address, data.amount]), 98 | )); 99 | const merkleTree = new MerkleTree(merkleLeaves, hash, {sortPairs: true}); 100 | const root = merkleTree.getRoot().toString('hex') 101 | 102 | const {address, amount} = rewards[1] 103 | const proof = merkleTree.getProof(hash( 104 | hre.ethers.utils.solidityPack(["address", "uint32"], [address, amount]), 105 | )).map((p) => '0x' + p.data.toString('hex')); 106 | await this.distributor.updateMerkleRoot("0x" + root) 107 | 108 | 109 | await this.distributor.connect(secondAccount).claim(address, amount, proof) 110 | await expectRevert.unspecified(this.distributor.connect(secondAccount).claim(address, amount, proof), "Distributor: no more TGT to claim."); 111 | expect(await this.distributor.amountClaimed(secondAccount.address)).to.equal(amount); 112 | const balance = await this.token.balanceOf(secondAccount.address) 113 | expect(balance.toString()).to.equal(toTGTDecimals(amount).toString()); 114 | }); 115 | 116 | it('Should be able to claim again after merkle root update', async function () { 117 | const [,,thirdAccount] = this.accounts; 118 | const merkleLeaves = rewards.map((data) => hash( 119 | hre.ethers.utils.solidityPack(["address", "uint32"], [data.address, data.amount]), 120 | )); 121 | const merkleTree = new MerkleTree(merkleLeaves, hash, {sortPairs: true}); 122 | const root = merkleTree.getRoot().toString('hex') 123 | 124 | const {address, amount} = rewards[2] 125 | const proof = merkleTree.getProof(hash( 126 | hre.ethers.utils.solidityPack(["address", "uint32"], [address, amount]), 127 | )).map((p) => '0x' + p.data.toString('hex')); 128 | await this.distributor.updateMerkleRoot("0x" + root) 129 | 130 | 131 | await this.distributor.connect(thirdAccount).claim(address, amount, proof) 132 | expect(await this.distributor.amountClaimed(thirdAccount.address)).to.equal(amount); 133 | const balance = await this.token.balanceOf(thirdAccount.address) 134 | expect(balance.toString()).to.equal(toTGTDecimals(amount).toString()); 135 | 136 | // increase reward for third address 137 | const modifiedRewards = rewards.map(({address: addr, amount:amnt}) => addr === address? {address: addr, amount: amnt+100}: {address: addr, amount: amnt}) 138 | const {amount: amountUpdated} = modifiedRewards[2] 139 | const merkleLeavesUpdated = modifiedRewards.map((data) => hash( 140 | hre.ethers.utils.solidityPack(["address", "uint32"], [data.address, data.amount]), 141 | )); 142 | const merkleTreeUpdated = new MerkleTree(merkleLeavesUpdated, hash, {sortPairs: true}); 143 | const rootUpdated = merkleTreeUpdated.getRoot().toString('hex') 144 | await this.distributor.updateMerkleRoot("0x" + rootUpdated) 145 | const proofUpdated = merkleTreeUpdated.getProof(hash( 146 | hre.ethers.utils.solidityPack(["address", "uint32"], [address, amountUpdated]), 147 | )).map((p) => '0x' + p.data.toString('hex')); 148 | 149 | await this.distributor.connect(thirdAccount).claim(address, amountUpdated, proofUpdated) 150 | expect(await this.distributor.amountClaimed(thirdAccount.address)).to.equal(amountUpdated); 151 | const balanceUpdated = await this.token.balanceOf(thirdAccount.address) 152 | expect(balanceUpdated.toString()).to.equal(toTGTDecimals(amountUpdated).toString()); 153 | }); 154 | 155 | it('Should not be able to claim higher amount than available', async function () { 156 | const [,secondAccount] = this.accounts; 157 | const merkleLeaves = rewards.map((data) => hash( 158 | hre.ethers.utils.solidityPack(["address", "uint32"], [data.address, data.amount]), 159 | )); 160 | const merkleTree = new MerkleTree(merkleLeaves, hash, {sortPairs: true}); 161 | const root = merkleTree.getRoot().toString('hex') 162 | 163 | const {address, amount} = rewards[1] 164 | const proof = merkleTree.getProof(hash( 165 | hre.ethers.utils.solidityPack(["address", "uint32"], [address, amount]), 166 | )).map((p) => '0x' + p.data.toString('hex')); 167 | await this.distributor.updateMerkleRoot("0x" + root) 168 | 169 | await expectRevert.unspecified(this.distributor.connect(secondAccount).claim(address, amount+1, proof), "Distributor: Invalid proof."); 170 | expect(await this.distributor.amountClaimed(secondAccount.address)).to.equal(0); 171 | const balance = await this.token.balanceOf(secondAccount.address) 172 | expect(balance).to.equal(0); 173 | }); 174 | }); 175 | -------------------------------------------------------------------------------- /test/faucet-test.js: -------------------------------------------------------------------------------- 1 | const hre = require("hardhat"); 2 | const {expect} = require("chai"); 3 | const {expectRevert, BN} = require("@openzeppelin/test-helpers"); 4 | 5 | 6 | let token = undefined; 7 | let faucet = undefined; 8 | let accounts = undefined; 9 | const INIT_SUPPLY = new BN("750000000000000000000000000"); 10 | const ETH1 = new BN("1000000000000000000"); 11 | const ETH2 = new BN("2000000000000000000"); 12 | 13 | describe("Faucet Test", function () { 14 | 15 | beforeEach("Setup TGT and Faucet contracts", async function () { 16 | this.accounts = await hre.ethers.getSigners(); 17 | 18 | const TGT = await ethers.getContractFactory("TGT"); 19 | this.token = await TGT.deploy(); 20 | await this.token.deployed(); 21 | 22 | const FAUCET = await ethers.getContractFactory("Faucet"); 23 | this.faucet = await FAUCET.deploy(this.token.address, this.accounts[0].address, ETH1.toString()); 24 | await this.faucet.deployed(); 25 | 26 | //set token live 27 | let acc = new Array(this.accounts[0].address); 28 | let amount = new Array(INIT_SUPPLY.toString()); 29 | await this.token.mint(acc, amount); 30 | await this.token.mintFinish(); 31 | }); 32 | 33 | it('Check token', async function () { 34 | expect(await this.token.name()).to.equal("THORWallet Governance Token"); 35 | }); 36 | 37 | it('Faucet spender not approved', async function () { 38 | await expectRevert.unspecified(this.faucet.connect(this.accounts[1]).claim(), "ERC20: transfer amount exceeds allowance"); 39 | }); 40 | 41 | it('Approve faucet spender', async function () { 42 | expect(await this.token.approve(this.faucet.address, ETH2.toString())) 43 | .to.emit(this.token, 'Approval').withArgs(this.accounts[0].address, this.faucet.address, ETH2.toString()); 44 | }); 45 | 46 | it('Faucet spender approved', async function () { 47 | await this.token.approve(this.faucet.address, ETH2.toString()); 48 | expect(await this.faucet.connect(this.accounts[1]).claim()) 49 | .to.emit(this.faucet, 'Claim').withArgs(this.accounts[1].address, ETH1.toString()); 50 | }); 51 | 52 | it('Faucet spender approved twice does not work', async function () { 53 | await this.token.approve(this.faucet.address, ETH2.toString()); 54 | await this.faucet.connect(this.accounts[1]).claim(); 55 | await expectRevert.unspecified(this.faucet.connect(this.accounts[1]).claim(), "Same address cannot claim twice"); 56 | }); 57 | 58 | it('Other spender approved', async function () { 59 | await this.token.approve(this.faucet.address, ETH2.toString()); 60 | await this.faucet.connect(this.accounts[1]).claim(); 61 | expect(await this.faucet.connect(this.accounts[2]).claim()) 62 | .to.emit(this.faucet, 'Claim').withArgs(this.accounts[2].address, ETH1.toString()); 63 | }); 64 | 65 | it('Out of funds', async function () { 66 | await this.token.approve(this.faucet.address, ETH2.toString()); 67 | await this.faucet.connect(this.accounts[1]).claim(); 68 | await this.faucet.connect(this.accounts[2]).claim(); 69 | await expectRevert.unspecified(this.faucet.connect(this.accounts[3]).claim(), "ERC20: transfer amount exceeds allowance"); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /test/utils/ERC20.allowance.js: -------------------------------------------------------------------------------- 1 | const {BN, constants, expectRevert} = require('@openzeppelin/test-helpers'); 2 | const {expect} = require('chai'); 3 | const {ZERO_ADDRESS} = constants; 4 | 5 | function allowanceERC20(errorPrefix, initialSupply, initialHolder, recipient, anotherAccount, token) { 6 | 7 | const spender = recipient.address; 8 | describe('decrease allowance', function () { 9 | 10 | describe('when the sender has enough balance', function () { 11 | const amount = initialSupply; 12 | 13 | shouldDecreaseApproval(amount, token); 14 | }); 15 | 16 | describe('when the sender does not have enough balance', function () { 17 | const amount = initialSupply.addn(1); 18 | 19 | shouldDecreaseApproval(amount, token); 20 | }); 21 | 22 | function shouldDecreaseApproval(initialSupply, token) { 23 | amount = initialSupply.toString() 24 | describe('when there was no approved amount before', function () { 25 | it('reverts', async function () { 26 | await expectRevert.unspecified(token.connect(initialHolder).decreaseAllowance( 27 | spender, amount), 'ERC20: decreased allowance below zero', 28 | ); 29 | }); 30 | }); 31 | 32 | describe('when the spender had an approved amount', function () { 33 | const approvedAmount = amount; 34 | 35 | beforeEach(async function () { 36 | await token.connect(initialHolder).approve(spender, approvedAmount); 37 | }); 38 | 39 | it('emits an approval event', async function () { 40 | expect(await token.connect(initialHolder).decreaseAllowance(spender, approvedAmount)).to.emit(token, 'Approval').withArgs(initialHolder.address, spender, '0'); 41 | }); 42 | 43 | it('decreases the spender allowance subtracting the requested amount', async function () { 44 | await token.connect(initialHolder).decreaseAllowance(spender, initialSupply.subn(1).toString()); 45 | 46 | expect((await token.allowance(initialHolder.address, spender)).toString()).to.be.bignumber.equal('1'); 47 | }); 48 | 49 | it('sets the allowance to zero when all allowance is removed', async function () { 50 | await token.connect(initialHolder).decreaseAllowance(spender, approvedAmount); 51 | expect((await token.allowance(initialHolder.address, spender)).toString()).to.be.bignumber.equal('0'); 52 | }); 53 | 54 | it('reverts when more than the full allowance is removed', async function () { 55 | await expectRevert.unspecified( 56 | token.connect(initialHolder).decreaseAllowance(spender, initialSupply.addn(1).toString()), 57 | 'ERC20: decreased allowance below zero', 58 | ); 59 | }); 60 | }); 61 | } 62 | 63 | describe('when the spender is the zero address', function () { 64 | const amount = initialSupply.toString(); 65 | const spender = ZERO_ADDRESS; 66 | 67 | it('reverts', async function () { 68 | await expectRevert.unspecified(token.connect(initialHolder).decreaseAllowance( 69 | spender, amount), 'ERC20: decreased allowance below zero', 70 | ); 71 | }); 72 | 73 | it('set allowance to 0', async function () { 74 | await token.connect(initialHolder).approve(recipient.address, '0'); 75 | }); 76 | }); 77 | }); 78 | 79 | describe('increase allowance', function () { 80 | const amount = initialSupply; 81 | 82 | describe('when the spender is not the zero address', function () { 83 | const spender = recipient.address; 84 | 85 | describe('when the sender has enough balance', function () { 86 | it('emits an approval event', async function () { 87 | expect(await token.connect(initialHolder).increaseAllowance(spender, amount.toString())) 88 | .to.emit(token, 'Approval').withArgs(initialHolder.address, spender, amount.toString()); 89 | }); 90 | 91 | 92 | 93 | describe('when there was no approved amount before', function () { 94 | beforeEach('set allowance to 0', async function () { 95 | await token.connect(initialHolder).approve(recipient.address, '0'); 96 | }); 97 | 98 | it('approves the requested amount', async function () { 99 | await token.connect(initialHolder).increaseAllowance(spender, amount.toString()); 100 | 101 | expect((await token.allowance(initialHolder.address, spender)).toString()).to.be.bignumber.equal(amount); 102 | }); 103 | }); 104 | 105 | describe('when the spender had an approved amount', function () { 106 | beforeEach(async function () { 107 | await token.connect(initialHolder).approve(spender, new BN(1).toString()); 108 | }); 109 | 110 | it('increases the spender allowance adding the requested amount', async function () { 111 | await token.connect(initialHolder).increaseAllowance(spender, amount.toString()); 112 | 113 | expect((await token.allowance(initialHolder.address, spender)).toString()).to.be.bignumber.equal(amount.addn(1)); 114 | }); 115 | }); 116 | }); 117 | 118 | describe('when the sender does not have enough balance', function () { 119 | const amount = initialSupply.addn(1); 120 | 121 | beforeEach('set allowance to 0', async function () { 122 | await token.connect(initialHolder).approve(recipient.address, '0'); 123 | }); 124 | 125 | it('emits an approval event', async function () { 126 | expect(await token.connect(initialHolder).increaseAllowance(spender, amount.toString())) 127 | .to.emit(token, 'Approval').withArgs(initialHolder.address, spender, amount.toString()) 128 | }); 129 | 130 | describe('when there was no approved amount before', function () { 131 | beforeEach('set allowance to 0', async function () { 132 | await token.connect(initialHolder).approve(recipient.address, '0'); 133 | }); 134 | 135 | it('approves the requested amount', async function () { 136 | await token.connect(initialHolder).increaseAllowance(spender, amount.toString()); 137 | 138 | expect((await token.allowance(initialHolder.address, spender)).toString()).to.be.bignumber.equal(amount); 139 | }); 140 | }); 141 | 142 | describe('when the spender had an approved amount', function () { 143 | beforeEach(async function () { 144 | await token.connect(initialHolder).approve(spender, new BN(1).toString()); 145 | }); 146 | 147 | it('increases the spender allowance adding the requested amount', async function () { 148 | await token.connect(initialHolder).increaseAllowance(spender, amount.toString()); 149 | 150 | expect((await token.allowance(initialHolder.address, spender)).toString()).to.be.bignumber.equal(amount.addn(1)); 151 | }); 152 | }); 153 | }); 154 | }); 155 | 156 | describe('when the spender is the zero address', function () { 157 | const spender = ZERO_ADDRESS; 158 | 159 | it('reverts', async function () { 160 | await expectRevert.unspecified( 161 | token.connect(initialHolder).increaseAllowance(spender, amount.toString()), 'ERC20: approve to the zero address', 162 | ); 163 | }); 164 | }); 165 | }); 166 | } 167 | 168 | module.exports = { 169 | allowanceERC20 170 | }; 171 | -------------------------------------------------------------------------------- /test/utils/ERC20.behavior.js: -------------------------------------------------------------------------------- 1 | const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); 2 | const { expect } = require('chai'); 3 | const { ZERO_ADDRESS } = constants; 4 | 5 | function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recipient, anotherAccount, token) { 6 | describe('total supply', function () { 7 | it('returns the total amount of tokens', async function () { 8 | expect((await token.totalSupply()).toString()).to.be.bignumber.equal(initialSupply); 9 | }); 10 | }); 11 | 12 | describe('balanceOf', function () { 13 | describe('when the requested account has no tokens', function () { 14 | it('returns zero', async function () { 15 | expect((await token.balanceOf(anotherAccount.address)).toString()).to.be.bignumber.equal('0'); 16 | }); 17 | }); 18 | 19 | describe('when the requested account has some tokens', function () { 20 | it('returns the total amount of tokens', async function () { 21 | expect((await token.balanceOf(initialHolder.address)).toString()).to.be.bignumber.equal(initialSupply); 22 | }); 23 | }); 24 | }); 25 | 26 | describe('transfer', function () { 27 | shouldBehaveLikeERC20Transfer(errorPrefix, initialHolder, recipient, initialSupply, 28 | function (from, to, value) { 29 | return token.transfer(to, value, { from }); 30 | }, token, 31 | ); 32 | }); 33 | 34 | describe('transfer from', function () { 35 | const spender = recipient.address; 36 | 37 | describe('when the token owner is not the zero address', function () { 38 | const tokenOwner = initialHolder.address; 39 | 40 | describe('when the recipient is not the zero address', function () { 41 | const to = anotherAccount.address; 42 | 43 | describe('when the spender has enough approved balance', function () { 44 | beforeEach(async function () { 45 | await token.connect(initialHolder).approve(spender, initialSupply.toString()); 46 | }); 47 | 48 | describe('when the token owner has enough balance', function () { 49 | const amount = initialSupply.toString(); 50 | 51 | it('transfers the requested amount', async function () { 52 | expect(await token.connect(recipient).transferFrom(tokenOwner, to, amount)) 53 | .to.emit(token, 'Transfer') 54 | .withArgs(tokenOwner, to, amount) 55 | .to.emit(token, 'Approval') 56 | .withArgs(tokenOwner, spender, await token.allowance(tokenOwner, spender)); 57 | expect((await token.balanceOf(tokenOwner)).toString()).to.be.bignumber.equal('0'); 58 | expect((await token.balanceOf(to)).toString()).to.be.bignumber.equal(amount); 59 | await token.connect(anotherAccount).transfer(tokenOwner, amount); 60 | }); 61 | 62 | it('decreases the spender allowance', async function () { 63 | await token.connect(recipient).transferFrom(tokenOwner, to, amount); 64 | expect((await token.allowance(tokenOwner, spender)).toString()).to.be.bignumber.equal('0'); 65 | }); 66 | }); 67 | 68 | describe('when the token owner does not have enough balance', function () { 69 | const amount = initialSupply.addn(1); 70 | 71 | it('reverts', async function () { 72 | await expectRevert.unspecified(token.connect(recipient).transferFrom( 73 | tokenOwner, to, amount.toString()), `${errorPrefix}: transfer amount exceeds balance`, 74 | ); 75 | }); 76 | }); 77 | }); 78 | 79 | describe('when the spender does not have enough approved balance', function () { 80 | beforeEach(async function () { 81 | await token.connect(initialHolder).approve(spender, initialSupply.subn(1).toString()); 82 | }); 83 | 84 | describe('when the token owner has enough balance', function () { 85 | const amount = initialSupply; 86 | 87 | it('reverts', async function () { 88 | await expectRevert.unspecified(token.connect(recipient).transferFrom( 89 | tokenOwner, to, amount.toString()), `${errorPrefix}: transfer amount exceeds allowance`, 90 | ); 91 | }); 92 | }); 93 | 94 | describe('when the token owner does not have enough balance', function () { 95 | const amount = initialSupply.addn(1); 96 | 97 | it('reverts', async function () { 98 | await expectRevert.unspecified(token.connect(recipient).transferFrom( 99 | tokenOwner, to, amount.toString()), `${errorPrefix}: transfer amount exceeds balance`, 100 | ); 101 | }); 102 | }); 103 | }); 104 | }); 105 | 106 | describe('when the recipient is the zero address', function () { 107 | const amount = initialSupply.toString(); 108 | const to = ZERO_ADDRESS; 109 | 110 | beforeEach(async function () { 111 | await token.connect(initialHolder).approve(spender, amount); 112 | }); 113 | 114 | it('reverts', async function () { 115 | await expectRevert.unspecified(token.connect(recipient).transferFrom( 116 | tokenOwner, to, amount), `${errorPrefix}: transfer to the zero address`, 117 | ); 118 | }); 119 | }); 120 | }); 121 | 122 | describe('when the token owner is the zero address', function () { 123 | const amount = 0; 124 | const tokenOwner = ZERO_ADDRESS; 125 | const to = recipient.address; 126 | 127 | it('reverts', async function () { 128 | await expectRevert.unspecified(token.connect(recipient).transferFrom( 129 | tokenOwner, to, amount), `${errorPrefix}: transfer from the zero address`, 130 | ); 131 | }); 132 | }); 133 | }); 134 | 135 | describe('approve', function () { 136 | shouldBehaveLikeERC20Approve(errorPrefix, initialHolder, recipient, initialSupply, 137 | function (owner, spender, amount) { 138 | return token.connect(owner).approve(spender, amount.toString()); 139 | }, token, 140 | ); 141 | }); 142 | } 143 | 144 | function shouldBehaveLikeERC20Transfer (errorPrefix, initialHolder, recipient, balance, transfer, token) { 145 | let from = initialHolder.address; 146 | let to = recipient.address; 147 | describe('when the recipient is not the zero address', function () { 148 | describe('when the sender does not have enough balance', function () { 149 | const amount = balance.addn(1).toString(); 150 | 151 | it('reverts', async function () { 152 | await expectRevert.unspecified(transfer.call(this, from, to, amount), 153 | `${errorPrefix}: transfer amount exceeds balance`, 154 | ); 155 | }); 156 | }); 157 | 158 | describe('when the sender transfers all balance', function () { 159 | const amount = balance.toString(); 160 | 161 | it('transfers the requested amount', async function () { 162 | expect(await transfer.call(this, from, to, amount)).to.emit(token, 'Transfer').withArgs(from, to, amount); 163 | 164 | expect((await token.balanceOf(from)).toString()).to.be.bignumber.equal('0'); 165 | 166 | expect((await token.balanceOf(to)).toString()).to.be.bignumber.equal(amount); 167 | 168 | }); 169 | }); 170 | 171 | describe('when the sender transfers zero tokens', function () { 172 | const amount = new BN('0').toString(); 173 | 174 | it('transfers the requested amount', async function () { 175 | expect(await transfer.call(this, from, to, amount)).to.emit(token, 'Transfer').withArgs(from, to, amount); 176 | 177 | expect((await token.balanceOf(from)).toString()).to.be.bignumber.equal('0'); 178 | 179 | expect((await token.balanceOf(to)).toString()).to.be.bignumber.equal(balance); 180 | }); 181 | }); 182 | }); 183 | 184 | describe('when the recipient is the zero address', function () { 185 | it('reverts', async function () { 186 | await expectRevert.unspecified(transfer.call(this, from, ZERO_ADDRESS, balance.toString()), 187 | `${errorPrefix}: transfer to the zero address`, 188 | ); 189 | }); 190 | 191 | after(async function () { 192 | await token.connect(recipient).transfer(initialHolder.address, balance.toString()); 193 | }); 194 | }); 195 | 196 | 197 | } 198 | 199 | function shouldBehaveLikeERC20Approve (errorPrefix, initialHolder, recipient, supply, approve, token) { 200 | 201 | let owner = initialHolder.address; 202 | let spender = recipient.address; 203 | 204 | describe('when the spender is not the zero address', function () { 205 | describe('when the sender has enough balance', function () { 206 | const amount = supply.toString(); 207 | 208 | it('emits an approval event', async function () { 209 | expect (await approve.call(this, initialHolder, spender, amount)).to.emit(token, 'Approval').withArgs(owner, spender, amount); 210 | }); 211 | 212 | describe('when there was no approved amount before', function () { 213 | it('approves the requested amount', async function () { 214 | await approve.call(this, initialHolder, spender, amount); 215 | 216 | expect((await token.allowance(owner, spender)).toString()).to.be.bignumber.equal(amount); 217 | }); 218 | }); 219 | 220 | describe('when the spender had an approved amount', function () { 221 | beforeEach(async function () { 222 | await approve.call(this, initialHolder, spender, new BN(1).toString()); 223 | }); 224 | 225 | it('approves the requested amount and replaces the previous one', async function () { 226 | await approve.call(this, initialHolder, spender, amount); 227 | 228 | expect((await token.allowance(owner, spender)).toString()).to.be.bignumber.equal(amount); 229 | }); 230 | }); 231 | }); 232 | 233 | describe('when the sender does not have enough balance', function () { 234 | const amount = supply.addn(1).toString(); 235 | 236 | it('emits an approval event', async function () { 237 | expect(await approve.call(this, initialHolder, spender, amount)).to.emit(token, 'Approval').withArgs(owner, spender, amount); 238 | }); 239 | 240 | describe('when there was no approved amount before', function () { 241 | it('approves the requested amount', async function () { 242 | await approve.call(this, initialHolder, spender, amount); 243 | 244 | expect((await token.allowance(owner, spender)).toString()).to.be.bignumber.equal(amount); 245 | }); 246 | }); 247 | 248 | describe('when the spender had an approved amount', function () { 249 | beforeEach(async function () { 250 | await approve.call(this, initialHolder, spender, new BN(1).toString()); 251 | }); 252 | 253 | it('approves the requested amount and replaces the previous one', async function () { 254 | await approve.call(this, initialHolder, spender, amount); 255 | 256 | expect((await token.allowance(owner, spender)).toString()).to.be.bignumber.equal(amount); 257 | }); 258 | }); 259 | }); 260 | }); 261 | 262 | describe('when the spender is the zero address', function () { 263 | it('reverts', async function () { 264 | await expectRevert.unspecified(approve.call(this, initialHolder, ZERO_ADDRESS, supply), 265 | `${errorPrefix}: approve to the zero address`, 266 | ); 267 | }); 268 | }); 269 | } 270 | 271 | module.exports = { 272 | shouldBehaveLikeERC20 273 | }; 274 | -------------------------------------------------------------------------------- /test/utils/minting-blocks.js: -------------------------------------------------------------------------------- 1 | const {ethers, network} = require('hardhat') 2 | 3 | const setBlockTimestampInSeconds = (startTime, seconds) => { 4 | return ethers.provider.send('evm_setNextBlockTimestamp', [startTime.toNumber() + seconds]); 5 | } 6 | 7 | const setBlockTimestampInMonth = (startTime, month) => { 8 | return ethers.provider.send('evm_setNextBlockTimestamp', [startTime.toNumber() + 60*60*24*30*month]); 9 | } 10 | 11 | const setBlockTimestampInMonthAndSeconds = (startTime, month, seconds) => { 12 | return ethers.provider.send('evm_setNextBlockTimestamp', [startTime.toNumber() + 60*60*24*30*month + seconds]); 13 | } 14 | 15 | const mintNewBlock = () => { 16 | return network.provider.send("evm_mine") 17 | } 18 | 19 | const getBlockNumber = () => { 20 | return network.provider.send("eth_blockNumber") 21 | } 22 | 23 | module.exports = { 24 | setBlockTimestampInSeconds, 25 | setBlockTimestampInMonth, 26 | setBlockTimestampInMonthAndSeconds, 27 | mintNewBlock, 28 | getBlockNumber 29 | }; --------------------------------------------------------------------------------