Skip to main content
Lockup wallet is a specialized wallet contract that locks funds until a specified time. The repository contains two implementations with different unlocking mechanisms.

Universal Lockup Wallet

Universal Lockup Wallet implements time-based fund locking with allowlist functionality. All funds unlock simultaneously when the time restrictions expire. Source code: universal/uni-lockup-wallet.fc

Use cases

Escrow services. Lock funds until conditions are met, with allowlist of valid recipients.

Persistent memory layout

  • seqno: 32-bit sequence number for replay protection.
  • subwallet_id: 32-bit wallet identifier.
  • public_key: 256-bit Ed25519 public key for signing external messages (wallet operations).
  • config_public_key: 256-bit Ed25519 public key for signing internal messages that add locked funds. This separation allows a third party to initialize and fund the lockup wallet without having access to spend the funds.
  • allowed_destinations: Prefix dictionary of allowlisted destination addresses (uses pfxdict_get? for prefix matching).
  • total_locked_value: Total amount of locked funds (unrestricted destinations).
  • locked: Dictionary mapping unlock timestamps to locked amounts.
  • total_restricted_value: Total amount of restricted funds (allowlist-only).
  • restricted: Dictionary mapping unlock timestamps to restricted amounts.

Message layout

External message body layout

  • signature: 512-bit Ed25519 signature.
  • subwallet_id: 32-bit subwallet identifier.
  • valid_until: 32-bit Unix timestamp.
  • msg_seqno: 32-bit sequence number.
  • Message list: References to messages to send.
The contract unlocks expired funds, reserves locked amounts using raw_reserve(effectively_locked, 2), and sends messages. Each message is sent with its specified mode, but if mode is not 2, it’s forced to mode 3 (pay fees separately, ignore errors).

Internal message body layout

Internal messages with op = 0x82eaf9c4 (rwallet_op) allow adding locked funds:
Message requirements:
  • Must carry ≥1 TON value.
  • Contain valid signature from config_public_key.
  • cmd must be 0x373aa9f4 (restricted_transfer).
  • only_restrict: Flag determining lock type: 1 for restricted funds, 0 for locked funds.
  • timestamp: Unix timestamp for unlock.

Get methods

  1. int seqno() returns current sequence number.
  2. int wallet_id() returns current subwallet ID.
  3. int get_public_key() returns stored public key.
  4. (int, int, int) get_balances_at(int time) returns balance, restricted value, and locked value at specified time.
  5. (int, int, int) get_balances() returns current balance, restricted value, and locked value.
  6. int check_destination(slice destination) returns whether destination address is allowlisted.

Exit codes

Vesting Wallet

Vesting Wallet implements gradual fund unlocking over time with an optional cliff period. The funds unlock linearly according to a vesting schedule. Available through web interface. Source code.

Use cases

Employee token vesting. Lock employee tokens with vesting schedule (e.g., 4 years with 1-year cliff).

Persistent memory layout

  • stored_seqno: 32-bit sequence number (replay protection).
  • stored_subwallet: 32-bit wallet identifier.
  • public_key: 256-bit Ed25519 public key for signing external messages.
  • start_time: 64-bit Unix timestamp when vesting begins.
  • total_duration: 32-bit total vesting duration in seconds.
  • unlock_period: 32-bit period between unlocks in seconds.
  • cliff_duration: 32-bit cliff period before first unlock.
  • total_amount: Total amount subject to vesting.
  • allow_elector: Boolean flag that bypasses vesting restrictions for transfers to Elector and Config contracts.

Message layout

External message body layout

  • signature: 512-bit Ed25519 signature.
  • subwallet_id: 32-bit subwallet identifier.
  • valid_until: 32-bit Unix timestamp.
  • msg_seqno: 32-bit sequence number.
  • Optional: One message reference. If present, mode MUST be 3 (pay fees separately, ignore errors).
The contract calculates locked amount based on vesting schedule:
  • Before start_time + cliff_duration: All funds locked.
  • During vesting: Linear unlock based on unlock_period.
  • After start_time + total_duration: All funds unlocked.
When allow_elector is enabled, vesting restrictions are bypassed for transfers to system contracts (see allow_elector field description above).

Internal message body layout

Internal messages are ignored (no operations performed).

Get methods

  1. int seqno() returns current sequence number.
  2. int get_public_key() returns stored public key.
  3. int get_locked_amount(int now_time) returns locked amount at specified time.
  4. (int, int, int, int, int, int) get_lockup_data() returns (start_time, total_duration, unlock_period, cliff_duration, total_amount, allow_elector).

Exit codes