@interest-protocol/blast-partners-sdk 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/src/state.ts CHANGED
@@ -1,8 +1,12 @@
1
+ import type { Delayed } from "@interest-protocol/blast-core/bcs";
1
2
  import {
2
3
  addressBcs,
4
+ delayedBcs,
3
5
  suiObjectIdBcs,
4
6
  suiUidBcs,
7
+ tableBcs,
5
8
  u64Bcs,
9
+ unitKeyBcs,
6
10
  } from "@interest-protocol/blast-core/bcs";
7
11
  import {
8
12
  BASIS_POINTS_DENOMINATOR,
@@ -10,31 +14,33 @@ import {
10
14
  normalizePositiveU64,
11
15
  } from "@interest-protocol/blast-core/integers";
12
16
  import type { SharedObjectIdentity } from "@interest-protocol/blast-core/sui";
13
- import type { Timelocked } from "@interest-protocol/blast-core/timelock";
14
- import {
15
- timelockedBcs,
16
- timelockedValue,
17
- } from "@interest-protocol/blast-core/timelock";
18
17
  import { bcs } from "@mysten/sui/bcs";
19
18
  import type { ClientWithCoreApi } from "@mysten/sui/client";
19
+ import { ObjectError } from "@mysten/sui/client";
20
20
  import { normalizeSuiObjectId, parseStructTag } from "@mysten/sui/utils";
21
21
 
22
- /** `blast_partners::delay_ms`: every change Blast makes to a partner waits this long. */
22
+ /**
23
+ * `blast_partners::delay_ms`: the fixed notice before a share or cap Blast
24
+ * proposes can be applied.
25
+ */
23
26
  export const PARTNER_DELAY_MS = 604_800_000n;
24
27
 
25
- /** The partner's recipient, scheduled share, and managing cap, as Move stores them. */
28
+ /**
29
+ * The partner's recipient, share and managing cap, as Move stores them. A
30
+ * share or cap proposal changes nothing until someone applies it.
31
+ */
26
32
  export type PartnerTerms = {
27
33
  recipient: string;
28
34
  /** Basis points of Blast's fee on each trade naming the partner. */
29
- shareBps: Timelocked<bigint>;
35
+ shareBps: Delayed<null, bigint>;
30
36
  /** The only `PartnerCap` that may set `recipient`. */
31
- capId: Timelocked<string>;
37
+ capId: Delayed<null, string>;
32
38
  };
33
39
 
34
40
  export const partnerTermsBcs = {
35
41
  recipient: addressBcs,
36
- share_bps: timelockedBcs(u64Bcs),
37
- cap_id: timelockedBcs(suiObjectIdBcs),
42
+ share_bps: delayedBcs(unitKeyBcs, u64Bcs),
43
+ cap_id: delayedBcs(unitKeyBcs, suiObjectIdBcs),
38
44
  };
39
45
 
40
46
  /** BCS layout of `blast_partners::blast_partners::Partner`. */
@@ -64,7 +70,11 @@ export function decodePartnerObject(
64
70
  }
65
71
  const parsed = partnerBcs.parse(object.content);
66
72
  const objectId = normalizeSuiObjectId(object.objectId);
67
- if (normalizeSuiObjectId(parsed.id.id.bytes) !== objectId) {
73
+ if (
74
+ normalizeSuiObjectId(parsed.id.id.bytes) !== objectId ||
75
+ parsed.share_bps.parent !== objectId ||
76
+ parsed.cap_id.parent !== objectId
77
+ ) {
68
78
  throw new TypeError(`partner ${object.objectId} has a mismatched UID`);
69
79
  }
70
80
  return {
@@ -103,18 +113,54 @@ export async function getPartner(
103
113
  }
104
114
 
105
115
  /**
106
- * Mirrors `blast_partners::pay`: the partner's share of `fee` (Blast's fee on
107
- * one trade, in that coin's atoms) at `nowMs`, rounded down; Blast keeps the
108
- * remainder.
116
+ * Mirrors `blast_partners::pay`: the partner's applied share of `fee`
117
+ * (Blast's fee on one trade, in that coin's atoms), rounded down; Blast keeps
118
+ * the remainder.
109
119
  */
110
120
  export function partnerPayment(
111
121
  partner: Pick<PartnerTerms, "shareBps">,
112
122
  fee: bigint,
113
- nowMs: bigint,
114
123
  ): bigint {
115
- return mulDivDown(
116
- fee,
117
- timelockedValue(partner.shareBps, nowMs),
118
- BASIS_POINTS_DENOMINATOR,
119
- );
124
+ return mulDivDown(fee, partner.shareBps.value, BASIS_POINTS_DENOMINATOR);
125
+ }
126
+
127
+ /**
128
+ * BCS layout of `blast_partners::partner_list::PartnerList`, embedded in a
129
+ * product's configuration (`LaunchConfig.partners`,
130
+ * `CreationPolicy.partners`). `parent` is that configuration's ID; each
131
+ * accepted partner is an entry of the `acceptance_ends_at_ms` table.
132
+ */
133
+ export const partnerListBcs = bcs.struct("PartnerList", {
134
+ parent: suiObjectIdBcs,
135
+ acceptance_ends_at_ms: tableBcs,
136
+ });
137
+
138
+ export type PartnerList = ReturnType<typeof partnerListBcs.parse>;
139
+
140
+ /**
141
+ * Reads one entry of a `PartnerList`'s `acceptance_ends_at_ms` table (its
142
+ * `id`): the millisecond from which new sales stop naming `partnerId`, or
143
+ * `U64_MAX` while it is accepted without end. Null when the list never
144
+ * accepted the partner. A partner is accepted at `now` when `now` is before
145
+ * the returned time (`partner_list::is_accepted`).
146
+ */
147
+ export async function getPartnerAcceptance(
148
+ client: ClientWithCoreApi,
149
+ listTableId: string,
150
+ partnerId: string,
151
+ ): Promise<bigint | null> {
152
+ try {
153
+ const { dynamicField } = await client.core.getDynamicField({
154
+ parentId: normalizeSuiObjectId(listTableId),
155
+ name: {
156
+ type: "0x2::object::ID",
157
+ bcs: suiObjectIdBcs.serialize(partnerId).toBytes(),
158
+ },
159
+ });
160
+ return u64Bcs.parse(dynamicField.value.bcs);
161
+ } catch (error) {
162
+ if (error instanceof ObjectError && error.reason === "notFound")
163
+ return null;
164
+ throw error;
165
+ }
120
166
  }
@@ -14,7 +14,7 @@ import type {
14
14
  import { Transaction } from "@mysten/sui/transactions";
15
15
  import { normalizeSuiAddress, normalizeSuiObjectId } from "@mysten/sui/utils";
16
16
 
17
- /** The immutable `blast_partners` package and the ACL its `PartnerAdmin` signs in to. */
17
+ /** The canonical `blast_partners` package (immutability deferred) and the ACL its `PartnerAdmin` signs in to. */
18
18
  export type PartnersDeployment = {
19
19
  packageId: string;
20
20
  admin: AdminDeployment;
@@ -78,7 +78,11 @@ export class PartnersCalls {
78
78
  };
79
79
  }
80
80
 
81
- /** `schedule_share` (`PartnerAdmin`): the share takes effect after the partner delay. */
81
+ /**
82
+ * `schedule_share` (`PartnerAdmin`): proposes the share, which `applyShare`
83
+ * applies once `PARTNER_DELAY_MS` has passed. A new proposal replaces a
84
+ * pending one and restarts the notice.
85
+ */
82
86
  scheduleShare(options: {
83
87
  partner: SharedObjectReference<true>;
84
88
  adminWitness: TransactionObjectInput;
@@ -98,7 +102,10 @@ export class PartnersCalls {
98
102
  };
99
103
  }
100
104
 
101
- /** `schedule_cap` (`PartnerAdmin`): moves the partner to the cap `capId` after the delay. */
105
+ /**
106
+ * `schedule_cap` (`PartnerAdmin`): proposes moving the partner to the cap
107
+ * `capId`, which `applyCap` applies once `PARTNER_DELAY_MS` has passed.
108
+ */
102
109
  scheduleCap(options: {
103
110
  partner: SharedObjectReference<true>;
104
111
  adminWitness: TransactionObjectInput;
@@ -118,7 +125,59 @@ export class PartnersCalls {
118
125
  };
119
126
  }
120
127
 
121
- /** `set_recipient`: the partner's live cap sets where it is paid, at once. */
128
+ /** `apply_share`: anyone applies the proposed share once its notice has passed. */
129
+ applyShare(options: { partner: SharedObjectReference<true> }) {
130
+ return (tx: Transaction): void => {
131
+ tx.moveCall({
132
+ target: this.#target("apply_share"),
133
+ arguments: [partner(tx, options.partner), tx.object.clock()],
134
+ });
135
+ };
136
+ }
137
+
138
+ /** `apply_cap`: anyone applies the proposed managing cap once its notice has passed. */
139
+ applyCap(options: { partner: SharedObjectReference<true> }) {
140
+ return (tx: Transaction): void => {
141
+ tx.moveCall({
142
+ target: this.#target("apply_cap"),
143
+ arguments: [partner(tx, options.partner), tx.object.clock()],
144
+ });
145
+ };
146
+ }
147
+
148
+ /** `cancel_share` (`PartnerAdmin`): discards the proposed share, even a ready one. */
149
+ cancelShare(options: {
150
+ partner: SharedObjectReference<true>;
151
+ adminWitness: TransactionObjectInput;
152
+ }) {
153
+ return (tx: Transaction): void => {
154
+ tx.moveCall({
155
+ target: this.#target("cancel_share"),
156
+ arguments: [
157
+ partner(tx, options.partner),
158
+ tx.object(options.adminWitness),
159
+ ],
160
+ });
161
+ };
162
+ }
163
+
164
+ /** `cancel_cap` (`PartnerAdmin`): discards the proposed managing cap, even a ready one. */
165
+ cancelCap(options: {
166
+ partner: SharedObjectReference<true>;
167
+ adminWitness: TransactionObjectInput;
168
+ }) {
169
+ return (tx: Transaction): void => {
170
+ tx.moveCall({
171
+ target: this.#target("cancel_cap"),
172
+ arguments: [
173
+ partner(tx, options.partner),
174
+ tx.object(options.adminWitness),
175
+ ],
176
+ });
177
+ };
178
+ }
179
+
180
+ /** `set_recipient`: the partner's applied cap sets where it is paid, at once. */
122
181
  setRecipient(options: {
123
182
  partner: SharedObjectReference<true>;
124
183
  partnerCap: TransactionObjectInput;
@@ -132,7 +191,6 @@ export class PartnersCalls {
132
191
  partner(tx, options.partner),
133
192
  tx.object(options.partnerCap),
134
193
  tx.pure.address(recipient),
135
- tx.object.clock(),
136
194
  ],
137
195
  });
138
196
  };
@@ -198,6 +256,36 @@ export class PartnersTransactions {
198
256
  );
199
257
  }
200
258
 
259
+ applyShare(options: { partner: SharedObjectReference<true> }): Transaction {
260
+ const tx = new Transaction();
261
+ tx.add(this.call.applyShare(options));
262
+ return tx;
263
+ }
264
+
265
+ applyCap(options: { partner: SharedObjectReference<true> }): Transaction {
266
+ const tx = new Transaction();
267
+ tx.add(this.call.applyCap(options));
268
+ return tx;
269
+ }
270
+
271
+ cancelShare(options: {
272
+ adminCapId: string;
273
+ partner: SharedObjectReference<true>;
274
+ }): Transaction {
275
+ return this.#asPartnerAdmin(options.adminCapId, (adminWitness) =>
276
+ this.call.cancelShare({ ...options, adminWitness }),
277
+ );
278
+ }
279
+
280
+ cancelCap(options: {
281
+ adminCapId: string;
282
+ partner: SharedObjectReference<true>;
283
+ }): Transaction {
284
+ return this.#asPartnerAdmin(options.adminCapId, (adminWitness) =>
285
+ this.call.cancelCap({ ...options, adminWitness }),
286
+ );
287
+ }
288
+
201
289
  setRecipient(options: {
202
290
  partner: SharedObjectReference<true>;
203
291
  partnerCapId: string;