-
Notifications
You must be signed in to change notification settings - Fork 118
Expand file tree
/
Copy pathsettlement.json
More file actions
548 lines (530 loc) · 21.6 KB
/
Copy pathsettlement.json
File metadata and controls
548 lines (530 loc) · 21.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
{
"contract": "callora-settlement",
"version": "0.2.0",
"description": "Advanced settlement contract with per-developer balance tracking. Receives USDC forwarded by the vault and credits either a shared global pool or an individual developer's balance.",
"crate": "callora-settlement",
"source": "contracts/settlement/src/lib.rs",
"errors": {
"description": "Typed error codes emitted via env.panic_with_error(). Callers and indexers match on the u32 code rather than parsing raw strings.",
"variants": [
{ "code": 1, "name": "NotInitialized", "when": "A function is called before init." },
{ "code": 2, "name": "AlreadyInitialized", "when": "init is called more than once." },
{ "code": 3, "name": "Unauthorized", "when": "Caller is not the registered vault or admin." },
{ "code": 4, "name": "AmountNotPositive", "when": "amount is zero or negative." },
{ "code": 5, "name": "DeveloperRequired", "when": "to_pool=false but no developer address was supplied." },
{ "code": 6, "name": "DeveloperMustBeNone", "when": "to_pool=true but a developer address was given." },
{ "code": 7, "name": "PoolOverflow", "when": "Global pool i128 addition would overflow." },
{ "code": 8, "name": "DeveloperOverflow", "when": "Developer balance i128 addition would overflow." },
{ "code": 9, "name": "UsdcTokenNotConfigured", "when": "USDC token address not configured for withdrawals." },
{ "code": 10, "name": "InsufficientDeveloperBalance", "when": "Developer balance is less than withdrawal amount." },
{ "code": 11, "name": "DeveloperBalanceUnderflow", "when": "Developer balance subtraction would overflow." },
{ "code": 12, "name": "InsufficientContractBalance", "when": "Settlement contract lacks on-ledger USDC." },
{ "code": 13, "name": "DailyWithdrawCapExceeded", "when": "Developer's daily withdrawal cap would be exceeded." },
{ "code": 14, "name": "GasExhaustionRisk", "when": "Index exceeds 100 entries; use get_developer_balances_cursor instead of get_all_developer_balances." },
{ "code": 15, "name": "ReasonTooLong", "when": "Reason Symbol exceeds maximum allowed length." },
{ "code": 16, "name": "InvalidClaimWindow", "when": "Claim window end timestamp is before the start timestamp." },
{ "code": 17, "name": "ClaimWindowClosed", "when": "A developer attempts to claim outside their configured claim window." }
]
},
"types": {
"DeveloperBalance": {
"description": "Snapshot of a single developer's tracked balance.",
"fields": {
"address": {
"type": "Address",
"description": "Developer Stellar address."
},
"balance": {
"type": "i128",
"description": "Credited balance in USDC base units (stroops)."
}
}
},
"PendingDeveloperMigration": {
"description": "Timelocked snapshot of an approved developer balance migration.",
"fields": {
"from": { "type": "Address" },
"to": { "type": "Address" },
"amount": { "type": "i128" },
"proposed_at": { "type": "u64" },
"execute_after": { "type": "u64" }
}
},
"AdminMigrationEvent": {
"description": "Audit payload emitted when a migration executes.",
"fields": {
"from": { "type": "Address" },
"to": { "type": "Address" },
"amount": { "type": "i128" },
"executed_at": { "type": "u64" }
}
},
"GlobalPool": {
"description": "Aggregate pool state.",
"fields": {
"total_balance": {
"type": "i128",
"description": "Total USDC credited to the global pool."
},
"last_updated": {
"type": "u64",
"description": "Ledger timestamp of the last pool update."
}
}
},
"PaymentReceivedEvent": {
"description": "Event payload emitted on every receive_payment call.",
"fields": {
"from_vault": {
"type": "Address",
"description": "Address of the caller (typically the vault)."
},
"amount": {
"type": "i128",
"description": "Payment amount in USDC base units."
},
"to_pool": {
"type": "bool",
"description": "True when credited to the global pool; false when credited to a developer."
},
"developer": {
"type": "Address | null",
"description": "Developer address when to_pool=false; null otherwise."
}
}
},
"BalanceCreditedEvent": {
"description": "Event payload emitted when a developer balance is increased (to_pool=false).",
"fields": {
"developer": {
"type": "Address",
"description": "Developer address that was credited."
},
"amount": { "type": "i128", "description": "Amount credited." },
"new_balance": {
"type": "i128",
"description": "Developer's balance after crediting."
}
}
},
"DeveloperClaimWindow": {
"description": "Inclusive ledger timestamp range during which a developer may claim accrued balance.",
"fields": {
"start_ts": { "type": "u64", "description": "First ledger timestamp at which claims are allowed." },
"end_ts": { "type": "u64", "description": "Last ledger timestamp at which claims are allowed." }
}
},
"DeveloperClaimWindowChanged": {
"description": "Event payload emitted when an admin sets or clears a developer claim window.",
"fields": {
"developer": { "type": "Address", "description": "Developer whose claim window changed." },
"start_ts": { "type": "u64", "description": "Configured start timestamp, or 0 when cleared." },
"end_ts": { "type": "u64", "description": "Configured end timestamp, or 0 when cleared." },
"enabled": { "type": "bool", "description": "True when a window is set; false when cleared." }
}
}
},
"functions": [
{
"name": "init",
"description": "Initialize the settlement contract. Can only be called once. Sets admin, registers the vault address, creates an empty developer balance map, and initializes the global pool.",
"access": "any (no auth requirement on init itself; admin address is stored)",
"params": [
{
"name": "admin",
"type": "Address",
"optional": false,
"description": "Address that may call set_admin and set_vault."
},
{
"name": "vault_address",
"type": "Address",
"optional": false,
"description": "Vault contract address permitted to call receive_payment."
}
],
"returns": "void",
"errors": [
{
"code": 2,
"name": "AlreadyInitialized",
"when": "Called more than once."
}
],
"events": []
},
{
"name": "receive_payment",
"description": "Receive a payment from the vault and credit the global pool or a specific developer. The caller must be the registered vault or the admin.",
"access": "registered vault OR admin",
"params": [
{
"name": "caller",
"type": "Address",
"optional": false,
"description": "Must be the registered vault or admin; must authorize."
},
{
"name": "amount",
"type": "i128",
"optional": false,
"description": "Payment amount in USDC base units; must be > 0."
},
{
"name": "to_pool",
"type": "bool",
"optional": false,
"description": "If true, credit the global pool. If false, credit the specified developer."
},
{
"name": "developer",
"type": "Address | null",
"optional": true,
"description": "Required when to_pool=false; the developer to credit. Ignored when to_pool=true."
}
],
"returns": "void",
"errors": [
{
"code": 3,
"name": "Unauthorized",
"when": "Caller is neither the vault nor the admin."
},
{ "code": 4, "name": "AmountNotPositive", "when": "amount <= 0." },
{
"code": 5,
"name": "DeveloperRequired",
"when": "to_pool=false and developer is null."
},
{
"code": 6,
"name": "DeveloperMustBeNone",
"when": "to_pool=true and developer is not null."
},
{
"code": 7,
"name": "PoolOverflow",
"when": "Global pool i128 addition would overflow."
},
{
"code": 8,
"name": "DeveloperOverflow",
"when": "Developer balance i128 addition would overflow."
}
],
"events": [
{
"always": true,
"topics": ["\"payment_received\"", "caller"],
"data": "PaymentReceivedEvent"
},
{
"condition": "to_pool=false",
"topics": ["\"balance_credited\"", "developer"],
"data": "BalanceCreditedEvent"
}
]
},
{
"name": "get_developer_balance",
"description": "Return the tracked USDC balance for a developer. Returns 0 if the developer has never received a payment.",
"access": "any",
"params": [
{
"name": "developer",
"type": "Address",
"optional": false,
"description": "Developer address to query."
}
],
"returns": "i128",
"errors": [
{ "code": 1, "name": "NotInitialized", "when": "Called before init." }
],
"events": []
},
{
"name": "withdraw_developer_balance",
"description": "Developer-authorized claim of accrued balance. If a claim window is configured for the developer, the current ledger timestamp must be within the inclusive [start_ts, end_ts] range.",
"access": "developer",
"params": [
{ "name": "developer", "type": "Address", "optional": false, "description": "Developer address; must authorize." },
{ "name": "amount", "type": "i128", "optional": false, "description": "Amount to withdraw; must be > 0 and <= tracked developer balance." },
{ "name": "to", "type": "Address | null", "optional": true, "description": "Recipient address. Null defaults to developer." }
],
"returns": "Result<void, SettlementError>",
"errors": [
{ "code": 4, "name": "AmountNotPositive", "when": "amount <= 0." },
{ "code": 9, "name": "UsdcTokenNotConfigured", "when": "USDC token address is not configured." },
{ "code": 10, "name": "InsufficientDeveloperBalance", "when": "Developer tracked balance is below amount." },
{ "code": 11, "name": "DeveloperBalanceUnderflow", "when": "Balance subtraction would underflow." },
{ "code": 12, "name": "InsufficientContractBalance", "when": "Settlement contract lacks on-ledger USDC." },
{ "code": 13, "name": "DailyWithdrawCapExceeded", "when": "Withdrawal would exceed the developer's daily cap." },
{ "code": 17, "name": "ClaimWindowClosed", "when": "Current ledger timestamp is outside the developer's configured claim window." }
],
"events": [
{ "topics": ["\"developer_withdraw\"", "developer"], "data": "DeveloperWithdrawEvent" }
]
},
{
"name": "set_developer_claim_window",
"description": "Configure an inclusive claim window for a developer. Developers without a configured window remain claimable at any time.",
"access": "admin",
"params": [
{ "name": "caller", "type": "Address", "optional": false, "description": "Must be the current admin; must authorize." },
{ "name": "developer", "type": "Address", "optional": false, "description": "Developer whose claim window is being configured." },
{ "name": "start_ts", "type": "u64", "optional": false, "description": "First allowed ledger timestamp." },
{ "name": "end_ts", "type": "u64", "optional": false, "description": "Last allowed ledger timestamp." }
],
"returns": "Result<void, SettlementError>",
"errors": [
{ "code": 3, "name": "Unauthorized", "when": "Caller is not the current admin." },
{ "code": 16, "name": "InvalidClaimWindow", "when": "end_ts < start_ts." }
],
"events": [
{ "topics": ["\"claim_window_changed\"", "developer"], "data": "DeveloperClaimWindowChanged { enabled: true }" }
]
},
{
"name": "clear_developer_claim_window",
"description": "Remove a developer's claim window and restore unrestricted developer claims.",
"access": "admin",
"params": [
{ "name": "caller", "type": "Address", "optional": false, "description": "Must be the current admin; must authorize." },
{ "name": "developer", "type": "Address", "optional": false, "description": "Developer whose claim window is being cleared." }
],
"returns": "Result<void, SettlementError>",
"errors": [
{ "code": 3, "name": "Unauthorized", "when": "Caller is not the current admin." }
],
"events": [
{ "topics": ["\"claim_window_changed\"", "developer"], "data": "DeveloperClaimWindowChanged { enabled: false }" }
]
},
{
"name": "get_developer_claim_window",
"description": "Return the configured claim window for a developer, or null when claims are unrestricted.",
"access": "any",
"params": [
{ "name": "developer", "type": "Address", "optional": false, "description": "Developer address to query." }
],
"returns": "DeveloperClaimWindow | null",
"errors": [],
"events": []
},
{
"name": "get_all_developer_balances",
"description": "Return a list of all developer balances. Admin only. Rejects the call with GasExhaustionRisk when the developer index exceeds 100 entries — use get_developer_balances_cursor for larger sets. The index is maintained in deterministic ascending order by address bytes.",
"access": "admin",
"params": [
{
"name": "caller",
"type": "Address",
"optional": false,
"description": "Must be the current admin."
}
],
"returns": "Vec<DeveloperBalance>",
"errors": [
{ "code": 1, "name": "NotInitialized", "when": "Called before init." },
{ "code": 3, "name": "Unauthorized", "when": "Caller is not the admin." },
{ "code": 14, "name": "GasExhaustionRisk", "when": "Developer index has more than 100 entries." }
],
"events": []
},
{
"name": "get_developer_balances_cursor",
"description": "Cursor-based paginated developer balances (admin only). Returns up to `limit` DeveloperBalance records starting after the supplied `cursor` address (exclusive), or from the beginning of the sorted index when `cursor` is null. The DeveloperIndex is maintained in deterministic ascending order by address bytes, so pages are stable across interleaved receive_payment calls for developers that sort after the cursor. The limit is capped at 100 (MAX_DEVELOPER_BALANCES_PAGE_SIZE).",
"access": "admin",
"params": [
{
"name": "caller",
"type": "Address",
"optional": false,
"description": "Must be the current admin; must authorize."
},
{
"name": "cursor",
"type": "Address | null",
"optional": true,
"description": "Exclusive start position. Pass null for the first page; pass the next_cursor value returned by the previous call for subsequent pages."
},
{
"name": "limit",
"type": "u32",
"optional": false,
"description": "Maximum records to return per page; silently capped at 100."
}
],
"returns": {
"type": "(Vec<DeveloperBalance>, Address | null)",
"description": "A tuple of (page, next_cursor). `page` contains up to `limit` DeveloperBalance records. `next_cursor` is the address of the last record on the page when more records may exist, or null when this is the final page."
},
"errors": [
{ "code": 1, "name": "NotInitialized", "when": "Called before init." },
{
"code": 3,
"name": "Unauthorized",
"when": "Caller is not the admin."
}
],
"events": [],
"notes": [
"Iterating all pages: call with cursor=null, then pass the returned next_cursor on each subsequent call until next_cursor is null.",
"Cursor stability: credits to developers that sort after the current cursor do not affect previously returned pages.",
"limit=0 returns an empty page and null next_cursor immediately.",
"A cursor pointing past the last entry returns an empty page and null next_cursor."
]
},
{
"name": "get_global_pool",
"description": "Return the global pool state (total balance and timestamp of last update).",
"access": "any",
"params": [],
"returns": "GlobalPool",
"errors": [
{ "code": 1, "name": "NotInitialized", "when": "Called before init." }
],
"events": []
},
{
"name": "get_admin",
"description": "Return the current admin address.",
"access": "any",
"params": [],
"returns": "Address",
"errors": [
{ "code": 1, "name": "NotInitialized", "when": "Called before init." }
],
"events": []
},
{
"name": "get_pending_admin",
"description": "Return the pending admin address, or null if no two-step transfer is in progress. Integrators can poll this to detect an in-flight admin handover before accept_admin is called.",
"access": "any",
"params": [],
"returns": "Address | null",
"panics": [],
"events": []
},
{
"name": "get_vault",
"description": "Return the registered vault contract address.",
"access": "any",
"params": [],
"returns": "Address",
"errors": [
{ "code": 1, "name": "NotInitialized", "when": "Called before init." }
],
"events": []
},
{
"name": "set_admin",
"description": "Nominate a new admin. The nominee must call accept_admin to finalize.",
"access": "admin",
"params": [
{
"name": "caller",
"type": "Address",
"optional": false,
"description": "Must be the current admin; must authorize."
},
{
"name": "new_admin",
"type": "Address",
"optional": false,
"description": "Proposed new admin address."
}
],
"returns": "void",
"errors": [
{
"code": 3,
"name": "Unauthorized",
"when": "Caller is not the current admin."
}
],
"events": [
{
"topics": ["\"admin_nominated\"", "current_admin", "new_admin"],
"data": "void"
}
]
},
{
"name": "accept_admin",
"description": "Finalize the admin transfer. Must be called by the pending admin.",
"access": "pending admin (must sign)",
"params": [],
"returns": "void",
"panics": [
"\"no admin transfer pending\" — set_admin was not called first."
],
"events": [
{
"topics": ["\"admin_accepted\"", "old_admin", "new_admin"],
"data": "void"
}
]
},
{
"name": "propose_balance_migration",
"description": "Snapshot a developer balance and begin the fixed 24-hour migration timelock. Re-proposal replaces the prior target and restarts the delay.",
"access": "admin (must sign; native account multisig thresholds apply)",
"params": [
{ "name": "caller", "type": "Address", "optional": false },
{ "name": "from", "type": "Address", "optional": false },
{ "name": "to", "type": "Address", "optional": false }
],
"returns": "void",
"events": [{ "topics": ["admin_migration_proposed", "from"], "data": "PendingDeveloperMigration" }]
},
{
"name": "execute_balance_migration",
"description": "Move the approved balance snapshot after the timelock and consume the proposal.",
"access": "admin (must sign; native account multisig thresholds apply)",
"params": [
{ "name": "caller", "type": "Address", "optional": false },
{ "name": "from", "type": "Address", "optional": false }
],
"returns": "void",
"events": [{ "topics": ["admin_migration", "from", "to"], "data": "AdminMigrationEvent" }]
},
{
"name": "get_balance_migration",
"description": "Return the pending migration for a source address, or null.",
"access": "any",
"params": [{ "name": "from", "type": "Address", "optional": false }],
"returns": "PendingDeveloperMigration | null",
"events": []
},
{
"name": "set_vault",
"description": "Update the registered vault address. Only admin may call this.",
"access": "admin",
"params": [
{
"name": "caller",
"type": "Address",
"optional": false,
"description": "Must be the current admin; must authorize."
},
{
"name": "new_vault",
"type": "Address",
"optional": false,
"description": "New vault contract address."
}
],
"returns": "void",
"errors": [
{
"code": 3,
"name": "Unauthorized",
"when": "Caller is not the current admin."
}
],
"events": []
}
]
}