Skip to content

Commit 96c723b

Browse files
authored
feat: lockup wallet page (#973)
1 parent db02928 commit 96c723b

File tree

4 files changed

+185
-2
lines changed

4 files changed

+185
-2
lines changed

docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@
287287
}
288288
]
289289
},
290+
"standard/wallets/lockup",
290291
{
291292
"group": "Preprocessed Wallet V2",
292293
"pages": [

standard/wallets/highload/overview.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Highload Wallets — overview"
2+
title: "Highload wallets"
33
sidebarTitle: "Overview"
44
---
55

standard/wallets/lockup.mdx

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
---
2+
title: "Lockup wallet"
3+
---
4+
5+
import { Aside } from '/snippets/aside.jsx';
6+
7+
Lockup wallet is a specialized wallet contract that locks funds until a specified time. The [repository](https://github.com/ton-blockchain/lockup-wallet-contract) contains two implementations with different unlocking mechanisms.
8+
9+
## Universal Lockup Wallet
10+
11+
[Universal Lockup Wallet](https://github.com/ton-blockchain/lockup-wallet-contract/tree/c2c9f73394853780621e6215410a95475ac7cf4f/universal) implements time-based fund locking with allowlist functionality. All funds unlock simultaneously when the time restrictions expire.
12+
13+
Source code: [`universal/uni-lockup-wallet.fc`](https://github.com/ton-blockchain/lockup-wallet-contract/blob/c2c9f73394853780621e6215410a95475ac7cf4f/universal/uni-lockup-wallet.fc)
14+
15+
### Use cases
16+
17+
Escrow services. Lock funds until conditions are met, with allowlist of valid recipients.
18+
19+
### Persistent memory layout
20+
21+
```tlb
22+
storage$_
23+
seqno:uint32
24+
subwallet_id:uint32
25+
public_key:uint256
26+
config_public_key:uint256
27+
allowed_destinations:(PfxHashmapE 267 ^Cell)
28+
total_locked_value:Coins
29+
locked:HashmapE 32 Coins
30+
total_restricted_value:Coins
31+
restricted:HashmapE 32 Coins
32+
= Storage;
33+
```
34+
35+
- `seqno`: 32-bit sequence number for replay protection.
36+
- `subwallet_id`: 32-bit wallet identifier.
37+
- `public_key`: 256-bit Ed25519 public key for signing external messages (wallet operations).
38+
- `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.
39+
- `allowed_destinations`: Prefix dictionary of allowlisted destination addresses (uses `pfxdict_get?` for prefix matching).
40+
- `total_locked_value`: Total amount of locked funds (unrestricted destinations).
41+
- `locked`: Dictionary mapping unlock timestamps to locked amounts.
42+
- `total_restricted_value`: Total amount of restricted funds (allowlist-only).
43+
- `restricted`: Dictionary mapping unlock timestamps to restricted amounts.
44+
45+
### Message layout
46+
47+
#### External message body layout
48+
49+
- `signature`: 512-bit Ed25519 signature.
50+
- `subwallet_id`: 32-bit subwallet identifier.
51+
- `valid_until`: 32-bit Unix timestamp.
52+
- `msg_seqno`: 32-bit sequence number.
53+
- Message list: References to messages to send.
54+
55+
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).
56+
57+
#### Internal message body layout
58+
59+
Internal messages with `op = 0x82eaf9c4` (`rwallet_op`) allow adding locked funds:
60+
61+
```tlb
62+
rwallet_op#82eaf9c4
63+
signature:(## 512)
64+
cmd:(## 32)
65+
only_restrict:(## 1)
66+
timestamp:(## 32)
67+
= InternalMsgBody;
68+
```
69+
70+
Message requirements:
71+
72+
- Must carry ≥1 TON value.
73+
- Contain valid signature from `config_public_key`.
74+
- `cmd` must be `0x373aa9f4` (`restricted_transfer`).
75+
- `only_restrict`: Flag determining lock type: `1` for restricted funds, `0` for locked funds.
76+
- `timestamp`: Unix timestamp for unlock.
77+
78+
<Aside type="note">
79+
Internal messages with other opcodes from allowlisted addresses are silently ignored.
80+
</Aside>
81+
82+
### Get methods
83+
84+
1. `int seqno()` returns current sequence number.
85+
1. `int wallet_id()` returns current subwallet ID.
86+
1. `int get_public_key()` returns stored public key.
87+
1. `(int, int, int) get_balances_at(int time)` returns balance, restricted value, and locked value at specified time.
88+
1. `(int, int, int) get_balances()` returns current balance, restricted value, and locked value.
89+
1. `int check_destination(slice destination)` returns whether destination address is allowlisted.
90+
91+
<Aside type="note">
92+
There is no get-method for `config_public_key`. This is by design — the configuration key is only used internally for adding locked funds.
93+
</Aside>
94+
95+
### Exit codes
96+
97+
| Exit code | Description |
98+
| --------- | ------------------------------------------------- |
99+
| 31 | Signature verification failed (`wrong_signature`) |
100+
| 32 | Config signature verification failed |
101+
| 33 | Message value too small (\< 1 TON) |
102+
| 34 | Sequence number mismatch (`wrong_seqno`) |
103+
| 35 | Subwallet ID mismatch (`wrong_subwallet_id`) |
104+
| 36 | Message expired (`replay_protection`) |
105+
| 40 | Unknown operation code (`unknown_op`) |
106+
| 41 | Unknown command (`unknown_cmd`) |
107+
108+
## Vesting Wallet
109+
110+
[Vesting Wallet](https://github.com/ton-blockchain/lockup-wallet-contract/tree/c2c9f73394853780621e6215410a95475ac7cf4f/vesting) implements gradual fund unlocking over time with an optional cliff period. The funds unlock linearly according to a vesting schedule.
111+
112+
Available through [web interface](https://toncenter.github.io/lockup-sender). [Source code](https://github.com/ton-blockchain/lockup-wallet-contract/blob/c2c9f73394853780621e6215410a95475ac7cf4f/vesting/vesting-lockup-wallet.fc).
113+
114+
### Use cases
115+
116+
Employee token vesting. Lock employee tokens with vesting schedule (e.g., 4 years with 1-year cliff).
117+
118+
### Persistent memory layout
119+
120+
```tlb
121+
storage$_
122+
stored_seqno:uint32
123+
stored_subwallet:uint32
124+
public_key:uint256
125+
start_time:uint64
126+
total_duration:uint32
127+
unlock_period:uint32
128+
cliff_duration:uint32
129+
total_amount:Coins
130+
allow_elector:Bool
131+
= Storage;
132+
```
133+
134+
- `stored_seqno`: 32-bit sequence number (replay protection).
135+
- `stored_subwallet`: 32-bit wallet identifier.
136+
- `public_key`: 256-bit Ed25519 public key for signing external messages.
137+
- `start_time`: 64-bit Unix timestamp when vesting begins.
138+
- `total_duration`: 32-bit total vesting duration in seconds.
139+
- `unlock_period`: 32-bit period between unlocks in seconds.
140+
- `cliff_duration`: 32-bit cliff period before first unlock.
141+
- `total_amount`: Total amount subject to vesting.
142+
- `allow_elector`: Boolean flag that bypasses vesting restrictions for transfers to [Elector](/foundations/system#elector) and [Config](/foundations/system#config) contracts.
143+
144+
### Message layout
145+
146+
#### External message body layout
147+
148+
- `signature`: 512-bit Ed25519 signature.
149+
- `subwallet_id`: 32-bit subwallet identifier.
150+
- `valid_until`: 32-bit Unix timestamp.
151+
- `msg_seqno`: 32-bit sequence number.
152+
- Optional: One message reference. If present, mode MUST be 3 (pay fees separately, ignore errors).
153+
154+
The contract calculates locked amount based on vesting schedule:
155+
156+
- Before `start_time + cliff_duration`: All funds locked.
157+
- During vesting: Linear unlock based on `unlock_period`.
158+
- After `start_time + total_duration`: All funds unlocked.
159+
160+
When `allow_elector` is enabled, vesting restrictions are bypassed for transfers to system contracts (see `allow_elector` field description above).
161+
162+
#### Internal message body layout
163+
164+
Internal messages are ignored (no operations performed).
165+
166+
### Get methods
167+
168+
1. `int seqno()` returns current sequence number.
169+
1. `int get_public_key()` returns stored public key.
170+
1. `int get_locked_amount(int now_time)` returns locked amount at specified time.
171+
1. `(int, int, int, int, int, int) get_lockup_data()` returns `(start_time, total_duration, unlock_period, cliff_duration, total_amount, allow_elector)`.
172+
173+
### Exit codes
174+
175+
| Exit code | Description |
176+
| --------- | ------------------------------------------------ |
177+
| 33 | Sequence number mismatch |
178+
| 34 | Subwallet ID mismatch |
179+
| 35 | Signature verification failed |
180+
| 36 | Message expired (`valid_until` check failed) |
181+
| 37 | Invalid number of message references |
182+
| 38 | Invalid send mode (must be 3 if message present) |

standard/wallets/restricted.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Restricted Wallet"
2+
title: "Restricted wallet"
33
---
44

55
import { Aside } from '/snippets/aside.jsx';

0 commit comments

Comments
 (0)