This is a contract based on standard openzeppelin-contracts ERC20
and Ownable
, with following functions added:
- The token has name
OctToken
and 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
beneficiary
of 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
OctToken
at construction time, and the address will be immutable after construction. - Anyone can call function
token()
to get the address of contractOctToken
bonded 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.timestamp
is smaller thanreleaseStartTime
, return 0 - If
block.timestamp
is larger thanRELEASE_END_TIME
:- If param
supervised
istrue
, returnunreleasedSupervisedBalance
- If param
supervised
isfalse
, returnunreleasedBalance
- If param
- Calculate
passedDays
: (block.timestamp
-releaseStartTime
) /SECONDS_OF_A_DAY
- Calculate
totalDays
: (RELEASE_END_TIME
-releaseStartTime
) /SECONDS_OF_A_DAY
- If param
supervised
istrue
, returnunreleasedSupervisedBalance
*passedDays
/totalDays
- If param
supervised
isfalse
, returnunreleasedBalance
*passedDays
/totalDays
- Get beneficiary corresponding to param
- Anyone can call function
unreleasedBalanceOf(address)
to get theunreleasedBalance
of 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 thewithdrawedBalance
of a certain beneficiary corresponding to paramaddress
. - Anyone can call function
unreleasedSupervisedBalanceOf(address)
to get theunreleasedSupervisedBalance
of 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.timestamp
is smaller thanEARLIEST_RELEASE_START_TIME
, update the properties of the beneficiary corresponding to paramaddress
as follow:unreleasedBalance
:- If param
supervised
istrue
: NO change - If param
supervised
isfalse
:unreleasedBalance
+amount
- If param
releaseStartTime
:EARLIEST_RELEASE_START_TIME
releasedBalance
: NO changewithdrawedBalance
: NO changeunreleasedSupervisedBalance
:- If param
supervised
istrue
:unreleasedSupervisedBalance
+amount
- If param
supervised
isfalse
: NO change
- If param
- If
block.timestamp
is larger thanEARLIEST_RELEASE_START_TIME
, update the properties of the beneficiary corresponding to paramaddress
as follow:releasedBalance
:releasedBalanceOf(address)
unreleasedBalance
:- If param
supervised
istrue
:unreleasedBalanceOf(address)
- If param
supervised
isfalse
:unreleasedBalanceOf(address)
+amount
- If param
withdrawedBalance
: NO changeunreleasedSupervisedBalance
:- If param
supervised
istrue
:unreleasedSupervisedBalanceOf(address)
+amount
- If param
supervised
isfalse
: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 increaseunreleasedBalance
orunreleasedSupervisedBalance
of a certain beneficiary corresponding to paramaddress
.- This function is a simple wraper of private function
_benefit(address, amount, supervised)
. - The param
address
MUST 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
amount
must be less or equal to avaialable balance, which is calculated by:releasedBalanceOf(_msgSender())
-withdrawedBalance
- The
withdrawedBalance
will 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
amount
must be less or equal tounreleasedBalanceOf(_msgSender())
- If
block.timestamp
is smaller thanEARLIEST_RELEASE_START_TIME
, update the properties of the beneficiary corresponding to_msgSender()
as follow:unreleasedBalance
:unreleasedBalance
-amount
releaseStartTime
:EARLIEST_RELEASE_START_TIME
releasedBalance
: NO changewithdrawedBalance
: NO changeunreleasedSupervisedBalance
: NO change
- If
block.timestamp
is larger thanEARLIEST_RELEASE_START_TIME
, update the properties of the beneficiary corresponding to_msgSender()
as follow:releasedBalance
:releasedBalanceOf(_msgSender())
unreleasedBalance
:unreleasedBalanceOf(_msgSender())
-amount
withdrawedBalance
: 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
amount
must be less or equal tounreleasedSupervisedBalanceOf(address)
- If
block.timestamp
is smaller thanEARLIEST_RELEASE_START_TIME
, update the properties of the beneficiary corresponding toaddress
as follow:unreleasedBalance
: NO changereleaseStartTime
:EARLIEST_RELEASE_START_TIME
releasedBalance
: NO changewithdrawedBalance
: NO changeunreleasedSupervisedBalance
:unreleasedSupervisedBalance
-amount
- If
block.timestamp
is larger thanEARLIEST_RELEASE_START_TIME
, update the properties of the beneficiary corresponding toaddress
as follow:releasedBalance
:releasedBalanceOf(address)
unreleasedBalance
:unreleasedBalanceOf(address)
withdrawedBalance
: NO changeunreleasedSupervisedBalance
:unreleasedSupervisedBalanceOf(address)
-amount
releaseStartTime
:block.timestamp
- (block.timestamp
%SECONDS_OF_A_DAY
)
- Get beneficiary corresponding to param
Install openzeppelin/contracts.
npm install @openzeppelin/contracts
Install hardhat for testing.
npm install --save-dev hardhat
Install modules for running testing scripts compatible with Waffle.
npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers
You can run the tests by:
npx hardhat test
or
npm run test