@interest-protocol/blast-core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aborts.d.ts +13 -0
- package/dist/aborts.d.ts.map +1 -0
- package/dist/aborts.js +32 -0
- package/dist/aborts.js.map +1 -0
- package/dist/bcs.d.ts +226 -7
- package/dist/bcs.d.ts.map +1 -1
- package/dist/bcs.js +227 -1
- package/dist/bcs.js.map +1 -1
- package/dist/bps.d.ts +25 -0
- package/dist/bps.d.ts.map +1 -0
- package/dist/bps.js +64 -0
- package/dist/bps.js.map +1 -0
- package/dist/bytecode-template.d.ts +14 -3
- package/dist/bytecode-template.d.ts.map +1 -1
- package/dist/bytecode-template.js +61 -6
- package/dist/bytecode-template.js.map +1 -1
- package/dist/deployment.d.ts +59 -0
- package/dist/deployment.d.ts.map +1 -0
- package/dist/deployment.js +172 -0
- package/dist/deployment.js.map +1 -0
- package/dist/effects.d.ts +69 -0
- package/dist/effects.d.ts.map +1 -0
- package/dist/effects.js +143 -0
- package/dist/effects.js.map +1 -0
- package/dist/fee-split.d.ts +55 -0
- package/dist/fee-split.d.ts.map +1 -0
- package/dist/fee-split.js +69 -0
- package/dist/fee-split.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/integers.d.ts +2 -0
- package/dist/integers.d.ts.map +1 -1
- package/dist/integers.js +8 -0
- package/dist/integers.js.map +1 -1
- package/dist/merkle.d.ts +6 -1
- package/dist/merkle.d.ts.map +1 -1
- package/dist/merkle.js +8 -1
- package/dist/merkle.js.map +1 -1
- package/dist/migration-math.d.ts +13 -2
- package/dist/migration-math.d.ts.map +1 -1
- package/dist/migration-math.js +34 -6
- package/dist/migration-math.js.map +1 -1
- package/dist/migration.d.ts +25 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +43 -0
- package/dist/migration.js.map +1 -0
- package/dist/payout.d.ts +21 -0
- package/dist/payout.d.ts.map +1 -0
- package/dist/payout.js +36 -0
- package/dist/payout.js.map +1 -0
- package/dist/protocol-fees.d.ts +63 -0
- package/dist/protocol-fees.d.ts.map +1 -0
- package/dist/protocol-fees.js +117 -0
- package/dist/protocol-fees.js.map +1 -0
- package/dist/rewards/allocations.d.ts +16 -17
- package/dist/rewards/allocations.d.ts.map +1 -1
- package/dist/rewards/allocations.js +47 -43
- package/dist/rewards/allocations.js.map +1 -1
- package/dist/roles.d.ts +17 -0
- package/dist/roles.d.ts.map +1 -0
- package/dist/roles.js +32 -0
- package/dist/roles.js.map +1 -0
- package/dist/treasury.d.ts +58 -0
- package/dist/treasury.d.ts.map +1 -0
- package/dist/treasury.js +61 -0
- package/dist/treasury.js.map +1 -0
- package/package.json +39 -11
- package/src/aborts.ts +38 -0
- package/src/bcs.ts +374 -1
- package/src/bps.ts +92 -0
- package/src/bytecode-template.ts +78 -6
- package/src/deployment.ts +230 -0
- package/src/effects.ts +247 -0
- package/src/fee-split.ts +103 -0
- package/src/index.ts +10 -1
- package/src/integers.ts +15 -0
- package/src/merkle.ts +11 -1
- package/src/migration-math.ts +38 -6
- package/src/migration.ts +69 -0
- package/src/payout.ts +57 -0
- package/src/protocol-fees.ts +174 -0
- package/src/rewards/allocations.ts +52 -67
- package/src/roles.ts +39 -0
- package/src/treasury.ts +94 -0
- package/dist/rewards/keeper.d.ts +0 -17
- package/dist/rewards/keeper.d.ts.map +0 -1
- package/dist/rewards/keeper.js +0 -21
- package/dist/rewards/keeper.js.map +0 -1
- package/dist/rewards/state.d.ts +0 -55
- package/dist/rewards/state.d.ts.map +0 -1
- package/dist/rewards/state.js +0 -80
- package/dist/rewards/state.js.map +0 -1
- package/dist/timelock.d.ts +0 -15
- package/dist/timelock.d.ts.map +0 -1
- package/dist/timelock.js +0 -30
- package/dist/timelock.js.map +0 -1
- package/src/rewards/keeper.ts +0 -45
- package/src/rewards/state.ts +0 -113
- package/src/timelock.ts +0 -47
package/src/bcs.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BcsType } from "@mysten/sui/bcs";
|
|
1
2
|
import { bcs } from "@mysten/sui/bcs";
|
|
2
3
|
import {
|
|
3
4
|
normalizeStructTag,
|
|
@@ -18,11 +19,22 @@ const asciiStringBcs = bcs.struct("AsciiString", {
|
|
|
18
19
|
bytes: bcs.vector(bcs.u8()),
|
|
19
20
|
});
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* `std::type_name::TypeName`. Move stores the type with every address as 64
|
|
24
|
+
* hex digits and no `0x`; the SDK reads and writes normalized struct tags, so
|
|
25
|
+
* `serialize(parse(bytes))` returns the Move bytes.
|
|
26
|
+
*/
|
|
21
27
|
export const moveTypeNameBcs = bcs
|
|
22
28
|
.struct("TypeName", { name: asciiStringBcs })
|
|
23
29
|
.transform({
|
|
24
30
|
input: (value: string) => ({
|
|
25
|
-
name: {
|
|
31
|
+
name: {
|
|
32
|
+
bytes: Array.from(
|
|
33
|
+
new TextEncoder().encode(
|
|
34
|
+
normalizeStructTag(value).replace(/0x([0-9a-f]{64})/g, "$1"),
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
},
|
|
26
38
|
}),
|
|
27
39
|
output: (value) =>
|
|
28
40
|
normalizeStructTag(
|
|
@@ -35,7 +47,368 @@ export const u64Bcs = bcs.u64().transform({
|
|
|
35
47
|
output: (value) => BigInt(value),
|
|
36
48
|
});
|
|
37
49
|
|
|
50
|
+
/** A `Balance<T>` decoded as its atomic amount. */
|
|
51
|
+
export const balanceBcs = bcs.struct("Balance", { value: u64Bcs }).transform({
|
|
52
|
+
input: (value: bigint) => ({ value }),
|
|
53
|
+
output: (balance) => balance.value,
|
|
54
|
+
});
|
|
55
|
+
|
|
38
56
|
export const addressBcs = bcs.Address.transform({
|
|
39
57
|
input: (value: string) => normalizeSuiAddress(value),
|
|
40
58
|
output: (value) => normalizeSuiAddress(value),
|
|
41
59
|
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A zero-field Move key such as `TradeFeesKey()`: Move gives it one
|
|
63
|
+
* `dummy_field: bool`, so it encodes as one byte.
|
|
64
|
+
*/
|
|
65
|
+
export const unitKeyBcs = bcs
|
|
66
|
+
.struct("Key", { dummy_field: bcs.bool() })
|
|
67
|
+
.transform({
|
|
68
|
+
input: (_: null) => ({ dummy_field: false }),
|
|
69
|
+
output: (): null => null,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/** `0x2::vec_set::VecSet<E>`: its `contents` vector, decoded as the members. */
|
|
73
|
+
export function vecSetBcs<E, Input>(element: BcsType<E, Input>) {
|
|
74
|
+
return bcs.struct("VecSet", { contents: bcs.vector(element) }).transform({
|
|
75
|
+
input: (members: Input[]) => ({ contents: members }),
|
|
76
|
+
output: (set): E[] => set.contents,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** `0x2::table::Table<K, V>`: its UID and size; entries are dynamic fields of `id`. */
|
|
81
|
+
export const tableBcs = bcs.struct("Table", {
|
|
82
|
+
id: suiObjectIdBcs,
|
|
83
|
+
size: u64Bcs,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
/** `blast_admin::delayed::Proposal<T>`: the value and the delay in force once it applies. */
|
|
87
|
+
export type Proposal<T> = { value: T; delay_ms: bigint; ready_at_ms: bigint };
|
|
88
|
+
|
|
89
|
+
export function proposalBcs<T, Input>(value: BcsType<T, Input>) {
|
|
90
|
+
return bcs.struct("Proposal", {
|
|
91
|
+
value,
|
|
92
|
+
delay_ms: u64Bcs,
|
|
93
|
+
ready_at_ms: u64Bcs,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** `blast_admin::delayed::Delayed<K, T>`: readiness never applies a value implicitly. */
|
|
98
|
+
export type Delayed<K, T> = {
|
|
99
|
+
parent: string;
|
|
100
|
+
key: K;
|
|
101
|
+
value: T;
|
|
102
|
+
delay_ms: bigint;
|
|
103
|
+
pending: Proposal<T> | null;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** Exact `blast_admin::delayed::Delayed<K, T>` field order. */
|
|
107
|
+
export function delayedBcs<K, KeyInput, T, Input>(
|
|
108
|
+
key: BcsType<K, KeyInput>,
|
|
109
|
+
value: BcsType<T, Input>,
|
|
110
|
+
) {
|
|
111
|
+
return bcs.struct("Delayed", {
|
|
112
|
+
parent: suiObjectIdBcs,
|
|
113
|
+
key,
|
|
114
|
+
value,
|
|
115
|
+
delay_ms: u64Bcs,
|
|
116
|
+
pending: bcs.option(proposalBcs(value)),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** `blast_admin::delayed_action::Change<A>`: an action, or a change of delay only. */
|
|
121
|
+
export type DelayedChange<A> =
|
|
122
|
+
| { kind: "Action"; action: A }
|
|
123
|
+
| { kind: "Delay" };
|
|
124
|
+
|
|
125
|
+
export function delayedChangeBcs<A, Input>(action: BcsType<A, Input>) {
|
|
126
|
+
return bcs.enum("Change", { Action: action, Delay: null }).transform({
|
|
127
|
+
input: (change: DelayedChange<Input>) =>
|
|
128
|
+
change.kind === "Action"
|
|
129
|
+
? { Action: change.action }
|
|
130
|
+
: { Delay: true as const },
|
|
131
|
+
output: (change): DelayedChange<A> =>
|
|
132
|
+
change.$kind === "Action"
|
|
133
|
+
? { kind: "Action", action: change.Action as A }
|
|
134
|
+
: { kind: "Delay" },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** `blast_admin::delayed_action::DelayedAction<K, A>`: a one-shot action slot. */
|
|
139
|
+
export type DelayedAction<K, A> = {
|
|
140
|
+
parent: string;
|
|
141
|
+
key: K;
|
|
142
|
+
delay_ms: bigint;
|
|
143
|
+
pending: Proposal<DelayedChange<A>> | null;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export function delayedActionBcs<K, KeyInput, A, Input>(
|
|
147
|
+
key: BcsType<K, KeyInput>,
|
|
148
|
+
action: BcsType<A, Input>,
|
|
149
|
+
) {
|
|
150
|
+
return bcs.struct("DelayedAction", {
|
|
151
|
+
parent: suiObjectIdBcs,
|
|
152
|
+
key,
|
|
153
|
+
delay_ms: u64Bcs,
|
|
154
|
+
pending: bcs.option(proposalBcs(delayedChangeBcs(action))),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** `blast_admin::delayed::PendingDelay`: a proposed next delay, ready from `ready_at_ms`. */
|
|
159
|
+
export type PendingDelay = { delay_ms: bigint; ready_at_ms: bigint };
|
|
160
|
+
|
|
161
|
+
export const pendingDelayBcs = bcs.struct("PendingDelay", {
|
|
162
|
+
delay_ms: u64Bcs,
|
|
163
|
+
ready_at_ms: u64Bcs,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* `blast_admin::delayed::Deadline`: a pending admission's or entry change's
|
|
168
|
+
* fixed deadline and the applied delay it was scheduled under; a later delay
|
|
169
|
+
* change never moves it.
|
|
170
|
+
*/
|
|
171
|
+
export type Deadline = { delay_ms: bigint; ready_at_ms: bigint };
|
|
172
|
+
|
|
173
|
+
export const deadlineBcs = bcs.struct("Deadline", {
|
|
174
|
+
delay_ms: u64Bcs,
|
|
175
|
+
ready_at_ms: u64Bcs,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
/** `blast_admin::delayed_set::DelayedSet<K, E>`: admissions wait; removals are immediate. */
|
|
179
|
+
export type DelayedSet<K, E> = {
|
|
180
|
+
parent: string;
|
|
181
|
+
key: K;
|
|
182
|
+
members: E[];
|
|
183
|
+
delay_ms: bigint;
|
|
184
|
+
admissions: { member: E; deadline: Deadline }[];
|
|
185
|
+
pending_delay: PendingDelay | null;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* `blast_admin::delayed::max_delay_ms`: no delayed value, action, set or map
|
|
190
|
+
* accepts a longer delay, in milliseconds (30 days); one aborts with
|
|
191
|
+
* `blast_admin` error 15 (`EInvalidDelay`).
|
|
192
|
+
*/
|
|
193
|
+
export const MAX_DELAY_MS = 2_592_000_000n;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* `delayed::is_guarded`: the applied delay is nonzero and no proposal would
|
|
197
|
+
* set it to zero. `package_admin::unpause` requires every policy guarded.
|
|
198
|
+
*/
|
|
199
|
+
export function isDelayedGuarded(self: Delayed<unknown, unknown>): boolean {
|
|
200
|
+
return self.delay_ms > 0n && (self.pending?.delay_ms ?? 1n) > 0n;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** `delayed_action::is_guarded`, as `isDelayedGuarded`. */
|
|
204
|
+
export function isDelayedActionGuarded(
|
|
205
|
+
self: DelayedAction<unknown, unknown>,
|
|
206
|
+
): boolean {
|
|
207
|
+
return self.delay_ms > 0n && (self.pending?.delay_ms ?? 1n) > 0n;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** `delayed_set::is_guarded`: also no admission scheduled earlier is pending. */
|
|
211
|
+
export function isDelayedSetGuarded(
|
|
212
|
+
self: DelayedSet<unknown, unknown>,
|
|
213
|
+
): boolean {
|
|
214
|
+
return (
|
|
215
|
+
self.delay_ms > 0n &&
|
|
216
|
+
self.admissions.length === 0 &&
|
|
217
|
+
(self.pending_delay?.delay_ms ?? 1n) > 0n
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function delayedSetBcs<K, KeyInput, E, Input>(
|
|
222
|
+
key: BcsType<K, KeyInput>,
|
|
223
|
+
member: BcsType<E, Input>,
|
|
224
|
+
) {
|
|
225
|
+
return bcs
|
|
226
|
+
.struct("DelayedSet", {
|
|
227
|
+
parent: suiObjectIdBcs,
|
|
228
|
+
key,
|
|
229
|
+
members: bcs.struct("VecSet", { contents: bcs.vector(member) }),
|
|
230
|
+
delay_ms: u64Bcs,
|
|
231
|
+
admissions: bcs.struct("VecMap", {
|
|
232
|
+
contents: bcs.vector(
|
|
233
|
+
bcs.struct("Entry", { key: member, value: deadlineBcs }),
|
|
234
|
+
),
|
|
235
|
+
}),
|
|
236
|
+
pending_delay: bcs.option(pendingDelayBcs),
|
|
237
|
+
})
|
|
238
|
+
.transform({
|
|
239
|
+
input: (set: {
|
|
240
|
+
parent: string;
|
|
241
|
+
key: KeyInput;
|
|
242
|
+
members: Input[];
|
|
243
|
+
delay_ms: bigint;
|
|
244
|
+
admissions: { member: Input; deadline: Deadline }[];
|
|
245
|
+
pending_delay: PendingDelay | null;
|
|
246
|
+
}) => ({
|
|
247
|
+
parent: set.parent,
|
|
248
|
+
key: set.key,
|
|
249
|
+
members: { contents: set.members },
|
|
250
|
+
delay_ms: set.delay_ms,
|
|
251
|
+
admissions: {
|
|
252
|
+
contents: set.admissions.map(({ member, deadline }) => ({
|
|
253
|
+
key: member,
|
|
254
|
+
value: deadline,
|
|
255
|
+
})),
|
|
256
|
+
},
|
|
257
|
+
pending_delay: set.pending_delay,
|
|
258
|
+
}),
|
|
259
|
+
output: (set): DelayedSet<K, E> => ({
|
|
260
|
+
parent: set.parent,
|
|
261
|
+
key: set.key,
|
|
262
|
+
members: set.members.contents,
|
|
263
|
+
delay_ms: set.delay_ms,
|
|
264
|
+
admissions: set.admissions.contents.map(({ key, value }) => ({
|
|
265
|
+
member: key,
|
|
266
|
+
deadline: value,
|
|
267
|
+
})),
|
|
268
|
+
pending_delay: set.pending_delay,
|
|
269
|
+
}),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** `blast_admin::delayed_set::SetChange<E>`: an admission, or a delay change. */
|
|
274
|
+
export type DelayedSetChange<E> =
|
|
275
|
+
| { kind: "Admission"; member: E }
|
|
276
|
+
| { kind: "Delay" };
|
|
277
|
+
|
|
278
|
+
export function delayedSetChangeBcs<E, Input>(member: BcsType<E, Input>) {
|
|
279
|
+
return bcs.enum("SetChange", { Admission: member, Delay: null }).transform({
|
|
280
|
+
input: (change: DelayedSetChange<Input>) =>
|
|
281
|
+
change.kind === "Admission"
|
|
282
|
+
? { Admission: change.member }
|
|
283
|
+
: { Delay: true as const },
|
|
284
|
+
output: (change): DelayedSetChange<E> =>
|
|
285
|
+
change.$kind === "Admission"
|
|
286
|
+
? { kind: "Admission", member: change.Admission as E }
|
|
287
|
+
: { kind: "Delay" },
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** `blast_admin::delayed_map::EntryProposal<V>`: a value, or null to clear, and its fixed deadline. */
|
|
292
|
+
export type EntryProposal<V> = { value: V | null; deadline: Deadline };
|
|
293
|
+
|
|
294
|
+
export function entryProposalBcs<V, Input>(value: BcsType<V, Input>) {
|
|
295
|
+
return bcs.struct("EntryProposal", {
|
|
296
|
+
value: bcs.option(value),
|
|
297
|
+
deadline: deadlineBcs,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** `blast_admin::delayed_map::MapChange<E, V>`: an entry change, or a delay change. */
|
|
302
|
+
export type DelayedMapChange<E, V> =
|
|
303
|
+
| { kind: "Entry"; entry: E; value: V | null }
|
|
304
|
+
| { kind: "Delay" };
|
|
305
|
+
|
|
306
|
+
export function delayedMapChangeBcs<E, EntryInput, V, Input>(
|
|
307
|
+
entry: BcsType<E, EntryInput>,
|
|
308
|
+
value: BcsType<V, Input>,
|
|
309
|
+
) {
|
|
310
|
+
return bcs
|
|
311
|
+
.enum("MapChange", {
|
|
312
|
+
Entry: bcs.struct("Entry", { entry, value: bcs.option(value) }),
|
|
313
|
+
Delay: null,
|
|
314
|
+
})
|
|
315
|
+
.transform({
|
|
316
|
+
input: (change: DelayedMapChange<EntryInput, Input>) =>
|
|
317
|
+
change.kind === "Entry"
|
|
318
|
+
? { Entry: { entry: change.entry, value: change.value } }
|
|
319
|
+
: { Delay: true as const },
|
|
320
|
+
output: (change): DelayedMapChange<E, V> =>
|
|
321
|
+
change.$kind === "Entry"
|
|
322
|
+
? {
|
|
323
|
+
kind: "Entry",
|
|
324
|
+
entry: change.Entry.entry as E,
|
|
325
|
+
value: change.Entry.value as V | null,
|
|
326
|
+
}
|
|
327
|
+
: { kind: "Delay" },
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* `blast_admin::delayed_map::DelayedMap<K, E, V>`: applied entries in a
|
|
333
|
+
* `Table` (read by dynamic field), one delay, and inline pending changes.
|
|
334
|
+
*/
|
|
335
|
+
export type DelayedMap<K, E, V> = {
|
|
336
|
+
parent: string;
|
|
337
|
+
key: K;
|
|
338
|
+
entries: { id: string; size: bigint };
|
|
339
|
+
delay_ms: bigint;
|
|
340
|
+
pending: { entry: E; proposal: EntryProposal<V> }[];
|
|
341
|
+
pending_delay: PendingDelay | null;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
export function delayedMapBcs<K, KeyInput, E, EntryInput, V, Input>(
|
|
345
|
+
key: BcsType<K, KeyInput>,
|
|
346
|
+
entry: BcsType<E, EntryInput>,
|
|
347
|
+
value: BcsType<V, Input>,
|
|
348
|
+
) {
|
|
349
|
+
return bcs
|
|
350
|
+
.struct("DelayedMap", {
|
|
351
|
+
parent: suiObjectIdBcs,
|
|
352
|
+
key,
|
|
353
|
+
entries: tableBcs,
|
|
354
|
+
delay_ms: u64Bcs,
|
|
355
|
+
pending: bcs.struct("VecMap", {
|
|
356
|
+
contents: bcs.vector(
|
|
357
|
+
bcs.struct("Entry", { key: entry, value: entryProposalBcs(value) }),
|
|
358
|
+
),
|
|
359
|
+
}),
|
|
360
|
+
pending_delay: bcs.option(pendingDelayBcs),
|
|
361
|
+
})
|
|
362
|
+
.transform({
|
|
363
|
+
input: (map: {
|
|
364
|
+
parent: string;
|
|
365
|
+
key: KeyInput;
|
|
366
|
+
entries: { id: string; size: bigint };
|
|
367
|
+
delay_ms: bigint;
|
|
368
|
+
pending: {
|
|
369
|
+
entry: EntryInput;
|
|
370
|
+
proposal: { value: Input | null; deadline: Deadline };
|
|
371
|
+
}[];
|
|
372
|
+
pending_delay: PendingDelay | null;
|
|
373
|
+
}) => ({
|
|
374
|
+
parent: map.parent,
|
|
375
|
+
key: map.key,
|
|
376
|
+
entries: map.entries,
|
|
377
|
+
delay_ms: map.delay_ms,
|
|
378
|
+
pending: {
|
|
379
|
+
contents: map.pending.map(({ entry, proposal }) => ({
|
|
380
|
+
key: entry,
|
|
381
|
+
value: proposal,
|
|
382
|
+
})),
|
|
383
|
+
},
|
|
384
|
+
pending_delay: map.pending_delay,
|
|
385
|
+
}),
|
|
386
|
+
output: (map): DelayedMap<K, E, V> => ({
|
|
387
|
+
parent: map.parent,
|
|
388
|
+
key: map.key,
|
|
389
|
+
entries: map.entries,
|
|
390
|
+
delay_ms: map.delay_ms,
|
|
391
|
+
pending: map.pending.contents.map(({ key, value }) => ({
|
|
392
|
+
entry: key,
|
|
393
|
+
proposal: value as EntryProposal<V>,
|
|
394
|
+
})),
|
|
395
|
+
pending_delay: map.pending_delay,
|
|
396
|
+
}),
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* A `DelayedSet` admission view: the admitted members, the applied delay, the
|
|
402
|
+
* proposed next delay, and each scheduled admission's deadline by member. Used
|
|
403
|
+
* for venue, witness and claim-signer allowlists alike.
|
|
404
|
+
*/
|
|
405
|
+
export function admission<E extends string>(set: DelayedSet<unknown, E>) {
|
|
406
|
+
return {
|
|
407
|
+
members: [...set.members],
|
|
408
|
+
delayMs: set.delay_ms,
|
|
409
|
+
pendingDelay: set.pending_delay,
|
|
410
|
+
pendingMembers: Object.fromEntries(
|
|
411
|
+
set.admissions.map(({ member, deadline }) => [member, deadline]),
|
|
412
|
+
) as Readonly<Record<E, Deadline>>,
|
|
413
|
+
};
|
|
414
|
+
}
|
package/src/bps.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { bcs } from "@mysten/sui/bcs";
|
|
2
|
+
import { u64Bcs } from "./bcs.ts";
|
|
3
|
+
import type { BasisPoints } from "./integers.ts";
|
|
4
|
+
import {
|
|
5
|
+
BASIS_POINTS_DENOMINATOR,
|
|
6
|
+
mulDivUp,
|
|
7
|
+
requireBasisPoints,
|
|
8
|
+
requireU64,
|
|
9
|
+
} from "./integers.ts";
|
|
10
|
+
|
|
11
|
+
/** `blast_core::constants::max_lp_fee_bps`: Blast's largest LP-fee share of each asset. */
|
|
12
|
+
export const MAX_LP_FEE_BPS = 3_000n;
|
|
13
|
+
/** `blast_core::constants::max_routes`: the most routes one fee split holds. */
|
|
14
|
+
export const MAX_ROUTES = 20;
|
|
15
|
+
/** `blast_core::constants::max_vesting_duration_ms`: the technical bound on a schedule. */
|
|
16
|
+
export const MAX_VESTING_DURATION_MS = 2n ** 63n;
|
|
17
|
+
|
|
18
|
+
/** BCS layout of the positional `blast_core::bps::Bps(u64)`. */
|
|
19
|
+
export const bpsBcs = bcs.struct("Bps", { value: u64Bcs }).transform({
|
|
20
|
+
input: (value: bigint) => ({ value: requireBps(value) }),
|
|
21
|
+
output: (value) => value.value,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/** Mirrors `bps::new`: a rate from zero through 10,000 basis points. */
|
|
25
|
+
export function requireBps(value: bigint): BasisPoints {
|
|
26
|
+
return requireBasisPoints("bps", value);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Mirrors `bps::apply_up`: `ceil(amount * bps / 10,000)` over atomic units,
|
|
31
|
+
* at most `amount`.
|
|
32
|
+
*/
|
|
33
|
+
export function applyBpsUp(amount: bigint, bps: bigint): bigint {
|
|
34
|
+
return mulDivUp(
|
|
35
|
+
requireU64("amount", amount),
|
|
36
|
+
requireBps(bps),
|
|
37
|
+
BASIS_POINTS_DENOMINATOR,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Mirrors `bps::apportion`: largest-remainder apportionment of `total` atoms
|
|
43
|
+
* over `rates`, which must sum to exactly 10,000. The `roundedUp` part takes
|
|
44
|
+
* its ceiling and every other part its floor; the leftover atoms go one each
|
|
45
|
+
* to the largest remaining remainders, earlier parts first. The amounts sum
|
|
46
|
+
* to `total`. Throws where the contract aborts (`EInvalidApportionment`).
|
|
47
|
+
*/
|
|
48
|
+
export function apportionBps(
|
|
49
|
+
rates: readonly bigint[],
|
|
50
|
+
total: bigint,
|
|
51
|
+
roundedUp: number | null,
|
|
52
|
+
): bigint[] {
|
|
53
|
+
requireU64("total", total);
|
|
54
|
+
rates.forEach(requireBps);
|
|
55
|
+
if (
|
|
56
|
+
rates.reduce((sum, rate) => sum + rate, 0n) !== BASIS_POINTS_DENOMINATOR ||
|
|
57
|
+
(roundedUp !== null &&
|
|
58
|
+
!(
|
|
59
|
+
Number.isInteger(roundedUp) &&
|
|
60
|
+
roundedUp >= 0 &&
|
|
61
|
+
roundedUp < rates.length
|
|
62
|
+
))
|
|
63
|
+
) {
|
|
64
|
+
throw new RangeError(
|
|
65
|
+
"apportioned rates must sum to 10,000 and name a rounded-up part inside them",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const products = rates.map((rate) => total * rate);
|
|
69
|
+
const amounts = products.map((product, index) =>
|
|
70
|
+
index === roundedUp
|
|
71
|
+
? (product + BASIS_POINTS_DENOMINATOR - 1n) / BASIS_POINTS_DENOMINATOR
|
|
72
|
+
: product / BASIS_POINTS_DENOMINATOR,
|
|
73
|
+
);
|
|
74
|
+
const order = products
|
|
75
|
+
.map((product, index) => ({
|
|
76
|
+
index,
|
|
77
|
+
remainder: product % BASIS_POINTS_DENOMINATOR,
|
|
78
|
+
}))
|
|
79
|
+
.filter(({ index }) => index !== roundedUp)
|
|
80
|
+
.sort((left, right) =>
|
|
81
|
+
left.remainder === right.remainder
|
|
82
|
+
? left.index - right.index
|
|
83
|
+
: left.remainder > right.remainder
|
|
84
|
+
? -1
|
|
85
|
+
: 1,
|
|
86
|
+
);
|
|
87
|
+
const leftover = Number(total - amounts.reduce((sum, a) => sum + a, 0n));
|
|
88
|
+
for (const { index } of order.slice(0, leftover)) {
|
|
89
|
+
amounts[index] = (amounts[index] ?? 0n) + 1n;
|
|
90
|
+
}
|
|
91
|
+
return amounts;
|
|
92
|
+
}
|
package/src/bytecode-template.ts
CHANGED
|
@@ -2,6 +2,12 @@ import initBytecodeTemplate, {
|
|
|
2
2
|
deserialize,
|
|
3
3
|
} from "@mysten/move-bytecode-template";
|
|
4
4
|
import { bcs } from "@mysten/sui/bcs";
|
|
5
|
+
import {
|
|
6
|
+
isValidSuiObjectId,
|
|
7
|
+
MOVE_STDLIB_ADDRESS,
|
|
8
|
+
normalizeSuiObjectId,
|
|
9
|
+
SUI_FRAMEWORK_ADDRESS,
|
|
10
|
+
} from "@mysten/sui/utils";
|
|
5
11
|
|
|
6
12
|
// Shared by every generated-package template: one WASM initialization, module
|
|
7
13
|
// table checks, and constant patching by type and default value.
|
|
@@ -32,6 +38,12 @@ export type BytecodeTemplateInitOptions = {
|
|
|
32
38
|
module_or_path?: unknown;
|
|
33
39
|
};
|
|
34
40
|
|
|
41
|
+
const SYSTEM_ADDRESSES = new Set([
|
|
42
|
+
normalizeSuiObjectId("0x0"),
|
|
43
|
+
MOVE_STDLIB_ADDRESS,
|
|
44
|
+
SUI_FRAMEWORK_ADDRESS,
|
|
45
|
+
]);
|
|
46
|
+
|
|
35
47
|
let initialization: Promise<void> | undefined;
|
|
36
48
|
|
|
37
49
|
/**
|
|
@@ -54,6 +66,66 @@ export function initializeBytecodeTemplate(
|
|
|
54
66
|
return initialization;
|
|
55
67
|
}
|
|
56
68
|
|
|
69
|
+
/** A deployed package's original ID that a generated package links: valid and not a system address. */
|
|
70
|
+
export function requireDependencyPackageId(
|
|
71
|
+
name: string,
|
|
72
|
+
packageId: string,
|
|
73
|
+
): string {
|
|
74
|
+
if (typeof packageId !== "string") {
|
|
75
|
+
throw new TypeError(`${name} must be a string`);
|
|
76
|
+
}
|
|
77
|
+
const normalized = normalizeSuiObjectId(packageId);
|
|
78
|
+
if (!isValidSuiObjectId(normalized)) {
|
|
79
|
+
throw new TypeError(`${name} must be a valid Sui package ID`);
|
|
80
|
+
}
|
|
81
|
+
if (SYSTEM_ADDRESSES.has(normalized)) {
|
|
82
|
+
throw new RangeError(`${name} must identify a deployed Blast package`);
|
|
83
|
+
}
|
|
84
|
+
return normalized;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Binds every non-system address of the template to the deployed package
|
|
89
|
+
* whose modules it names: `bindings` maps a module name to that package's
|
|
90
|
+
* original ID. Each placeholder address must be claimed by exactly one
|
|
91
|
+
* package through the modules it holds, and each binding must claim one, so
|
|
92
|
+
* a template that gains or loses a dependency fails here instead of linking
|
|
93
|
+
* a placeholder.
|
|
94
|
+
*/
|
|
95
|
+
export function patchDependencyAddresses(
|
|
96
|
+
label: string,
|
|
97
|
+
compiled: CompiledModule,
|
|
98
|
+
bindings: Readonly<Record<string, string>>,
|
|
99
|
+
): void {
|
|
100
|
+
const claimed = new Set<string>();
|
|
101
|
+
compiled.address_identifiers.forEach((address, index) => {
|
|
102
|
+
if (SYSTEM_ADDRESSES.has(normalizeSuiObjectId(address))) return;
|
|
103
|
+
const packageIds = new Set(
|
|
104
|
+
compiled.module_handles.flatMap((handle) => {
|
|
105
|
+
const module = compiled.identifiers[handle.name];
|
|
106
|
+
const packageId =
|
|
107
|
+
handle.address === index && module !== undefined
|
|
108
|
+
? bindings[module]
|
|
109
|
+
: undefined;
|
|
110
|
+
if (packageId !== undefined && module !== undefined) {
|
|
111
|
+
claimed.add(module);
|
|
112
|
+
}
|
|
113
|
+
return packageId === undefined ? [] : [packageId];
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
if (packageIds.size !== 1) {
|
|
117
|
+
throw new TypeError(`${label} has an unbound dependency address`);
|
|
118
|
+
}
|
|
119
|
+
const [packageId] = packageIds as Set<string>;
|
|
120
|
+
compiled.address_identifiers[index] = (packageId as string).slice(2);
|
|
121
|
+
});
|
|
122
|
+
for (const module of Object.keys(bindings)) {
|
|
123
|
+
if (!claimed.has(module)) {
|
|
124
|
+
throw new TypeError(`${label} does not link ${module}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
57
129
|
/** Deserializes a template module and checks the tables patching reads. */
|
|
58
130
|
export function deserializeTemplate(
|
|
59
131
|
label: string,
|
|
@@ -192,20 +264,20 @@ export function assertIdentifierSlots(
|
|
|
192
264
|
}
|
|
193
265
|
|
|
194
266
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
267
|
+
* Defaults the module to the lowercased symbol, with spaces becoming underscores.
|
|
268
|
+
* Sui requires its one-time witness to be the uppercase module name, including
|
|
269
|
+
* when the caller overrides the module name.
|
|
198
270
|
*/
|
|
199
271
|
export function deriveTemplateIdentifiers(
|
|
200
272
|
symbol: string,
|
|
201
273
|
requestedModuleName?: string,
|
|
202
274
|
): { moduleName: string; witnessName: string } {
|
|
203
275
|
const normalizedSymbol = requireNonEmptyString("symbol", symbol);
|
|
204
|
-
const
|
|
276
|
+
const symbolIdentifier = normalizedSymbol.toUpperCase().replaceAll(" ", "_");
|
|
205
277
|
const moduleName =
|
|
206
278
|
requestedModuleName ?? normalizedSymbol.toLowerCase().replaceAll(" ", "_");
|
|
207
279
|
|
|
208
|
-
if (!/^[A-Z][A-Z0-9_]*$/.test(
|
|
280
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(symbolIdentifier)) {
|
|
209
281
|
throw new RangeError(
|
|
210
282
|
"symbol must produce an uppercase Move witness identifier",
|
|
211
283
|
);
|
|
@@ -215,7 +287,7 @@ export function deriveTemplateIdentifiers(
|
|
|
215
287
|
"moduleName must start with a lowercase letter and contain only lowercase letters, digits, or underscores",
|
|
216
288
|
);
|
|
217
289
|
}
|
|
218
|
-
return { moduleName, witnessName };
|
|
290
|
+
return { moduleName, witnessName: moduleName.toUpperCase() };
|
|
219
291
|
}
|
|
220
292
|
|
|
221
293
|
export function requireString(name: string, value: string): string {
|