kinlock-contracts
One Soroban contract, kinlock, with two modules: registry (who's a verified
payee) and vault (where the money actually sits). This is the only place funds exist outside
a user's own wallet.
Entry points
- Admin
init,add_attester/remove_attester,add_token/remove_token,set_paused_new_locks,set_caps,upgrade- Registry
register_payee,set_status,update_payout(affects new locks only)- Vault
create_lock,release,refund,decline,bump_lock(permissionless), reads
Rules that don't bend
release: caller must belock.payout; payee must be Active;unlock_at ≤ now < expires_at.refund(sender):now ≥ expires_at, or payee Revoked, or payee Suspended past a 14-day grace.decline(payee): any time while the lock is Open; returns the remainder to the sender.- No fee, no
SenderApprovalmode, no mutual cancel, no payout timelock. - State is written before every token transfer; a failed transfer reverts everything.
Invariants (property-tested)
Ten invariants, checked by proptest over random action sequences, not just unit tests:
released + returned ≤ total; equality once the lock reaches a terminal state.- Tranches sum to the lock's total; each tranche's
unlock_at ≤ expires_at. - Funds exit only to
lock.payoutorlock.sender. No third path. - A tranche is released at most once.
- Release only happens inside its time window, called by
lock.payout. - Refund only on expiry, Revoked status, or Suspended-past-grace.
lock.payoutis immutable once the lock is created.- Admin, attester, pause, and allowlist actions never block release, decline, or refund on existing locks.
total_lockedequals the sum of remainders across every Open lock.- A failed token transfer reverts all state changes from that call.
On testnet today
- Contract
CCSHDQFRYFC3AHV5NE6ULQW6X2CMG5RPANBORDXJGSUD6UKECASJQBRI- Admin
- 2-of-3 multisig (testnet keys only)
- Token
- Circle testnet USDC, allowlisted
- Test coverage
- 103 unit / auth / event / TTL tests, plus property tests over 512 random action sequences
Full record: DEPLOYMENTS.md.
Stack
Rust, #![no_std], soroban-sdk. No floating point, no std collections, no unwrap/expect/bare panic! in contract code. USDC via the Stellar Asset Contract (SEP-41), 7 decimals, i128 checked arithmetic throughout.