---
version: 2026-10-14
name: x-lock-agent-skill
description: >-
  Lock or vest ERC-20 tokens with X-Lock, check lock status via x402, or use
  X-Escrow (X-Crow) for non-custodial escrow. Paste "connect to
  xlock.liquidlogicx.com/skill.md" into an agent to load this guide.
---

# X-Lock by Liquid Logic X

X-Lock is a non-custodial token lock and vesting factory. Anyone can lock ERC-20 tokens to a beneficiary until a time or on a vesting schedule; locks cannot be cancelled and the owner has no path to move locked tokens. Public lock pages live at [xlock.liquidlogicx.com](https://xlock.liquidlogicx.com). Contact: hello@liquidlogicx.com.

Also at: https://xlock.liquidlogicx.com/.well-known/skill.md

## Chains and factories

| Chain | Chain ID | Factory | Fee token |
|---|---|---|---|
| Base | 8453 | `0xD47326d2B5822355D9c4dfF5265849a5bb5D20ec` (V1) | 10 USDC or 575,000 LLX |
| BNB Chain | 56 | `0x54b3019acBBf9aeFDc545d5CeA75e10322eA9D44` (V2) | 10 USDC (18 dec) |
| Ethereum | 1 | same V2 | 10 USDC |
| Arbitrum | 42161 | same V2 | 10 USDC |
| Polygon | 137 | same V2 | 10 USDC |
| Avalanche | 43114 | same V2 | 10 USDC |
| Optimism | 10 | same V2 | 10 USDC |
| Arc | 5042 | same V2 | 10 USDC |
| Tempo | 4217 | same V2 | 10 USDC |
| Robinhood Chain | 4663 | same V2 | 10 USDG |
| XRPL EVM | 1440000 | same V2 | 10 RLUSD (18 dec) |

- Fee cap: 50 USDC/USDG/RLUSD (or 11,500,000 LLX on Base). `maxFee` is a slippage guard.
- Fee recipient: `0x8e351267f713e8fa21befd4aad2ed4ae4146cb38`
- Owner: `0x931b89e025beb0b4243faa63c70e8d64c43bc9b8` (no function can touch locked tokens)
- Base USDC: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`
- Base $LLX: `0xB9Dd507a5b352783b25e14c9b6E77D9f0067380f`
- Basescan (Base V1): https://basescan.org/address/0xD47326d2B5822355D9c4dfF5265849a5bb5D20ec#code
- Official address list: https://liquidlogicx.com/addresses
- Tutorial: https://xlock.liquidlogicx.com/tutorial

Public lock URLs:

- Base: `https://xlock.liquidlogicx.com/lock/{id}`
- Other chains: `https://xlock.liquidlogicx.com/c/{key}/lock/{id}` (`key` = `bnb` | `ethereum` | `arbitrum` | `polygon` | `avalanche` | `optimism` | `arc` | `tempo` | `robinhood` | `xrplevm`)

---

## Read — lock-status (x402)

Ask “is this token locked in X-Lock?” before buying or listing.

```
GET https://audit.liquidlogicx.com/api/lock-status?token=0x…&chain=base
```

Also accepts POST JSON `{ "token": "0x…", "chain": "base" }` (`address` is an alias for `token`).

| | |
|---|---|
| Price | $0.005 USDC on Base (5000 atomic units, 6 decimals) |
| payTo | `0x147991A1c25e78f6D9225d2dBA61eD93A6158c7b` |
| asset | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` (Base USDC) |
| Network | eip155:8453 |

`chain` optional: `base` | `bnb` | `ethereum` | `arbitrum` | `polygon` | `avalanche` | `optimism` | `arc` | `tempo` | `robinhood` | `xrplevm`. Omit (or `all`) to scan all 11.

### 402 flow

1. Call without payment → **402 Payment Required** with a `PAYMENT-REQUIRED` header (x402 v2).
2. Pay $0.005 USDC on Base to `payTo` via an x402 client / facilitator.
3. Retry with the payment proof headers; on success you get **200** JSON (`isLocked`, per-chain locks, `lockUrl`s, next unlock).

x402 only settles when the handler returns status &lt; 400. Requests that fail validation are **never charged**, but the payment gate runs first: a request with no payment header gets **402 Payment Required** even if its input is bad (it does not get a 400). The 400 / 503 codes below appear only after a valid payment header is attached, and the payment is not settled when they are returned.

### 400 / 503 cases (returned after payment is attached; not charged)

| Status | code | When |
|---|---|---|
| 400 | `BAD_TOKEN` | Missing/invalid `token` (must be `0x` + 40 hex) |
| 400 | `BAD_CHAIN` | Unknown `chain` |
| 503 | `RPC_UNAVAILABLE` | Every chain RPC failed (`chainsChecked` empty) |

Example (an unpaid request, even with bad input, gets 402 first):

```bash
curl -sS -i "https://audit.liquidlogicx.com/api/lock-status"
# HTTP/2 402 (payment required; the BAD_TOKEN 400 only shows once a payment header is attached)
```

Paid example (after x402 payment headers are attached by your client):

```bash
curl -sS "https://audit.liquidlogicx.com/api/lock-status?token=0xB9Dd507a5b352783b25e14c9b6E77D9f0067380f&chain=base"
```

Verify any lock on its public page or on the chain explorer (factory `getLock(id)`, lock wallet balance).

---

## Write — create a lock (agent wallet)

Flow: **approve** the factory for the token amount (and the fee token if needed) → **createTimeLock** or **createVestingLock** → parse **LockCreated** → share the lock URL.

`feeMode`: `0` = USDC/USDG/RLUSD, `1` = LLX (Base only). Pass `maxFee` ≥ quoted fee (e.g. `10000000` for 10 USDC on 6-dec chains; on BNB USDC and on XRPL EVM RLUSD are 18-dec so `10e18`).

### createTimeLock

```text
createTimeLock(token, beneficiary, amount, unlockTime, feeMode, maxFee)
  → (lockId, wallet)
```

### createVestingLock

```text
createVestingLock(token, beneficiary, amount, start, cliffDuration, duration, feeMode, maxFee)
  → (lockId, wallet)
```

### LockCreated event

```text
LockCreated(lockId, wallet, token, creator, beneficiary, amount, start, cliffDuration, duration, kind)
```

`kind`: `0` = time lock, `1` = vesting.

### Vouchers (optional)

V2 factories (non-Base) and X-Escrow support `create*WithVoucher` / `createWithVoucher` with a signed `FeeVoucher` (token + creator bound, nonce, deadline, discount bps) from the voucher signer. Domain for escrow vouchers is `("XEscrow","1")` so X-Lock vouchers do not replay there. Most agents can ignore vouchers and pay the flat fee.

### Example (viem, Base time lock, USDC fee)

```ts
import { createWalletClient, createPublicClient, http, parseAbi, parseUnits } from "viem";
import { base } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";

const FACTORY = "0xD47326d2B5822355D9c4dfF5265849a5bb5D20ec" as const;
const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" as const;
const abi = parseAbi([
  "function approve(address spender, uint256 amount) returns (bool)",
  "function createTimeLock(address token, address beneficiary, uint256 amount, uint64 unlockTime, uint8 feeMode, uint256 maxFee) returns (uint256 lockId, address wallet)",
  "event LockCreated(uint256 indexed lockId, address indexed wallet, address indexed token, address creator, address beneficiary, uint256 amount, uint64 start, uint64 cliffDuration, uint64 duration, uint8 kind)",
]);

const account = privateKeyToAccount(process.env.AGENT_PK as `0x${string}`);
const wallet = createWalletClient({ account, chain: base, transport: http() });
const publicClient = createPublicClient({ chain: base, transport: http() });

const token = "0x…" as `0x${string}`; // token to lock
const amount = parseUnits("1000000", 18);
const fee = parseUnits("10", 6);
const unlockTime = BigInt(Math.floor(Date.now() / 1000) + 90 * 24 * 3600);

await wallet.writeContract({ address: token, abi, functionName: "approve", args: [FACTORY, amount] });
await wallet.writeContract({ address: USDC, abi, functionName: "approve", args: [FACTORY, fee] });
const hash = await wallet.writeContract({
  address: FACTORY, abi, functionName: "createTimeLock",
  args: [token, account.address, amount, unlockTime, 0, fee],
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
// parse LockCreated → lockId; share https://xlock.liquidlogicx.com/lock/{lockId}
```

Release (beneficiary only, after unlock / as vested): call `release(token)` on the per-lock **wallet** from LockCreated (OpenZeppelin VestingWallet).

---

## X-Escrow (X-Crow)

Non-custodial escrow: buyer deposits; funds end once via release, verifier release, refund after deadline, or cancel with seller signature. Owner cannot move escrowed funds.

| | |
|---|---|
| **All ten mainnets (live, launch caps)** | Base, Arbitrum, Optimism, BNB, Polygon, Avalanche, Ethereum, Arc, Tempo, Robinhood Chain. Same factory address on every chain: `0xa01454C9102052a00dd9B7a5a2eDBA159E93e385` (verified on Basescan) |
| **Base Sepolia (testnet)** | Factory `0x8247E07B2e821a5f6E6260e74EA3daB0d6867BC3` |
| Fee | 2 USDC on create (cap 50), paid by buyer **on top of** `amount` |
| Cap until audit | Per escrow: 2,500 USDC, 1.0 ETH or 10,000,000 LLX; other tokens not escrowable yet. Not independently audited: start small |
| UI | https://xlock.liquidlogicx.com/escrow (all ten mainnets with launch caps; pick the network in the form, or `?chain=base-sepolia` for testnet) |
| Status pages | `https://xlock.liquidlogicx.com/escrow/{chain}/{id}` e.g. `/escrow/base/1` or `/escrow/base-sepolia/1` |

### Methods

| Method | Who | Effect |
|---|---|---|
| `create(seller, token, amount, deadline, verifier, feeMode, maxFee)` | buyer | Fund escrow → `escrowId` |
| `createWithVoucher(...)` | buyer | Same + fee voucher |
| `release(escrowId)` | buyer | Pay seller |
| `releaseWithSig(escrowId, sigDeadline, buyerSig)` | anyone (relayer) | Pay seller with buyer EIP-712 sig |
| `releaseByVerifier(escrowId)` | anyone | Pay seller if `IEscrowVerifier.isSatisfied(id)` before deadline |
| `refund(escrowId)` | anyone | After `deadline`, return to buyer |
| `cancel(escrowId, sigDeadline, sellerSig)` | buyer | Seller-signed cancel → buyer |

### EIP-712 domain

- Name: `XEscrow`, version: `1`, `chainId` + verifying contract = factory.

**Release** (buyer signs):

```text
Release(uint256 escrowId,address buyer,address seller,address token,uint256 amount,uint64 sigDeadline)
```

**Cancel** (seller signs):

```text
Cancel(uint256 escrowId,uint64 sigDeadline)
```

### IEscrowVerifier

```solidity
interface IEscrowVerifier {
  function isSatisfied(uint256 escrowId) external view returns (bool);
}
```

Called via STATICCALL (100k gas). Anything other than success + `true` = not satisfied. A broken verifier cannot block `refund` after the deadline.

### escrow-status (x402, $0.005 USDC on Base)

```
GET https://audit.liquidlogicx.com/api/escrow-status?chain=base-sepolia&id=1
GET https://audit.liquidlogicx.com/api/escrow-status?address=0x…   # buyer or seller
```

Same payTo/asset/price as lock-status. Provide **exactly one** of `id` (+ recommended `chain`) or `address`.

Errors returned after payment is attached (not charged; an unpaid request gets 402 first): `BAD_QUERY`, `BAD_ID`, `BAD_ADDRESS`, `BAD_CHAIN`, `CHAIN_NOT_LIVE`; `RPC_UNAVAILABLE` → 503, not charged.

Live chain keys today: `base` and `base-sepolia`. Other mainnet keys are reserved (`bnb`, `ethereum`, …) and return `CHAIN_NOT_LIVE` until deploy.

---

## Safety

- Non-custodial. No admin path moves locked or escrowed funds.
- Never `approve` unlimited (`type(uint256).max`) to a factory or unknown spender — approve the exact amount (+ fee).
- Never lock or escrow to an address the agent does not control as beneficiary / seller / buyer as intended.
- Prefer quoting `quoteFee(feeMode)` and setting `maxFee` accordingly.
- Test X-Escrow on Base Sepolia first; on mainnet keep escrows small (launch caps apply, no paid audit yet).

---

## Contact

hello@liquidlogicx.com

Official addresses: https://liquidlogicx.com/addresses  
This skill: https://xlock.liquidlogicx.com/skill.md  
Product index: https://liquidlogicx.com/skill.md
