Locker
Free token and LP locks plus batch vesting schedules, with public on-chain proof anyone can verify.
HoodLocker is two products in one contract: time locks for any ERC-20 (including v2-style LP tokens), and batch vesting schedules. It has no owner, no admin, no pause, no upgrade path and no fee — you pay gas and nothing else.
#Locks
function createLock(address token, uint256 amount, uint64 unlockTime, address unlocker)
external returns (uint256 lockId);
function extendLock(uint256 lockId, uint64 newUnlockTime) external; // owner; extend only
function withdraw(uint256 lockId) external; // unlocker, after expiryunlocker — which defaults to you if you pass the zero address. Tokens always go to the unlocker, never to a caller-supplied address.unlockTime further out at any time — including re-locking one that already expired but was not withdrawn. It can never be shortened.#Vesting
function createVestings(
address token,
address[] calldata recipients,
uint256[] calldata amounts,
uint64 start, // 0 = now; must not be in the past
uint64 duration, // seconds
uint32 interval // 0 = continuous; 86400 / 604800 / 2592000 = daily / weekly / monthly
) external returns (uint256 firstVestingId);
function claim(uint256 vestingId) external; // beneficiary only
function claimable(uint256 vestingId) external view returns (uint256);
function vestedAmount(uint256 vestingId, uint64 timestamp) external view returns (uint256);One call creates one schedule per recipient, funded by a single transfer, and the batch occupies ids [firstVestingId, firstVestingId + recipients.length). Beneficiaries claim their own schedules whenever they like; nobody can claim on their behalf and nobody can revoke.
#Linear vs stepped
| interval | Behaviour |
|---|---|
| 0 | Continuous. Vested amount is exactly proportional to elapsed time. |
| 86400 / 604800 / 2592000 | Stepped. The schedule splits into ceil(duration / interval) equal tranches, one unlocking at the end of each interval — daily, weekly, or monthly. |
intervalmay not exceedduration, anddurationmay not be zero.- Fee-on-transfer tokens are rejected here (unlike locks). A skim would make the per-recipient amounts unpayable, and silently rescaling somebody’s allocation is not an acceptable failure mode.
- A cliff is a stepped schedule whose first tranche is the cliff — there is no separate cliff parameter.
#Reading locks and schedules
The contract keeps on-chain list views — locksOf(owner), vestingsOf(beneficiary), vestingsCreatedBy(creator) — but they do not paginate, so they are a fallback rather than a UI data source. Everything here also lands in indexed form as TokenLock, VestingSchedule and VestingClaim, already joined to token metadata — see Data & indexing.
#Proof
Every lock and schedule emits an event and is indexed, so a lock is a public fact rather than a screenshot: anyone can read the token, the amount, the unlock time and the unlocker straight off the chain. That is the entire point of using a lock contract instead of a promise.
