@interest-protocol/blast-admin-sdk 0.0.0-stage → 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.
@@ -0,0 +1,439 @@
1
+ import { requireU64 } from "@interest-protocol/blast-core/integers";
2
+ import type { SharedObjectIdentity } from "@interest-protocol/blast-core/sui";
3
+ import { normalizeSharedObjectIdentity } from "@interest-protocol/blast-core/sui";
4
+ import type {
5
+ TransactionArgument,
6
+ TransactionObjectArgument,
7
+ TransactionObjectInput,
8
+ } from "@mysten/sui/transactions";
9
+ import { Transaction } from "@mysten/sui/transactions";
10
+ import {
11
+ normalizeStructTag,
12
+ normalizeSuiAddress,
13
+ normalizeSuiObjectId,
14
+ } from "@mysten/sui/utils";
15
+
16
+ /** The canonical `blast_admin` package (immutability deferred) and its shared singleton `blast_acl::ACL`. */
17
+ export type AdminDeployment = {
18
+ packageId: string;
19
+ acl: SharedObjectIdentity;
20
+ };
21
+
22
+ export function normalizeAdminDeployment(
23
+ deployment: AdminDeployment,
24
+ ): AdminDeployment {
25
+ return {
26
+ packageId: normalizeSuiObjectId(deployment.packageId),
27
+ acl: normalizeSharedObjectIdentity("admin.acl", deployment.acl),
28
+ };
29
+ }
30
+
31
+ /** `blast_admin::package_admin`'s roles, each generic over one package's OTW. */
32
+ export const PACKAGE_ROLES = [
33
+ "UpgradeAdmin",
34
+ "PauseAdmin",
35
+ "UnpauseAdmin",
36
+ "CancelAdmin",
37
+ ] as const;
38
+ export type PackageRole = (typeof PACKAGE_ROLES)[number];
39
+
40
+ /** `<admin>::package_admin::<role><otw>`: one package's infrastructure role. */
41
+ export function packageRoleType(
42
+ admin: Pick<AdminDeployment, "packageId">,
43
+ otw: string,
44
+ role: PackageRole,
45
+ ): string {
46
+ if (!PACKAGE_ROLES.includes(role))
47
+ throw new TypeError(`unknown package_admin role ${String(role)}`);
48
+ return normalizeStructTag(
49
+ `${normalizeSuiObjectId(admin.packageId)}::package_admin::${role}<${normalizeStructTag(otw)}>`,
50
+ );
51
+ }
52
+
53
+ /** One package's shared `PackageVersion<OTW>` or `PackageAdmin<OTW>` and its OTW type. */
54
+ type PackageObjectOptions<Name extends "packageVersion" | "packageAdmin"> = {
55
+ otw: string;
56
+ } & Record<Name, SharedObjectIdentity>;
57
+
58
+ /**
59
+ * `blast_admin::blast_acl` and `blast_admin::package_admin` calls. A product
60
+ * role is a `blast_core::blast_domain` type (`coreRoleType`); a package role
61
+ * is `package_admin::<Role><OTW>` (`packageRoleType`). A product's own
62
+ * `unpause` and `make_immutable` wrappers live with the product.
63
+ */
64
+ export class AdminCalls {
65
+ readonly deployment: AdminDeployment;
66
+
67
+ constructor(deployment: AdminDeployment) {
68
+ this.deployment = normalizeAdminDeployment(deployment);
69
+ }
70
+
71
+ /** `sign_in<Role>`: this PTB's `AdminWitness<Role>`; the `AdminCap` must hold `role`. */
72
+ signIn(options: { adminCap: TransactionObjectInput; role: string }) {
73
+ const role = normalizeStructTag(options.role);
74
+ return (tx: Transaction): TransactionObjectArgument =>
75
+ tx.moveCall({
76
+ target: this.#target("sign_in"),
77
+ typeArguments: [role],
78
+ arguments: [this.#acl(tx, false), tx.object(options.adminCap)],
79
+ });
80
+ }
81
+
82
+ /** `grant_admin`: delivers a new `AdminCap` with no role to `recipient`. */
83
+ grantAdmin(options: {
84
+ superAdminCap: TransactionObjectInput;
85
+ recipient: string;
86
+ }) {
87
+ const recipient = normalizeSuiAddress(options.recipient);
88
+ return (tx: Transaction): void => {
89
+ tx.moveCall({
90
+ target: this.#target("grant_admin"),
91
+ arguments: [
92
+ this.#acl(tx, true),
93
+ tx.object(options.superAdminCap),
94
+ tx.pure.address(recipient),
95
+ ],
96
+ });
97
+ };
98
+ }
99
+
100
+ /** `revoke_admin`: retires a live `AdminCap` and every role it holds. */
101
+ revokeAdmin(options: {
102
+ superAdminCap: TransactionObjectInput;
103
+ adminCapId: string;
104
+ }) {
105
+ const adminCapId = normalizeSuiObjectId(options.adminCapId);
106
+ return (tx: Transaction): void => {
107
+ tx.moveCall({
108
+ target: this.#target("revoke_admin"),
109
+ arguments: [
110
+ this.#acl(tx, true),
111
+ tx.object(options.superAdminCap),
112
+ tx.pure.id(adminCapId),
113
+ ],
114
+ });
115
+ };
116
+ }
117
+
118
+ /** `grant_role<Role>` to a live administrator. */
119
+ grantRole(options: {
120
+ superAdminCap: TransactionObjectInput;
121
+ adminCapId: string;
122
+ role: string;
123
+ }) {
124
+ return this.#roleCall("grant_role", options);
125
+ }
126
+
127
+ /** `revoke_role<Role>` from a live administrator. */
128
+ revokeRole(options: {
129
+ superAdminCap: TransactionObjectInput;
130
+ adminCapId: string;
131
+ role: string;
132
+ }) {
133
+ return this.#roleCall("revoke_role", options);
134
+ }
135
+
136
+ /** `destroy_admin_cap`: deletes the holder's own live or revoked cap. */
137
+ destroyAdminCap(options: { adminCap: TransactionObjectInput }) {
138
+ return (tx: Transaction): void => {
139
+ tx.moveCall({
140
+ target: this.#target("destroy_admin_cap"),
141
+ arguments: [this.#acl(tx, true), tx.object(options.adminCap)],
142
+ });
143
+ };
144
+ }
145
+
146
+ /**
147
+ * `begin_super_admin_transfer`: nominates `recipient`, who may accept once
148
+ * the ACL's seven-day delay passes; it replaces a pending nomination.
149
+ */
150
+ beginSuperAdminTransfer(options: {
151
+ superAdminCap: TransactionObjectInput;
152
+ recipient: string;
153
+ }) {
154
+ const recipient = normalizeSuiAddress(options.recipient);
155
+ return (tx: Transaction): void => {
156
+ tx.moveCall({
157
+ target: this.#target("begin_super_admin_transfer"),
158
+ arguments: [
159
+ this.#acl(tx, true),
160
+ tx.object(options.superAdminCap),
161
+ tx.pure.address(recipient),
162
+ tx.object.clock(),
163
+ ],
164
+ });
165
+ };
166
+ }
167
+
168
+ cancelSuperAdminTransfer(options: { superAdminCap: TransactionObjectInput }) {
169
+ return (tx: Transaction): void => {
170
+ tx.moveCall({
171
+ target: this.#target("cancel_super_admin_transfer"),
172
+ arguments: [this.#acl(tx, true), tx.object(options.superAdminCap)],
173
+ });
174
+ };
175
+ }
176
+
177
+ /** `accept_super_admin_transfer`: the sender must be the ready nominee. */
178
+ acceptSuperAdminTransfer() {
179
+ return (tx: Transaction): void => {
180
+ tx.moveCall({
181
+ target: this.#target("accept_super_admin_transfer"),
182
+ arguments: [this.#acl(tx, true), tx.object.clock()],
183
+ });
184
+ };
185
+ }
186
+
187
+ /** `destroy_retired_super_admin_cap`: deletes a root an accepted transfer replaced. */
188
+ destroyRetiredSuperAdminCap(options: {
189
+ superAdminCap: TransactionObjectInput;
190
+ }) {
191
+ return (tx: Transaction): void => {
192
+ tx.moveCall({
193
+ target: this.#target("destroy_retired_super_admin_cap"),
194
+ arguments: [this.#acl(tx, false), tx.object(options.superAdminCap)],
195
+ });
196
+ };
197
+ }
198
+
199
+ /** `package_admin::pause<OTW>` with a `PauseAdmin<OTW>` witness: sets `bits` now. */
200
+ pause(
201
+ options: PackageObjectOptions<"packageVersion"> & {
202
+ adminWitness: TransactionObjectInput;
203
+ bits: number;
204
+ },
205
+ ) {
206
+ const bits = requirePauseBits(options.bits);
207
+ return (tx: Transaction): void => {
208
+ tx.moveCall({
209
+ target: this.#packageAdminTarget("pause"),
210
+ typeArguments: [normalizeStructTag(options.otw)],
211
+ arguments: [
212
+ tx.sharedObjectRef({ ...options.packageVersion, mutable: true }),
213
+ tx.object(options.adminWitness),
214
+ tx.pure.u8(bits),
215
+ ],
216
+ });
217
+ };
218
+ }
219
+
220
+ /** `package_admin::schedule_upgrade<OTW>` of one 32-byte package digest. */
221
+ scheduleUpgrade(
222
+ options: PackageObjectOptions<"packageAdmin"> & {
223
+ adminWitness: TransactionObjectInput;
224
+ digest: Uint8Array;
225
+ },
226
+ ) {
227
+ if (options.digest.length !== 32)
228
+ throw new RangeError("a package digest is 32 bytes");
229
+ const digest = Array.from(options.digest);
230
+ return this.#upgradeCall(options, "schedule_upgrade", (tx) => [
231
+ tx.object(options.adminWitness),
232
+ tx.pure.vector("u8", digest),
233
+ tx.object.clock(),
234
+ ]);
235
+ }
236
+
237
+ /** `package_admin::schedule_make_immutable<OTW>`. */
238
+ scheduleMakeImmutable(
239
+ options: PackageObjectOptions<"packageAdmin"> & {
240
+ adminWitness: TransactionObjectInput;
241
+ },
242
+ ) {
243
+ return this.#upgradeCall(options, "schedule_make_immutable", (tx) => [
244
+ tx.object(options.adminWitness),
245
+ tx.object.clock(),
246
+ ]);
247
+ }
248
+
249
+ /** `package_admin::cancel_upgrade_action<OTW>` with a `CancelAdmin<OTW>` witness. */
250
+ cancelUpgradeAction(
251
+ options: PackageObjectOptions<"packageAdmin"> & {
252
+ adminWitness: TransactionObjectInput;
253
+ },
254
+ ) {
255
+ return this.#upgradeCall(options, "cancel_upgrade_action", (tx) => [
256
+ tx.object(options.adminWitness),
257
+ ]);
258
+ }
259
+
260
+ /** `package_admin::schedule_upgrade_delay<OTW>`: proposes only a new delay. */
261
+ scheduleUpgradeDelay(
262
+ options: PackageObjectOptions<"packageAdmin"> & {
263
+ adminWitness: TransactionObjectInput;
264
+ delayMs: bigint;
265
+ },
266
+ ) {
267
+ const delayMs = requireU64("delayMs", options.delayMs);
268
+ return this.#upgradeCall(options, "schedule_upgrade_delay", (tx) => [
269
+ tx.object(options.adminWitness),
270
+ tx.pure.u64(delayMs),
271
+ tx.object.clock(),
272
+ ]);
273
+ }
274
+
275
+ /** `package_admin::apply_upgrade_delay<OTW>`: anyone, once ready. */
276
+ applyUpgradeDelay(options: PackageObjectOptions<"packageAdmin">) {
277
+ return this.#upgradeCall(options, "apply_upgrade_delay", (tx) => [
278
+ tx.object.clock(),
279
+ ]);
280
+ }
281
+
282
+ #upgradeCall(
283
+ options: PackageObjectOptions<"packageAdmin">,
284
+ functionName: string,
285
+ rest: (tx: Transaction) => TransactionArgument[],
286
+ ) {
287
+ const otw = normalizeStructTag(options.otw);
288
+ return (tx: Transaction): void => {
289
+ tx.moveCall({
290
+ target: this.#packageAdminTarget(functionName),
291
+ typeArguments: [otw],
292
+ arguments: [
293
+ tx.sharedObjectRef({ ...options.packageAdmin, mutable: true }),
294
+ ...rest(tx),
295
+ ],
296
+ });
297
+ };
298
+ }
299
+
300
+ #packageAdminTarget(functionName: string): string {
301
+ return `${this.deployment.packageId}::package_admin::${functionName}`;
302
+ }
303
+
304
+ #roleCall(
305
+ functionName: "grant_role" | "revoke_role",
306
+ options: {
307
+ superAdminCap: TransactionObjectInput;
308
+ adminCapId: string;
309
+ role: string;
310
+ },
311
+ ) {
312
+ const role = normalizeStructTag(options.role);
313
+ const adminCapId = normalizeSuiObjectId(options.adminCapId);
314
+ return (tx: Transaction): void => {
315
+ tx.moveCall({
316
+ target: this.#target(functionName),
317
+ typeArguments: [role],
318
+ arguments: [
319
+ this.#acl(tx, true),
320
+ tx.object(options.superAdminCap),
321
+ tx.pure.id(adminCapId),
322
+ ],
323
+ });
324
+ };
325
+ }
326
+
327
+ #target(functionName: string): string {
328
+ return `${this.deployment.packageId}::blast_acl::${functionName}`;
329
+ }
330
+
331
+ #acl(tx: Transaction, mutable: boolean): TransactionObjectArgument {
332
+ return tx.sharedObjectRef({ ...this.deployment.acl, mutable });
333
+ }
334
+ }
335
+
336
+ /** One-call PTBs for the ACL's root and administrators. */
337
+ export class AdminTransactions {
338
+ readonly call: AdminCalls;
339
+
340
+ constructor(deployment: AdminDeployment) {
341
+ this.call = new AdminCalls(deployment);
342
+ }
343
+
344
+ grantAdmin(options: { superAdminCapId: string; recipient: string }) {
345
+ return single(
346
+ this.call.grantAdmin({
347
+ superAdminCap: options.superAdminCapId,
348
+ recipient: options.recipient,
349
+ }),
350
+ );
351
+ }
352
+
353
+ revokeAdmin(options: { superAdminCapId: string; adminCapId: string }) {
354
+ return single(
355
+ this.call.revokeAdmin({
356
+ superAdminCap: options.superAdminCapId,
357
+ adminCapId: options.adminCapId,
358
+ }),
359
+ );
360
+ }
361
+
362
+ grantRole(options: {
363
+ superAdminCapId: string;
364
+ adminCapId: string;
365
+ role: string;
366
+ }) {
367
+ return single(
368
+ this.call.grantRole({
369
+ ...options,
370
+ superAdminCap: options.superAdminCapId,
371
+ }),
372
+ );
373
+ }
374
+
375
+ revokeRole(options: {
376
+ superAdminCapId: string;
377
+ adminCapId: string;
378
+ role: string;
379
+ }) {
380
+ return single(
381
+ this.call.revokeRole({
382
+ ...options,
383
+ superAdminCap: options.superAdminCapId,
384
+ }),
385
+ );
386
+ }
387
+
388
+ destroyAdminCap(options: { adminCapId: string }) {
389
+ return single(this.call.destroyAdminCap({ adminCap: options.adminCapId }));
390
+ }
391
+
392
+ beginSuperAdminTransfer(options: {
393
+ superAdminCapId: string;
394
+ recipient: string;
395
+ }) {
396
+ return single(
397
+ this.call.beginSuperAdminTransfer({
398
+ superAdminCap: options.superAdminCapId,
399
+ recipient: options.recipient,
400
+ }),
401
+ );
402
+ }
403
+
404
+ cancelSuperAdminTransfer(options: { superAdminCapId: string }) {
405
+ return single(
406
+ this.call.cancelSuperAdminTransfer({
407
+ superAdminCap: options.superAdminCapId,
408
+ }),
409
+ );
410
+ }
411
+
412
+ acceptSuperAdminTransfer() {
413
+ return single(this.call.acceptSuperAdminTransfer());
414
+ }
415
+
416
+ destroyRetiredSuperAdminCap(options: { superAdminCapId: string }) {
417
+ return single(
418
+ this.call.destroyRetiredSuperAdminCap({
419
+ superAdminCap: options.superAdminCapId,
420
+ }),
421
+ );
422
+ }
423
+ }
424
+
425
+ /**
426
+ * Pause bits are a nonzero mask within `valid` (a package's
427
+ * `valid_pause_bits`; any `u8` when the package checks them itself).
428
+ */
429
+ export function requirePauseBits(bits: number, valid = 0xff): number {
430
+ if (!Number.isInteger(bits) || bits <= 0 || (bits & ~valid) !== 0)
431
+ throw new RangeError(`pause bits must be a nonzero mask within ${valid}`);
432
+ return bits;
433
+ }
434
+
435
+ function single(call: (tx: Transaction) => void): Transaction {
436
+ const tx = new Transaction();
437
+ tx.add(call);
438
+ return tx;
439
+ }