Simple ERC20 token lock with cliff + daily linear vesting. Intended for Polygon-compatible deployment.
ratePerDayis a percentage-per-day scaled byRATE_SCALE = 1_000_000_000_000.1_000_000_000_000= 100% per day- Example: 2 years (730 days) =>
ratePerDay = 1_000_000_000_000 / 730.
- Vesting starts only after
unlock()is called and the cliff period has elapsed.
lock(...)- Transfers tokens into the contract.
- Assigns a
lockIdand emitsLockCreated. retractUntilUnlockcontrols the retraction window:retractUntilUnlock = true: creator may retract only untilunlock()is called.retractUntilUnlock = false: creator may retract until the first withdrawal.
unlock(lockId, unlockTime)- Must be called by the original lock creator.
- Starts the cliff countdown at
unlockTime. unlockTimeis a Unix timestamp in seconds.
withdraw(lockId, amount, percent, to)- Must be called by the
withdrawAddressset inlock(). - Can withdraw by exact
amountorpercent(scaled byRATE_SCALE). - If both
amountandpercentare zero, defaults to 100% of available. - When fully withdrawn, emits
LockClosedwithreason = 0and deletes the lock.
- Must be called by the
retract(lockId, to)- Creator-only.
- If
retractUntilUnlock = true: only allowed while the lock is still locked (beforeunlock()). - If
retractUntilUnlock = false: only allowed if no withdrawals have occurred. - Emits
LockClosedwithreason = 1and deletes the lock.
LockCreated— new lock created.Unlocked— unlock time set.Withdrawn— withdrawal executed.Retracted— retraction executed.LockClosed— final snapshot emitted on full withdraw or retract.
npm install
npm run compile
npm test- Copy
.env.exampleto.envand fill in:AMOY_RPC_URLPRIVATE_KEYPOLYGONSCAN_API_KEY
- Deploy:
npx hardhat run scripts/deploy.js --network amoy- Verify on PolygonScan (to enable the contract UI there):
npx hardhat verify --network amoy <DEPLOYED_ADDRESS>Deploy a mock token to Amoy for local testing against the lock contract:
npx hardhat run scripts/deploy-mock.js --network amoyApprove the TokenLock contract to move your tokens before calling lock:
# TokenLock address (Amoy): 0x324d9b90A07D587B4FA0D68c22645B9c8D321079
# Approve 1,000 tokens (18 decimals)
approve(spender=0x324d9b90A07D587B4FA0D68c22645B9c8D321079, amount=1000000000000000000000)Mint to a wallet (example: 1,000,000 tokens):
npx hardhat run scripts/mint-mock.js --network amoy -- \\
0xYourMockToken 0xYourWallet 1000000Or via task:
npx hardhat mint-mock --network amoy --token 0xYourMockToken --to 0xYourWallet --amount 1000000Approve via task:
npx hardhat approve-mock --network amoy --token 0xYourMockToken --spender 0x324d9b90A07D587B4FA0D68c22645B9c8D321079 --amount 1000- This repo uses Hardhat.
- ERC20 only (no permit, no ERC777 hooks).