For Developers & AI Agents

Machine-readable reference · Updated September 2026

What 0xKeep is

An immutable, non-custodial smart contract that time-locks ERC-20 tokens (including Uniswap v2 and Aerodrome classic LP tokens) and runs linear vesting schedules with an optional cliff. No owner, no admin, no pause, no proxy. It does not lock NFT liquidity positions (Uniswap v3/v4). Not audited by a third party; see the security page.

Contracts

ChainIDAddressLock / vest fee
Base84530x048d1326B3b0531A5d043984F4e495285B07af4B0 / 0 ETH
Arbitrum One421610xDC9bFb15C28486590Cbf58F3FEA9ADbEB9B0334c0.03 / 0.02 ETH
Optimism100x1Ecf87D69c4a5c8D10ffb7D73e8ABB415043f8660.03 / 0.02 ETH

ABI: /abi/ZeroXKeepLocker.json. Source verified on Sourcify (repo.sourcify.dev/<chainId>/<address>). Read LOCK_FEE() and VESTING_FEE() from the contract instead of hardcoding them.

Check a lock (read-only JSON API)

No key, no wallet. Open CORS, cached about 30 seconds.

GET https://app.0x-keep.xyz/api/lock/0xK-BL-0 GET https://app.0x-keep.xyz/api/vesting/0xK-BV-0

Lock responses include status (locked | unlocked | withdrawn), token address, symbol and decimals, amount, owner, unlock time (unix and ISO), and contract and explorer links. Vesting responses add total, claimed, vestedNow, claimableNow and the schedule (start, cliff end, end).

ID format: 0xK-{chain}{type}-{n}. Chain: B Base, A Arbitrum, O Optimism. Type: L lock, V vesting. n is the on-chain index used by locks(n) and vestings(n). Errors return {"error": "..."} with status 400 (bad ID), 404 (not found) or 502 (RPC problem).

For security scanners: all locks for a token

One call returns every active 0xKeep lock and vesting schedule for a token, the share of supply locked, and a locked_detail block in the GoPlus Token Security format.

GET https://app.0x-keep.xyz/api/token/{chainId}/{tokenAddress} e.g. https://app.0x-keep.xyz/api/token/8453/0x4200000000000000000000000000000000000006

Full integration notes (events, state reads, guarantees): INTEGRATIONS.md.

Read directly from the contract

allLocksCount() returns uint256 locks(uint256 id) returns (token, amount uint96, owner, decimals uint8, withdrawn bool, unlockTime uint32, id) getUserLocks(address owner) returns uint256[] allVestingsCount() returns uint256 vestings(uint256 id) returns (token, totalAmount uint96, owner, decimals uint8, claimedAmount uint96, startTime uint32, cliffDuration uint32, duration uint32, id) getUserVestings(address owner) returns uint256[]

A lock can be withdrawn by its owner when block.timestamp >= unlockTime and withdrawn is false. Vesting releases linearly from startTime + cliffDuration over duration seconds.

Create a lock or vesting schedule

Lock: 1. token.approve(LOCKER, amount) 2. LOCKER.lockToken(token, amount, unlockTime) // payable, send LOCK_FEE() emits Locked(lockId, token, owner, amount, unlockTime, chainId) Vesting: 1. token.approve(LOCKER, amount) 2. LOCKER.createVesting(token, amount, cliffSeconds, durationSeconds) // payable, send VESTING_FEE() emits VestingCreated(vestingId, token, owner, amount, cliff, duration, chainId)

Contract limits: amount above 0 and within uint96; unlock time in the future and under 100 years away; vesting duration at least 1 day; cliff under 10 years. Fee-on-transfer tokens are recorded at the amount actually received. Rebasing tokens are not supported. Other owner actions: extendLock, transferLockOwnership, withdrawLock, claimVesting, transferVestingOwnership.

Plain-text version

The same information for language models: /llms.txt.