This is a contract based on standard openzeppelin-contracts ERC20 and Ownable, with following functions added:
- The token has name
OctTokenand symbolOCT. - The token has fixed total supply - 100 million (100,000,000).
- All of the OCT tokens will be minted to the owner (deployer) of the contract at construction time. After this, there is NO WAY to mint or burn OCT tokens.
- Only the owner of the contract can transfer OCT tokens to other accounts until the function
unlockTransfer()is called. - The function
unlockTransfer()can ONLY be called by the owner of the contract. - After the owner of the contract call function
unlockTransfer()the contract will act as a standard ERC20 contract, and there is NO WAY to lock the contract again.
This is a contract based on standard openzeppelin-contracts Ownable, with the following functions added:
- This contract has the following constants:
// Seconds of a day
uint256 private constant SECONDS_OF_A_DAY = 86400;
// The earliest timestamp of token release period (2021/09/01 00:00:00 GMT).
//
// Before this time NO ONE can withdraw any token from this contract.
uint256 private constant EARLIEST_RELEASE_START_TIME = 1630454400;
// The end timestamp of token release period (2024/09/01 00:00:00 GMT).
//
// After this time, ANY ONE can withdraw any amount of tokens they held.
uint256 private constant RELEASE_END_TIME = 1725148800;- The
beneficiaryof this contract is defined as follow:
// The storage data of a beneficiary
//
// Because the smart contract can NOT automatically execute over time,
// the value of 'unreleasedBalance', 'unreleasedSupervisedBalance' and 'releasedBalance'
// will be updated ONLY when 'unreleasedBalance' or 'unreleasedSupervisedBalance'
// need to be modified during release period (from EARLIEST_RELEASE_START_TIME to RELEASE_END_TIME)
// by calling function '_benefit(address, amount, supervised)' or 'decreaseBenefitOf(address, amount)'
struct Beneficiary {
// The amount of unreleased balance of the beneficiary.
//
// This value may NOT be equal to the actual unreleased balance,
// call function 'unreleasedBalanceOf(address)' to get actual value.
uint256 unreleasedBalance;
// The amount of unreleased supervised balance of the beneficiary.
//
// This value may NOT be equal to the actual unreleased supervised balance,
// call function 'unreleasedSupervisedBalanceOf(address)' to get actual value.
uint256 unreleasedSupervisedBalance;
// The amount of released balance of the beneficiary.
//
// This value may NOT be equal to the actual total released balance,
// call function 'releasedBalanceOf(address)' to get actual value.
uint256 releasedBalance;
// The amount of withdrawed balance of the beneficiary.
//
// This value will be updated on each withdraw operation.
uint256 withdrawedBalance;
// The start time when the beneficiary can withdraw held tokens.
//
// This value will be updated ONLY when 'unreleasedBalance' or 'unreleasedSupervisedBalance'
// is changed during release period (from EARLIEST_RELEASE_START_TIME to RELEASE_END_TIME)
// for recalculating the time lock amount of held balance of beneficiary.
uint256 releaseStartTime;
}- This contract will accept an address of contract
OctTokenat construction time, and the address will be immutable after construction. - Anyone can call function
token()to get the address of contractOctTokenbonded to this contract. - This contract has a private function
_balanceToReleaseTo(address, supervised)which implements the following logic:- Get beneficiary corresponding to param
address. - If
block.timestampis smaller thanreleaseStartTime, return 0 - If
block.timestampis larger thanRELEASE_END_TIME:- If param
supervisedistrue, returnunreleasedSupervisedBalance - If param
supervisedisfalse, returnunreleasedBalance
- If param
- Calculate
passedDays: (block.timestamp-releaseStartTime) /SECONDS_OF_A_DAY - Calculate
totalDays: (RELEASE_END_TIME-releaseStartTime) /SECONDS_OF_A_DAY - If param
supervisedistrue, returnunreleasedSupervisedBalance*passedDays/totalDays - If param
supervisedisfalse, returnunreleasedBalance*passedDays/totalDays
- Get beneficiary corresponding to param
- Anyone can call function
unreleasedBalanceOf(address)to get theunreleasedBalanceof a certain beneficiary corresponding to paramaddress.- Get beneficiary corresponding to param
address. - The result of this function is calculated by:
unreleasedBalance-_balanceToReleaseTo(address, false)
- Get beneficiary corresponding to param
- Anyone can call function
withdrawedBalanceOf(address)to get thewithdrawedBalanceof a certain beneficiary corresponding to paramaddress. - Anyone can call function
unreleasedSupervisedBalanceOf(address)to get theunreleasedSupervisedBalanceof a certain beneficiary corresponding to paramaddress.- Get beneficiary corresponding to param
address. - The result of this function is calculated by:
unreleasedSupervisedBalance-_balanceToReleaseTo(address, true)
- Get beneficiary corresponding to param
- Anyone can call function
releasedBalanceOf(address)to get the total released balance of a certain beneficiary corresponding to paramaddress.- Get beneficiary corresponding to param
address. - The result of this function is calculated by:
releasedBalance+_balanceToReleaseTo(address, true)+_balanceToReleaseTo(address, false)
- Get beneficiary corresponding to param
- This contract has a private function
_benefit(address, amount, supervised)which implements the following logic:- If
block.timestampis smaller thanEARLIEST_RELEASE_START_TIME, update the properties of the beneficiary corresponding to paramaddressas follow:unreleasedBalance:- If param
supervisedistrue: NO change - If param
supervisedisfalse:unreleasedBalance+amount
- If param
releaseStartTime:EARLIEST_RELEASE_START_TIMEreleasedBalance: NO changewithdrawedBalance: NO changeunreleasedSupervisedBalance:- If param
supervisedistrue:unreleasedSupervisedBalance+amount - If param
supervisedisfalse: NO change
- If param
- If
block.timestampis larger thanEARLIEST_RELEASE_START_TIME, update the properties of the beneficiary corresponding to paramaddressas follow:releasedBalance:releasedBalanceOf(address)unreleasedBalance:- If param
supervisedistrue:unreleasedBalanceOf(address) - If param
supervisedisfalse:unreleasedBalanceOf(address)+amount
- If param
withdrawedBalance: NO changeunreleasedSupervisedBalance:- If param
supervisedistrue:unreleasedSupervisedBalanceOf(address)+amount - If param
supervisedisfalse:unreleasedSupervisedBalanceOf(address)
- If param
releaseStartTime:block.timestamp- (block.timestamp%SECONDS_OF_A_DAY)
- If
- Only the owner (deployer) of this contract can call function
benefit(address, amount, supervised)to increaseunreleasedBalanceorunreleasedSupervisedBalanceof a certain beneficiary corresponding to paramaddress.- This function is a simple wraper of private function
_benefit(address, amount, supervised). - The param
addressMUST be an EOA address. (This will be verified by the owner of this contract rather than by contract code.)
- This function is a simple wraper of private function
- Anyone can call function
withdraw(amount)to withdraw a certain amount tokens to the address of himself.- Get beneficiary corresponding to
_msgSender(). - The param
amountmust be less or equal to avaialable balance, which is calculated by:releasedBalanceOf(_msgSender())-withdrawedBalance - The
withdrawedBalancewill be increased byamount, if the token transfer is success.
- Get beneficiary corresponding to
- Anyone can call function
transferUnreleasedBalance(address, amount)to transfer a part or whole of his unreleased balance to another account (address).- Get beneficiary corresponding to
_msgSender(). - The param
amountmust be less or equal tounreleasedBalanceOf(_msgSender()) - If
block.timestampis smaller thanEARLIEST_RELEASE_START_TIME, update the properties of the beneficiary corresponding to_msgSender()as follow:unreleasedBalance:unreleasedBalance-amountreleaseStartTime:EARLIEST_RELEASE_START_TIMEreleasedBalance: NO changewithdrawedBalance: NO changeunreleasedSupervisedBalance: NO change
- If
block.timestampis larger thanEARLIEST_RELEASE_START_TIME, update the properties of the beneficiary corresponding to_msgSender()as follow:releasedBalance:releasedBalanceOf(_msgSender())unreleasedBalance:unreleasedBalanceOf(_msgSender())-amountwithdrawedBalance: NO changeunreleasedSupervisedBalance:unreleasedSupervisedBalanceOf(_msgSender())releaseStartTime:block.timestamp- (block.timestamp%SECONDS_OF_A_DAY)
- Call private function
_benefit(address, amount, false).
- Get beneficiary corresponding to
- Only the owner (deployer) of this contract can call function
decreaseBenefitOf(address, amount)to decrease the benefit of a certain beneficiary corresponding to paramaddress.- Get beneficiary corresponding to param
address. - The param
amountmust be less or equal tounreleasedSupervisedBalanceOf(address) - If
block.timestampis smaller thanEARLIEST_RELEASE_START_TIME, update the properties of the beneficiary corresponding toaddressas follow:unreleasedBalance: NO changereleaseStartTime:EARLIEST_RELEASE_START_TIMEreleasedBalance: NO changewithdrawedBalance: NO changeunreleasedSupervisedBalance:unreleasedSupervisedBalance-amount
- If
block.timestampis larger thanEARLIEST_RELEASE_START_TIME, update the properties of the beneficiary corresponding toaddressas follow:releasedBalance:releasedBalanceOf(address)unreleasedBalance:unreleasedBalanceOf(address)withdrawedBalance: NO changeunreleasedSupervisedBalance:unreleasedSupervisedBalanceOf(address)-amountreleaseStartTime:block.timestamp- (block.timestamp%SECONDS_OF_A_DAY)
- Get beneficiary corresponding to param
Install openzeppelin/contracts.
npm install @openzeppelin/contractsInstall hardhat for testing.
npm install --save-dev hardhatInstall modules for running testing scripts compatible with Waffle.
npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethersYou can run the tests by:
npx hardhat testor
npm run test