diff --git a/contracts/token/LockedTokenVault.sol b/contracts/token/LockedTokenVault.sol index f79f6a1..682a158 100644 --- a/contracts/token/LockedTokenVault.sol +++ b/contracts/token/LockedTokenVault.sol @@ -31,8 +31,6 @@ contract LockedTokenVault is Ownable { mapping(address => uint256) internal originBalances; mapping(address => uint256) internal claimedBalances; - mapping(address => address) internal holderTransferRequest; - uint256 public _UNDISTRIBUTED_AMOUNT_; uint256 public _START_RELEASE_TIME_; uint256 public _RELEASE_DURATION_; @@ -42,13 +40,17 @@ contract LockedTokenVault is Ownable { // ============ Modifiers ============ + event Claim(address indexed holder, uint256 origin, uint256 claimed, uint256 amount); + + // ============ Modifiers ============ + modifier beforeStartRelease() { require(block.timestamp < _START_RELEASE_TIME_, "RELEASE START"); _; } modifier afterStartRelease() { - require(block.timestamp > _START_RELEASE_TIME_, "RELEASE NOT START"); + require(block.timestamp >= _START_RELEASE_TIME_, "RELEASE NOT START"); _; } @@ -94,6 +96,7 @@ contract LockedTokenVault is Ownable { require(holderList.length == amountList.length, "batch grant length not match"); uint256 amount = 0; for (uint256 i = 0; i < holderList.length; ++i) { + // for saving gas, no event for grant originBalances[holderList[i]] = originBalances[holderList[i]].add(amountList[i]); amount = amount.add(amountList[i]); } @@ -101,9 +104,11 @@ contract LockedTokenVault is Ownable { } function recall(address holder) external onlyOwner distributeNotFinished { - uint256 amount = originBalances[holder]; + _UNDISTRIBUTED_AMOUNT_ = _UNDISTRIBUTED_AMOUNT_.add(originBalances[holder]).sub( + claimedBalances[holder] + ); originBalances[holder] = 0; - _UNDISTRIBUTED_AMOUNT_ = _UNDISTRIBUTED_AMOUNT_.add(amount); + claimedBalances[holder] = 0; } // ============ For Holder ============ @@ -120,10 +125,20 @@ contract LockedTokenVault is Ownable { uint256 claimableToken = getClaimableBalance(msg.sender); _tokenTransferOut(msg.sender, claimableToken); claimedBalances[msg.sender] = claimedBalances[msg.sender].add(claimableToken); + emit Claim( + msg.sender, + originBalances[msg.sender], + claimedBalances[msg.sender], + claimableToken + ); } // ============ View ============ + function isReleaseStart() external view returns (bool) { + return block.timestamp >= _START_RELEASE_TIME_; + } + function getOriginBalance(address holder) external view returns (uint256) { return originBalances[holder]; } @@ -132,29 +147,27 @@ contract LockedTokenVault is Ownable { return claimedBalances[holder]; } - function getHolderTransferRequest(address holder) external view returns (address) { - return holderTransferRequest[holder]; - } - function getClaimableBalance(address holder) public view returns (uint256) { - if (block.timestamp < _START_RELEASE_TIME_) { - return 0; - } uint256 remainingToken = getRemainingBalance(holder); return originBalances[holder].sub(remainingToken).sub(claimedBalances[holder]); } function getRemainingBalance(address holder) public view returns (uint256) { - uint256 remainingToken = 0; - uint256 timePast = block.timestamp.sub(_START_RELEASE_TIME_); + uint256 remainingRatio = getRemainingRatio(block.timestamp); + return DecimalMath.mul(originBalances[holder], remainingRatio); + } + + function getRemainingRatio(uint256 timestamp) public view returns (uint256) { + if (timestamp < _START_RELEASE_TIME_) { + return DecimalMath.ONE; + } + uint256 timePast = timestamp.sub(_START_RELEASE_TIME_); if (timePast < _RELEASE_DURATION_) { uint256 remainingTime = _RELEASE_DURATION_.sub(timePast); - remainingToken = originBalances[holder] - .sub(DecimalMath.mul(originBalances[holder], _CLIFF_RATE_)) - .mul(remainingTime) - .div(_RELEASE_DURATION_); + return DecimalMath.ONE.sub(_CLIFF_RATE_).mul(remainingTime).div(_RELEASE_DURATION_); + } else { + return 0; } - return remainingToken; } // ============ Internal Helper ============ diff --git a/test/TokenLock.test.ts b/test/TokenLock.test.ts index d678ebc..b82e52b 100644 --- a/test/TokenLock.test.ts +++ b/test/TokenLock.test.ts @@ -134,6 +134,10 @@ describe("Lock DODO Token", () => { assert.equal(await LockedTokenVault.methods.getClaimableBalance(u3).call(), "0") assert.ok(approxEqual(await LockedTokenVault.methods.getRemainingBalance(u3).call(), decimalStr("82.5"))) assert.ok(approxEqual(await DODOToken.methods.balanceOf(u3).call(), decimalStr("93.75"))) + + // transfer from u3 to u1 + await LockedTokenVault.methods.transferLockedToken(u1).send(ctx.sendParam(u3)) + }) it("withdraw", async () => {