@interest-protocol/blast-partners-sdk 0.0.0-stage → 0.1.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/README.md CHANGED
@@ -1,3 +1,9 @@
1
- # Temporary Holding Version
1
+ # @interest-protocol/blast-partners-sdk
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The partners package in the [Blast SDK monorepo](https://github.com/interest-protocol/blast-v2-sdk).
4
+
5
+ ```sh
6
+ npm install @interest-protocol/blast-partners-sdk
7
+ ```
8
+
9
+ See the repository README and docs for usage, development, and release guidance.
@@ -0,0 +1,36 @@
1
+ import type { SuiEventPosition } from "@interest-protocol/blast-core/events";
2
+ import type { SuiClientTypes } from "@mysten/sui/client";
3
+ /** The `blast_partners` events, in Move field order. */
4
+ export declare const partnerEventBcs: {
5
+ /** After every change, registration included: the partner's complete terms. */
6
+ PartnerUpdated: import("@mysten/sui/bcs").BcsStruct<{
7
+ recipient: import("@mysten/sui/bcs").BcsType<string, string, "bytes[32]">;
8
+ share_bps: import("@mysten/sui/bcs").BcsType<import("@interest-protocol/blast-core/timelock").Timelocked<bigint>, import("@interest-protocol/blast-core/timelock").Timelocked<bigint>, string>;
9
+ cap_id: import("@mysten/sui/bcs").BcsType<import("@interest-protocol/blast-core/timelock").Timelocked<string>, import("@interest-protocol/blast-core/timelock").Timelocked<string>, string>;
10
+ partner_id: import("@mysten/sui/bcs").BcsType<string, string, string>;
11
+ }, string>;
12
+ /** `amount` of the event's coin type sent to the partner's recipient. */
13
+ PartnerPaid: import("@mysten/sui/bcs").BcsStruct<{
14
+ partner_id: import("@mysten/sui/bcs").BcsType<string, string, string>;
15
+ amount: import("@mysten/sui/bcs").BcsType<bigint, bigint, "u64">;
16
+ }, string>;
17
+ };
18
+ export type PartnerEventKind = keyof typeof partnerEventBcs;
19
+ export type PartnerEventPayloads = {
20
+ [Kind in PartnerEventKind]: ReturnType<(typeof partnerEventBcs)[Kind]["parse"]>;
21
+ };
22
+ export type DecodedPartnerEvent = {
23
+ [Kind in PartnerEventKind]: {
24
+ source: "partners";
25
+ kind: Kind;
26
+ payload: PartnerEventPayloads[Kind];
27
+ /** `PartnerPaid` only: the paid coin type `T`. */
28
+ coinType: Kind extends "PartnerPaid" ? string : null;
29
+ sender: string;
30
+ position: SuiEventPosition;
31
+ };
32
+ }[PartnerEventKind];
33
+ /** Decodes one `<partnersPackageId>::blast_partners::<Kind>` event. */
34
+ export declare function decodePartnerEvent(event: SuiClientTypes.EventEntry, partnersPackageId: string): DecodedPartnerEvent;
35
+ export declare function partnerEventId(event: DecodedPartnerEvent): string;
36
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAM7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQzD,wDAAwD;AACxD,eAAO,MAAM,eAAe;IAC1B,+EAA+E;IAC/E,cAAc;;;;;;IAId,yEAAyE;IACzE,WAAW;;;;CAIZ,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,eAAe,CAAC;AAE5D,MAAM,MAAM,oBAAoB,GAAG;KAChC,IAAI,IAAI,gBAAgB,GAAG,UAAU,CACpC,CAAC,OAAO,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CACxC;CACF,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;KAC/B,IAAI,IAAI,gBAAgB,GAAG;QAC1B,MAAM,EAAE,UAAU,CAAC;QACnB,IAAI,EAAE,IAAI,CAAC;QACX,OAAO,EAAE,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACpC,kDAAkD;QAClD,QAAQ,EAAE,IAAI,SAAS,aAAa,GAAG,MAAM,GAAG,IAAI,CAAC;QACrD,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,gBAAgB,CAAC;KAC5B;CACF,CAAC,gBAAgB,CAAC,CAAC;AAEpB,uEAAuE;AACvE,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,cAAc,CAAC,UAAU,EAChC,iBAAiB,EAAE,MAAM,GACxB,mBAAmB,CAqBrB;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAEjE"}
package/dist/events.js ADDED
@@ -0,0 +1,43 @@
1
+ import { suiObjectIdBcs, u64Bcs } from "@interest-protocol/blast-core/bcs";
2
+ import { normalizeSuiEventPosition, suiEventId, } from "@interest-protocol/blast-core/events";
3
+ import { bcs } from "@mysten/sui/bcs";
4
+ import { normalizeStructTag, normalizeSuiAddress, parseStructTag, } from "@mysten/sui/utils";
5
+ import { partnerTermsBcs } from "./state.js";
6
+ /** The `blast_partners` events, in Move field order. */
7
+ export const partnerEventBcs = {
8
+ /** After every change, registration included: the partner's complete terms. */
9
+ PartnerUpdated: bcs.struct("PartnerUpdated", {
10
+ partner_id: suiObjectIdBcs,
11
+ ...partnerTermsBcs,
12
+ }),
13
+ /** `amount` of the event's coin type sent to the partner's recipient. */
14
+ PartnerPaid: bcs.struct("PartnerPaid", {
15
+ partner_id: suiObjectIdBcs,
16
+ amount: u64Bcs,
17
+ }),
18
+ };
19
+ /** Decodes one `<partnersPackageId>::blast_partners::<Kind>` event. */
20
+ export function decodePartnerEvent(event, partnersPackageId) {
21
+ const tag = parseStructTag(event.eventType);
22
+ const kind = tag.name;
23
+ const [coinType = null, ...extra] = tag.typeParams;
24
+ if (tag.address !== normalizeSuiAddress(partnersPackageId) ||
25
+ tag.module !== "blast_partners" ||
26
+ !Object.hasOwn(partnerEventBcs, kind) ||
27
+ (coinType === null) === (kind === "PartnerPaid") ||
28
+ extra.length > 0) {
29
+ throw new TypeError(`unexpected partner event type ${event.eventType}`);
30
+ }
31
+ return {
32
+ source: "partners",
33
+ kind,
34
+ payload: partnerEventBcs[kind].parse(event.bcs),
35
+ coinType: coinType && normalizeStructTag(coinType),
36
+ sender: normalizeSuiAddress(event.sender),
37
+ position: normalizeSuiEventPosition(event),
38
+ };
39
+ }
40
+ export function partnerEventId(event) {
41
+ return suiEventId(event.position);
42
+ }
43
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAE3E,OAAO,EACL,yBAAyB,EACzB,UAAU,GACX,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAEtC,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,wDAAwD;AACxD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,+EAA+E;IAC/E,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,gBAAgB,EAAE;QAC3C,UAAU,EAAE,cAAc;QAC1B,GAAG,eAAe;KACnB,CAAC;IACF,yEAAyE;IACzE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE;QACrC,UAAU,EAAE,cAAc;QAC1B,MAAM,EAAE,MAAM;KACf,CAAC;CACH,CAAC;AAsBF,uEAAuE;AACvE,MAAM,UAAU,kBAAkB,CAChC,KAAgC,EAChC,iBAAyB;IAEzB,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAwB,CAAC;IAC1C,MAAM,CAAC,QAAQ,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC;IACnD,IACE,GAAG,CAAC,OAAO,KAAK,mBAAmB,CAAC,iBAAiB,CAAC;QACtD,GAAG,CAAC,MAAM,KAAK,gBAAgB;QAC/B,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,IAAI,CAAC;QACrC,CAAC,QAAQ,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,aAAa,CAAC;QAChD,KAAK,CAAC,MAAM,GAAG,CAAC,EAChB,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,iCAAiC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO;QACL,MAAM,EAAE,UAAU;QAClB,IAAI;QACJ,OAAO,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;QAC/C,QAAQ,EAAE,QAAQ,IAAI,kBAAkB,CAAC,QAAQ,CAAC;QAClD,MAAM,EAAE,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC;QACzC,QAAQ,EAAE,yBAAyB,CAAC,KAAK,CAAC;KACpB,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAA0B;IACvD,OAAO,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export * from "./events.ts";
2
+ export * from "./reducer.ts";
3
+ export * from "./state.ts";
4
+ export * from "./transactions.ts";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./events.js";
2
+ export * from "./reducer.js";
3
+ export * from "./state.js";
4
+ export * from "./transactions.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { DecodedPartnerEvent, PartnerEventKind } from "./events.ts";
2
+ import type { PartnerTerms } from "./state.ts";
3
+ export type PartnerProjection = PartnerTerms & {
4
+ partnerId: string;
5
+ /** Everything sent to the partner, by coin type. */
6
+ paid: Readonly<Record<string, bigint>>;
7
+ };
8
+ /** Partners rebuilt from their events; terms match `PartnerState` once replayed. */
9
+ export type PartnersEventProjection = {
10
+ appliedEventIds: Readonly<Record<string, true>>;
11
+ eventCounts: Readonly<Record<PartnerEventKind, number>>;
12
+ partners: Readonly<Record<string, PartnerProjection>>;
13
+ };
14
+ export declare function createPartnersEventProjection(): PartnersEventProjection;
15
+ /** Applies one event once; replaying an applied event returns `state` unchanged. */
16
+ export declare function reducePartnerEvent(state: PartnersEventProjection, event: DecodedPartnerEvent): PartnersEventProjection;
17
+ //# sourceMappingURL=reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../src/reducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACxC,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,uBAAuB,GAAG;IACpC,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAChD,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;CACvD,CAAC;AAEF,wBAAgB,6BAA6B,IAAI,uBAAuB,CAQvE;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,EAC9B,KAAK,EAAE,mBAAmB,GACzB,uBAAuB,CAiCzB"}
@@ -0,0 +1,52 @@
1
+ import { partnerEventBcs, partnerEventId } from "./events.js";
2
+ export function createPartnersEventProjection() {
3
+ return {
4
+ appliedEventIds: {},
5
+ eventCounts: Object.fromEntries(Object.keys(partnerEventBcs).map((kind) => [kind, 0])),
6
+ partners: {},
7
+ };
8
+ }
9
+ /** Applies one event once; replaying an applied event returns `state` unchanged. */
10
+ export function reducePartnerEvent(state, event) {
11
+ const eventId = partnerEventId(event);
12
+ if (state.appliedEventIds[eventId])
13
+ return state;
14
+ const next = {
15
+ ...state,
16
+ appliedEventIds: { ...state.appliedEventIds, [eventId]: true },
17
+ eventCounts: {
18
+ ...state.eventCounts,
19
+ [event.kind]: state.eventCounts[event.kind] + 1,
20
+ },
21
+ };
22
+ const partnerId = event.payload.partner_id;
23
+ const current = state.partners[partnerId];
24
+ if (event.kind === "PartnerUpdated") {
25
+ const { recipient, share_bps, cap_id } = event.payload;
26
+ return withPartner(next, {
27
+ partnerId,
28
+ recipient,
29
+ shareBps: share_bps,
30
+ capId: cap_id,
31
+ paid: current?.paid ?? {},
32
+ });
33
+ }
34
+ // A partner is registered, with an update, before it can be paid.
35
+ if (!current)
36
+ return next;
37
+ const coinType = event.coinType;
38
+ return withPartner(next, {
39
+ ...current,
40
+ paid: {
41
+ ...current.paid,
42
+ [coinType]: (current.paid[coinType] ?? 0n) + event.payload.amount,
43
+ },
44
+ });
45
+ }
46
+ function withPartner(state, partner) {
47
+ return {
48
+ ...state,
49
+ partners: { ...state.partners, [partner.partnerId]: partner },
50
+ };
51
+ }
52
+ //# sourceMappingURL=reducer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer.js","sourceRoot":"","sources":["../src/reducer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAgB9D,MAAM,UAAU,6BAA6B;IAC3C,OAAO;QACL,eAAe,EAAE,EAAE;QACnB,WAAW,EAAE,MAAM,CAAC,WAAW,CAC7B,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAClB;QACrC,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,kBAAkB,CAChC,KAA8B,EAC9B,KAA0B;IAE1B,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACjD,MAAM,IAAI,GAA4B;QACpC,GAAG,KAAK;QACR,eAAe,EAAE,EAAE,GAAG,KAAK,CAAC,eAAe,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE;QAC9D,WAAW,EAAE;YACX,GAAG,KAAK,CAAC,WAAW;YACpB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;SAChD;KACF,CAAC;IACF,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QACpC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC;QACvD,OAAO,WAAW,CAAC,IAAI,EAAE;YACvB,SAAS;YACT,SAAS;YACT,QAAQ,EAAE,SAAS;YACnB,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE;SAC1B,CAAC,CAAC;IACL,CAAC;IACD,kEAAkE;IAClE,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAChC,OAAO,WAAW,CAAC,IAAI,EAAE;QACvB,GAAG,OAAO;QACV,IAAI,EAAE;YACJ,GAAG,OAAO,CAAC,IAAI;YACf,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM;SAClE;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAClB,KAA8B,EAC9B,OAA0B;IAE1B,OAAO;QACL,GAAG,KAAK;QACR,QAAQ,EAAE,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;KAC9D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { SharedObjectIdentity } from "@interest-protocol/blast-core/sui";
2
+ import type { Timelocked } from "@interest-protocol/blast-core/timelock";
3
+ import type { ClientWithCoreApi } from "@mysten/sui/client";
4
+ /** `blast_partners::delay_ms`: every change Blast makes to a partner waits this long. */
5
+ export declare const PARTNER_DELAY_MS = 604800000n;
6
+ /** The partner's recipient, scheduled share, and managing cap, as Move stores them. */
7
+ export type PartnerTerms = {
8
+ recipient: string;
9
+ /** Basis points of Blast's fee on each trade naming the partner. */
10
+ shareBps: Timelocked<bigint>;
11
+ /** The only `PartnerCap` that may set `recipient`. */
12
+ capId: Timelocked<string>;
13
+ };
14
+ export declare const partnerTermsBcs: {
15
+ recipient: import("@mysten/sui/bcs").BcsType<string, string, "bytes[32]">;
16
+ share_bps: import("@mysten/sui/bcs").BcsType<Timelocked<bigint>, Timelocked<bigint>, string>;
17
+ cap_id: import("@mysten/sui/bcs").BcsType<Timelocked<string>, Timelocked<string>, string>;
18
+ };
19
+ /** BCS layout of `blast_partners::blast_partners::Partner`. */
20
+ export declare const partnerBcs: import("@mysten/sui/bcs").BcsStruct<{
21
+ recipient: import("@mysten/sui/bcs").BcsType<string, string, "bytes[32]">;
22
+ share_bps: import("@mysten/sui/bcs").BcsType<Timelocked<bigint>, Timelocked<bigint>, string>;
23
+ cap_id: import("@mysten/sui/bcs").BcsType<Timelocked<string>, Timelocked<string>, string>;
24
+ id: import("@mysten/sui/bcs").BcsStruct<{
25
+ id: import("@mysten/sui/bcs").BcsStruct<{
26
+ bytes: import("@mysten/sui/bcs").BcsType<string, string | Uint8Array<ArrayBufferLike>, "bytes[32]">;
27
+ }, string>;
28
+ }, string>;
29
+ }, string>;
30
+ export type PartnerState = SharedObjectIdentity & PartnerTerms;
31
+ export type PartnerObject = SharedObjectIdentity & {
32
+ type: string;
33
+ content: Uint8Array;
34
+ };
35
+ export declare function decodePartnerObject(object: PartnerObject, partnersPackageId: string): PartnerState;
36
+ export declare function getPartner(client: ClientWithCoreApi, objectId: string, partnersPackageId: string): Promise<PartnerState>;
37
+ /**
38
+ * Mirrors `blast_partners::pay`: the partner's share of `fee` (Blast's fee on
39
+ * one trade, in that coin's atoms) at `nowMs`, rounded down; Blast keeps the
40
+ * remainder.
41
+ */
42
+ export declare function partnerPayment(partner: Pick<PartnerTerms, "shareBps">, fee: bigint, nowMs: bigint): bigint;
43
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAMzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG5D,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,aAAe,CAAC;AAE7C,uFAAuF;AACvF,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC7B,sDAAsD;IACtD,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,eAAe;IAC1B,SAAS;IACT,SAAS;IACT,MAAM;CACP,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,UAAU;;;;;;;;;UAGrB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,oBAAoB,GAAG,YAAY,CAAC;AAE/D,MAAM,MAAM,aAAa,GAAG,oBAAoB,GAAG;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,iBAAiB,EAAE,MAAM,GACxB,YAAY,CAwBd;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,YAAY,CAAC,CAiBvB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,EACvC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,MAAM,CAMR"}
package/dist/state.js ADDED
@@ -0,0 +1,61 @@
1
+ import { addressBcs, suiObjectIdBcs, suiUidBcs, u64Bcs, } from "@interest-protocol/blast-core/bcs";
2
+ import { BASIS_POINTS_DENOMINATOR, mulDivDown, normalizePositiveU64, } from "@interest-protocol/blast-core/integers";
3
+ import { timelockedBcs, timelockedValue, } from "@interest-protocol/blast-core/timelock";
4
+ import { bcs } from "@mysten/sui/bcs";
5
+ import { normalizeSuiObjectId, parseStructTag } from "@mysten/sui/utils";
6
+ /** `blast_partners::delay_ms`: every change Blast makes to a partner waits this long. */
7
+ export const PARTNER_DELAY_MS = 604800000n;
8
+ export const partnerTermsBcs = {
9
+ recipient: addressBcs,
10
+ share_bps: timelockedBcs(u64Bcs),
11
+ cap_id: timelockedBcs(suiObjectIdBcs),
12
+ };
13
+ /** BCS layout of `blast_partners::blast_partners::Partner`. */
14
+ export const partnerBcs = bcs.struct("Partner", {
15
+ id: suiUidBcs,
16
+ ...partnerTermsBcs,
17
+ });
18
+ export function decodePartnerObject(object, partnersPackageId) {
19
+ const tag = parseStructTag(object.type);
20
+ if (tag.address !== normalizeSuiObjectId(partnersPackageId) ||
21
+ tag.module !== "blast_partners" ||
22
+ tag.name !== "Partner") {
23
+ throw new TypeError(`object ${object.objectId} is not a Blast partner`);
24
+ }
25
+ const parsed = partnerBcs.parse(object.content);
26
+ const objectId = normalizeSuiObjectId(object.objectId);
27
+ if (normalizeSuiObjectId(parsed.id.id.bytes) !== objectId) {
28
+ throw new TypeError(`partner ${object.objectId} has a mismatched UID`);
29
+ }
30
+ return {
31
+ objectId,
32
+ initialSharedVersion: normalizePositiveU64("initialSharedVersion", object.initialSharedVersion),
33
+ recipient: parsed.recipient,
34
+ shareBps: parsed.share_bps,
35
+ capId: parsed.cap_id,
36
+ };
37
+ }
38
+ export async function getPartner(client, objectId, partnersPackageId) {
39
+ const { object } = await client.core.getObject({
40
+ objectId,
41
+ include: { content: true },
42
+ });
43
+ if (object.owner.$kind !== "Shared") {
44
+ throw new TypeError(`partner ${object.objectId} is not a shared object`);
45
+ }
46
+ return decodePartnerObject({
47
+ objectId: object.objectId,
48
+ initialSharedVersion: object.owner.Shared.initialSharedVersion,
49
+ type: object.type,
50
+ content: object.content,
51
+ }, partnersPackageId);
52
+ }
53
+ /**
54
+ * Mirrors `blast_partners::pay`: the partner's share of `fee` (Blast's fee on
55
+ * one trade, in that coin's atoms) at `nowMs`, rounded down; Blast keeps the
56
+ * remainder.
57
+ */
58
+ export function partnerPayment(partner, fee, nowMs) {
59
+ return mulDivDown(fee, timelockedValue(partner.shareBps, nowMs), BASIS_POINTS_DENOMINATOR);
60
+ }
61
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import type { AdminDeployment } from "@interest-protocol/blast-admin-sdk/transactions";
2
+ import { AdminCalls } from "@interest-protocol/blast-admin-sdk/transactions";
3
+ import type { SharedObjectReference } from "@interest-protocol/blast-core/sui";
4
+ import type { TransactionObjectArgument, TransactionObjectInput, TransactionResult } from "@mysten/sui/transactions";
5
+ import { Transaction } from "@mysten/sui/transactions";
6
+ /** The immutable `blast_partners` package and the ACL its `PartnerAdmin` signs in to. */
7
+ export type PartnersDeployment = {
8
+ packageId: string;
9
+ admin: AdminDeployment;
10
+ };
11
+ /** `blast_partners::blast_partners` calls. */
12
+ export declare class PartnersCalls {
13
+ #private;
14
+ readonly deployment: PartnersDeployment;
15
+ readonly admin: AdminCalls;
16
+ constructor(deployment: PartnersDeployment);
17
+ /** The `PartnerAdmin` role type, for `AdminCalls.grantRole`. */
18
+ roleType(): string;
19
+ /** This PTB's `AdminWitness<PartnerAdmin>`. */
20
+ signIn(options: {
21
+ adminCap: TransactionObjectInput;
22
+ }): (tx: Transaction) => TransactionObjectArgument;
23
+ /** `new_cap`: anyone may mint one; it controls nothing until a partner names it. */
24
+ newCap(): (tx: Transaction) => TransactionResult;
25
+ /**
26
+ * `register` (`PartnerAdmin`): shares a partner managed by the cap
27
+ * `capId`, its share and cap in force at once.
28
+ */
29
+ register(options: {
30
+ adminWitness: TransactionObjectInput;
31
+ capId: string;
32
+ recipient: string;
33
+ shareBps: bigint;
34
+ }): (tx: Transaction) => void;
35
+ /** `schedule_share` (`PartnerAdmin`): the share takes effect after the partner delay. */
36
+ scheduleShare(options: {
37
+ partner: SharedObjectReference<true>;
38
+ adminWitness: TransactionObjectInput;
39
+ shareBps: bigint;
40
+ }): (tx: Transaction) => void;
41
+ /** `schedule_cap` (`PartnerAdmin`): moves the partner to the cap `capId` after the delay. */
42
+ scheduleCap(options: {
43
+ partner: SharedObjectReference<true>;
44
+ adminWitness: TransactionObjectInput;
45
+ capId: string;
46
+ }): (tx: Transaction) => void;
47
+ /** `set_recipient`: the partner's live cap sets where it is paid, at once. */
48
+ setRecipient(options: {
49
+ partner: SharedObjectReference<true>;
50
+ partnerCap: TransactionObjectInput;
51
+ recipient: string;
52
+ }): (tx: Transaction) => void;
53
+ destroyCap(options: {
54
+ partnerCap: TransactionObjectInput;
55
+ }): (tx: Transaction) => void;
56
+ }
57
+ export declare class PartnersTransactions {
58
+ #private;
59
+ readonly call: PartnersCalls;
60
+ constructor(deployment: PartnersDeployment);
61
+ /** Mints a `PartnerCap` and sends it to `recipient`. */
62
+ newCap(options: {
63
+ recipient: string;
64
+ }): Transaction;
65
+ register(options: {
66
+ adminCapId: string;
67
+ capId: string;
68
+ recipient: string;
69
+ shareBps: bigint;
70
+ }): Transaction;
71
+ scheduleShare(options: {
72
+ adminCapId: string;
73
+ partner: SharedObjectReference<true>;
74
+ shareBps: bigint;
75
+ }): Transaction;
76
+ scheduleCap(options: {
77
+ adminCapId: string;
78
+ partner: SharedObjectReference<true>;
79
+ capId: string;
80
+ }): Transaction;
81
+ setRecipient(options: {
82
+ partner: SharedObjectReference<true>;
83
+ partnerCapId: string;
84
+ recipient: string;
85
+ }): Transaction;
86
+ destroyCap(options: {
87
+ partnerCapId: string;
88
+ }): Transaction;
89
+ }
90
+ //# sourceMappingURL=transactions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../src/transactions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ import { AdminCalls, normalizeAdminDeployment, } from "@interest-protocol/blast-admin-sdk/transactions";
2
+ import { requireBasisPoints } from "@interest-protocol/blast-core/integers";
3
+ import { normalizeSharedObjectReference } from "@interest-protocol/blast-core/sui";
4
+ import { Transaction } from "@mysten/sui/transactions";
5
+ import { normalizeSuiAddress, normalizeSuiObjectId } from "@mysten/sui/utils";
6
+ /** `blast_partners::blast_partners` calls. */
7
+ export class PartnersCalls {
8
+ deployment;
9
+ admin;
10
+ constructor(deployment) {
11
+ this.deployment = {
12
+ packageId: normalizeSuiObjectId(deployment.packageId),
13
+ admin: normalizeAdminDeployment(deployment.admin),
14
+ };
15
+ this.admin = new AdminCalls(this.deployment.admin);
16
+ }
17
+ /** The `PartnerAdmin` role type, for `AdminCalls.grantRole`. */
18
+ roleType() {
19
+ return `${this.deployment.packageId}::blast_partners::PartnerAdmin`;
20
+ }
21
+ /** This PTB's `AdminWitness<PartnerAdmin>`. */
22
+ signIn(options) {
23
+ return this.admin.signIn({
24
+ adminCap: options.adminCap,
25
+ role: this.roleType(),
26
+ });
27
+ }
28
+ /** `new_cap`: anyone may mint one; it controls nothing until a partner names it. */
29
+ newCap() {
30
+ return (tx) => tx.moveCall({ target: this.#target("new_cap") });
31
+ }
32
+ /**
33
+ * `register` (`PartnerAdmin`): shares a partner managed by the cap
34
+ * `capId`, its share and cap in force at once.
35
+ */
36
+ register(options) {
37
+ const capId = normalizeSuiObjectId(options.capId);
38
+ const recipient = normalizeSuiAddress(options.recipient);
39
+ const shareBps = requireBasisPoints("shareBps", options.shareBps);
40
+ return (tx) => {
41
+ tx.moveCall({
42
+ target: this.#target("register"),
43
+ arguments: [
44
+ tx.object(options.adminWitness),
45
+ tx.pure.id(capId),
46
+ tx.pure.address(recipient),
47
+ tx.pure.u64(shareBps),
48
+ ],
49
+ });
50
+ };
51
+ }
52
+ /** `schedule_share` (`PartnerAdmin`): the share takes effect after the partner delay. */
53
+ scheduleShare(options) {
54
+ const shareBps = requireBasisPoints("shareBps", options.shareBps);
55
+ return (tx) => {
56
+ tx.moveCall({
57
+ target: this.#target("schedule_share"),
58
+ arguments: [
59
+ partner(tx, options.partner),
60
+ tx.object(options.adminWitness),
61
+ tx.pure.u64(shareBps),
62
+ tx.object.clock(),
63
+ ],
64
+ });
65
+ };
66
+ }
67
+ /** `schedule_cap` (`PartnerAdmin`): moves the partner to the cap `capId` after the delay. */
68
+ scheduleCap(options) {
69
+ const capId = normalizeSuiObjectId(options.capId);
70
+ return (tx) => {
71
+ tx.moveCall({
72
+ target: this.#target("schedule_cap"),
73
+ arguments: [
74
+ partner(tx, options.partner),
75
+ tx.object(options.adminWitness),
76
+ tx.pure.id(capId),
77
+ tx.object.clock(),
78
+ ],
79
+ });
80
+ };
81
+ }
82
+ /** `set_recipient`: the partner's live cap sets where it is paid, at once. */
83
+ setRecipient(options) {
84
+ const recipient = normalizeSuiAddress(options.recipient);
85
+ return (tx) => {
86
+ tx.moveCall({
87
+ target: this.#target("set_recipient"),
88
+ arguments: [
89
+ partner(tx, options.partner),
90
+ tx.object(options.partnerCap),
91
+ tx.pure.address(recipient),
92
+ tx.object.clock(),
93
+ ],
94
+ });
95
+ };
96
+ }
97
+ destroyCap(options) {
98
+ return (tx) => {
99
+ tx.moveCall({
100
+ target: this.#target("destroy_cap"),
101
+ arguments: [tx.object(options.partnerCap)],
102
+ });
103
+ };
104
+ }
105
+ #target(functionName) {
106
+ return `${this.deployment.packageId}::blast_partners::${functionName}`;
107
+ }
108
+ }
109
+ export class PartnersTransactions {
110
+ call;
111
+ constructor(deployment) {
112
+ this.call = new PartnersCalls(deployment);
113
+ }
114
+ /** Mints a `PartnerCap` and sends it to `recipient`. */
115
+ newCap(options) {
116
+ const tx = new Transaction();
117
+ const cap = tx.add(this.call.newCap());
118
+ tx.transferObjects([cap], normalizeSuiAddress(options.recipient));
119
+ return tx;
120
+ }
121
+ register(options) {
122
+ return this.#asPartnerAdmin(options.adminCapId, (adminWitness) => this.call.register({ ...options, adminWitness }));
123
+ }
124
+ scheduleShare(options) {
125
+ return this.#asPartnerAdmin(options.adminCapId, (adminWitness) => this.call.scheduleShare({ ...options, adminWitness }));
126
+ }
127
+ scheduleCap(options) {
128
+ return this.#asPartnerAdmin(options.adminCapId, (adminWitness) => this.call.scheduleCap({ ...options, adminWitness }));
129
+ }
130
+ setRecipient(options) {
131
+ const tx = new Transaction();
132
+ tx.add(this.call.setRecipient({ ...options, partnerCap: options.partnerCapId }));
133
+ return tx;
134
+ }
135
+ destroyCap(options) {
136
+ const tx = new Transaction();
137
+ tx.add(this.call.destroyCap({ partnerCap: options.partnerCapId }));
138
+ return tx;
139
+ }
140
+ #asPartnerAdmin(adminCapId, call) {
141
+ const tx = new Transaction();
142
+ const adminWitness = tx.add(this.call.signIn({ adminCap: adminCapId }));
143
+ tx.add(call(adminWitness));
144
+ return tx;
145
+ }
146
+ }
147
+ function partner(tx, reference) {
148
+ return tx.sharedObjectRef(normalizeSharedObjectReference("partner", reference, true));
149
+ }
150
+ //# sourceMappingURL=transactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactions.js","sourceRoot":"","sources":["../src/transactions.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,58 @@
1
1
  {
2
2
  "name": "@interest-protocol/blast-partners-sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Blast partners SDK",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/interest-protocol/blast-v2-sdk.git",
9
+ "directory": "packages/partners"
10
+ },
11
+ "sideEffects": false,
12
+ "files": [
13
+ "dist",
14
+ "src"
15
+ ],
16
+ "exports": {
17
+ "./events": {
18
+ "types": "./dist/events.d.ts",
19
+ "default": "./dist/events.js"
20
+ },
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ },
25
+ "./reducer": {
26
+ "types": "./dist/reducer.d.ts",
27
+ "default": "./dist/reducer.js"
28
+ },
29
+ "./state": {
30
+ "types": "./dist/state.d.ts",
31
+ "default": "./dist/state.js"
32
+ },
33
+ "./transactions": {
34
+ "types": "./dist/transactions.d.ts",
35
+ "default": "./dist/transactions.js"
36
+ },
37
+ "./package.json": "./package.json"
38
+ },
39
+ "engines": {
40
+ "node": ">=22"
41
+ },
42
+ "scripts": {
43
+ "build": "node --eval \"fs.rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json",
44
+ "typecheck": "tsc -p tsconfig.json --noEmit",
45
+ "check:package": "publint --strict && attw --pack . --profile esm-only",
46
+ "prepack": "npm run build"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "dependencies": {
52
+ "@interest-protocol/blast-admin-sdk": "0.1.0",
53
+ "@interest-protocol/blast-core": "0.1.0"
54
+ },
55
+ "peerDependencies": {
56
+ "@mysten/sui": "^2.26.2"
57
+ }
58
+ }
package/src/events.ts ADDED
@@ -0,0 +1,79 @@
1
+ import { suiObjectIdBcs, u64Bcs } from "@interest-protocol/blast-core/bcs";
2
+ import type { SuiEventPosition } from "@interest-protocol/blast-core/events";
3
+ import {
4
+ normalizeSuiEventPosition,
5
+ suiEventId,
6
+ } from "@interest-protocol/blast-core/events";
7
+ import { bcs } from "@mysten/sui/bcs";
8
+ import type { SuiClientTypes } from "@mysten/sui/client";
9
+ import {
10
+ normalizeStructTag,
11
+ normalizeSuiAddress,
12
+ parseStructTag,
13
+ } from "@mysten/sui/utils";
14
+ import { partnerTermsBcs } from "./state.ts";
15
+
16
+ /** The `blast_partners` events, in Move field order. */
17
+ export const partnerEventBcs = {
18
+ /** After every change, registration included: the partner's complete terms. */
19
+ PartnerUpdated: bcs.struct("PartnerUpdated", {
20
+ partner_id: suiObjectIdBcs,
21
+ ...partnerTermsBcs,
22
+ }),
23
+ /** `amount` of the event's coin type sent to the partner's recipient. */
24
+ PartnerPaid: bcs.struct("PartnerPaid", {
25
+ partner_id: suiObjectIdBcs,
26
+ amount: u64Bcs,
27
+ }),
28
+ };
29
+
30
+ export type PartnerEventKind = keyof typeof partnerEventBcs;
31
+
32
+ export type PartnerEventPayloads = {
33
+ [Kind in PartnerEventKind]: ReturnType<
34
+ (typeof partnerEventBcs)[Kind]["parse"]
35
+ >;
36
+ };
37
+
38
+ export type DecodedPartnerEvent = {
39
+ [Kind in PartnerEventKind]: {
40
+ source: "partners";
41
+ kind: Kind;
42
+ payload: PartnerEventPayloads[Kind];
43
+ /** `PartnerPaid` only: the paid coin type `T`. */
44
+ coinType: Kind extends "PartnerPaid" ? string : null;
45
+ sender: string;
46
+ position: SuiEventPosition;
47
+ };
48
+ }[PartnerEventKind];
49
+
50
+ /** Decodes one `<partnersPackageId>::blast_partners::<Kind>` event. */
51
+ export function decodePartnerEvent(
52
+ event: SuiClientTypes.EventEntry,
53
+ partnersPackageId: string,
54
+ ): DecodedPartnerEvent {
55
+ const tag = parseStructTag(event.eventType);
56
+ const kind = tag.name as PartnerEventKind;
57
+ const [coinType = null, ...extra] = tag.typeParams;
58
+ if (
59
+ tag.address !== normalizeSuiAddress(partnersPackageId) ||
60
+ tag.module !== "blast_partners" ||
61
+ !Object.hasOwn(partnerEventBcs, kind) ||
62
+ (coinType === null) === (kind === "PartnerPaid") ||
63
+ extra.length > 0
64
+ ) {
65
+ throw new TypeError(`unexpected partner event type ${event.eventType}`);
66
+ }
67
+ return {
68
+ source: "partners",
69
+ kind,
70
+ payload: partnerEventBcs[kind].parse(event.bcs),
71
+ coinType: coinType && normalizeStructTag(coinType),
72
+ sender: normalizeSuiAddress(event.sender),
73
+ position: normalizeSuiEventPosition(event),
74
+ } as DecodedPartnerEvent;
75
+ }
76
+
77
+ export function partnerEventId(event: DecodedPartnerEvent): string {
78
+ return suiEventId(event.position);
79
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./events.ts";
2
+ export * from "./reducer.ts";
3
+ export * from "./state.ts";
4
+ export * from "./transactions.ts";
package/src/reducer.ts ADDED
@@ -0,0 +1,75 @@
1
+ import type { DecodedPartnerEvent, PartnerEventKind } from "./events.ts";
2
+ import { partnerEventBcs, partnerEventId } from "./events.ts";
3
+ import type { PartnerTerms } from "./state.ts";
4
+
5
+ export type PartnerProjection = PartnerTerms & {
6
+ partnerId: string;
7
+ /** Everything sent to the partner, by coin type. */
8
+ paid: Readonly<Record<string, bigint>>;
9
+ };
10
+
11
+ /** Partners rebuilt from their events; terms match `PartnerState` once replayed. */
12
+ export type PartnersEventProjection = {
13
+ appliedEventIds: Readonly<Record<string, true>>;
14
+ eventCounts: Readonly<Record<PartnerEventKind, number>>;
15
+ partners: Readonly<Record<string, PartnerProjection>>;
16
+ };
17
+
18
+ export function createPartnersEventProjection(): PartnersEventProjection {
19
+ return {
20
+ appliedEventIds: {},
21
+ eventCounts: Object.fromEntries(
22
+ Object.keys(partnerEventBcs).map((kind) => [kind, 0]),
23
+ ) as Record<PartnerEventKind, number>,
24
+ partners: {},
25
+ };
26
+ }
27
+
28
+ /** Applies one event once; replaying an applied event returns `state` unchanged. */
29
+ export function reducePartnerEvent(
30
+ state: PartnersEventProjection,
31
+ event: DecodedPartnerEvent,
32
+ ): PartnersEventProjection {
33
+ const eventId = partnerEventId(event);
34
+ if (state.appliedEventIds[eventId]) return state;
35
+ const next: PartnersEventProjection = {
36
+ ...state,
37
+ appliedEventIds: { ...state.appliedEventIds, [eventId]: true },
38
+ eventCounts: {
39
+ ...state.eventCounts,
40
+ [event.kind]: state.eventCounts[event.kind] + 1,
41
+ },
42
+ };
43
+ const partnerId = event.payload.partner_id;
44
+ const current = state.partners[partnerId];
45
+ if (event.kind === "PartnerUpdated") {
46
+ const { recipient, share_bps, cap_id } = event.payload;
47
+ return withPartner(next, {
48
+ partnerId,
49
+ recipient,
50
+ shareBps: share_bps,
51
+ capId: cap_id,
52
+ paid: current?.paid ?? {},
53
+ });
54
+ }
55
+ // A partner is registered, with an update, before it can be paid.
56
+ if (!current) return next;
57
+ const coinType = event.coinType;
58
+ return withPartner(next, {
59
+ ...current,
60
+ paid: {
61
+ ...current.paid,
62
+ [coinType]: (current.paid[coinType] ?? 0n) + event.payload.amount,
63
+ },
64
+ });
65
+ }
66
+
67
+ function withPartner(
68
+ state: PartnersEventProjection,
69
+ partner: PartnerProjection,
70
+ ): PartnersEventProjection {
71
+ return {
72
+ ...state,
73
+ partners: { ...state.partners, [partner.partnerId]: partner },
74
+ };
75
+ }
package/src/state.ts ADDED
@@ -0,0 +1,120 @@
1
+ import {
2
+ addressBcs,
3
+ suiObjectIdBcs,
4
+ suiUidBcs,
5
+ u64Bcs,
6
+ } from "@interest-protocol/blast-core/bcs";
7
+ import {
8
+ BASIS_POINTS_DENOMINATOR,
9
+ mulDivDown,
10
+ normalizePositiveU64,
11
+ } from "@interest-protocol/blast-core/integers";
12
+ 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
+ import { bcs } from "@mysten/sui/bcs";
19
+ import type { ClientWithCoreApi } from "@mysten/sui/client";
20
+ import { normalizeSuiObjectId, parseStructTag } from "@mysten/sui/utils";
21
+
22
+ /** `blast_partners::delay_ms`: every change Blast makes to a partner waits this long. */
23
+ export const PARTNER_DELAY_MS = 604_800_000n;
24
+
25
+ /** The partner's recipient, scheduled share, and managing cap, as Move stores them. */
26
+ export type PartnerTerms = {
27
+ recipient: string;
28
+ /** Basis points of Blast's fee on each trade naming the partner. */
29
+ shareBps: Timelocked<bigint>;
30
+ /** The only `PartnerCap` that may set `recipient`. */
31
+ capId: Timelocked<string>;
32
+ };
33
+
34
+ export const partnerTermsBcs = {
35
+ recipient: addressBcs,
36
+ share_bps: timelockedBcs(u64Bcs),
37
+ cap_id: timelockedBcs(suiObjectIdBcs),
38
+ };
39
+
40
+ /** BCS layout of `blast_partners::blast_partners::Partner`. */
41
+ export const partnerBcs = bcs.struct("Partner", {
42
+ id: suiUidBcs,
43
+ ...partnerTermsBcs,
44
+ });
45
+
46
+ export type PartnerState = SharedObjectIdentity & PartnerTerms;
47
+
48
+ export type PartnerObject = SharedObjectIdentity & {
49
+ type: string;
50
+ content: Uint8Array;
51
+ };
52
+
53
+ export function decodePartnerObject(
54
+ object: PartnerObject,
55
+ partnersPackageId: string,
56
+ ): PartnerState {
57
+ const tag = parseStructTag(object.type);
58
+ if (
59
+ tag.address !== normalizeSuiObjectId(partnersPackageId) ||
60
+ tag.module !== "blast_partners" ||
61
+ tag.name !== "Partner"
62
+ ) {
63
+ throw new TypeError(`object ${object.objectId} is not a Blast partner`);
64
+ }
65
+ const parsed = partnerBcs.parse(object.content);
66
+ const objectId = normalizeSuiObjectId(object.objectId);
67
+ if (normalizeSuiObjectId(parsed.id.id.bytes) !== objectId) {
68
+ throw new TypeError(`partner ${object.objectId} has a mismatched UID`);
69
+ }
70
+ return {
71
+ objectId,
72
+ initialSharedVersion: normalizePositiveU64(
73
+ "initialSharedVersion",
74
+ object.initialSharedVersion,
75
+ ),
76
+ recipient: parsed.recipient,
77
+ shareBps: parsed.share_bps,
78
+ capId: parsed.cap_id,
79
+ };
80
+ }
81
+
82
+ export async function getPartner(
83
+ client: ClientWithCoreApi,
84
+ objectId: string,
85
+ partnersPackageId: string,
86
+ ): Promise<PartnerState> {
87
+ const { object } = await client.core.getObject({
88
+ objectId,
89
+ include: { content: true },
90
+ });
91
+ if (object.owner.$kind !== "Shared") {
92
+ throw new TypeError(`partner ${object.objectId} is not a shared object`);
93
+ }
94
+ return decodePartnerObject(
95
+ {
96
+ objectId: object.objectId,
97
+ initialSharedVersion: object.owner.Shared.initialSharedVersion,
98
+ type: object.type,
99
+ content: object.content,
100
+ },
101
+ partnersPackageId,
102
+ );
103
+ }
104
+
105
+ /**
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.
109
+ */
110
+ export function partnerPayment(
111
+ partner: Pick<PartnerTerms, "shareBps">,
112
+ fee: bigint,
113
+ nowMs: bigint,
114
+ ): bigint {
115
+ return mulDivDown(
116
+ fee,
117
+ timelockedValue(partner.shareBps, nowMs),
118
+ BASIS_POINTS_DENOMINATOR,
119
+ );
120
+ }
@@ -0,0 +1,239 @@
1
+ import type { AdminDeployment } from "@interest-protocol/blast-admin-sdk/transactions";
2
+ import {
3
+ AdminCalls,
4
+ normalizeAdminDeployment,
5
+ } from "@interest-protocol/blast-admin-sdk/transactions";
6
+ import { requireBasisPoints } from "@interest-protocol/blast-core/integers";
7
+ import type { SharedObjectReference } from "@interest-protocol/blast-core/sui";
8
+ import { normalizeSharedObjectReference } from "@interest-protocol/blast-core/sui";
9
+ import type {
10
+ TransactionObjectArgument,
11
+ TransactionObjectInput,
12
+ TransactionResult,
13
+ } from "@mysten/sui/transactions";
14
+ import { Transaction } from "@mysten/sui/transactions";
15
+ import { normalizeSuiAddress, normalizeSuiObjectId } from "@mysten/sui/utils";
16
+
17
+ /** The immutable `blast_partners` package and the ACL its `PartnerAdmin` signs in to. */
18
+ export type PartnersDeployment = {
19
+ packageId: string;
20
+ admin: AdminDeployment;
21
+ };
22
+
23
+ /** `blast_partners::blast_partners` calls. */
24
+ export class PartnersCalls {
25
+ readonly deployment: PartnersDeployment;
26
+ readonly admin: AdminCalls;
27
+
28
+ constructor(deployment: PartnersDeployment) {
29
+ this.deployment = {
30
+ packageId: normalizeSuiObjectId(deployment.packageId),
31
+ admin: normalizeAdminDeployment(deployment.admin),
32
+ };
33
+ this.admin = new AdminCalls(this.deployment.admin);
34
+ }
35
+
36
+ /** The `PartnerAdmin` role type, for `AdminCalls.grantRole`. */
37
+ roleType(): string {
38
+ return `${this.deployment.packageId}::blast_partners::PartnerAdmin`;
39
+ }
40
+
41
+ /** This PTB's `AdminWitness<PartnerAdmin>`. */
42
+ signIn(options: { adminCap: TransactionObjectInput }) {
43
+ return this.admin.signIn({
44
+ adminCap: options.adminCap,
45
+ role: this.roleType(),
46
+ });
47
+ }
48
+
49
+ /** `new_cap`: anyone may mint one; it controls nothing until a partner names it. */
50
+ newCap() {
51
+ return (tx: Transaction): TransactionResult =>
52
+ tx.moveCall({ target: this.#target("new_cap") });
53
+ }
54
+
55
+ /**
56
+ * `register` (`PartnerAdmin`): shares a partner managed by the cap
57
+ * `capId`, its share and cap in force at once.
58
+ */
59
+ register(options: {
60
+ adminWitness: TransactionObjectInput;
61
+ capId: string;
62
+ recipient: string;
63
+ shareBps: bigint;
64
+ }) {
65
+ const capId = normalizeSuiObjectId(options.capId);
66
+ const recipient = normalizeSuiAddress(options.recipient);
67
+ const shareBps = requireBasisPoints("shareBps", options.shareBps);
68
+ return (tx: Transaction): void => {
69
+ tx.moveCall({
70
+ target: this.#target("register"),
71
+ arguments: [
72
+ tx.object(options.adminWitness),
73
+ tx.pure.id(capId),
74
+ tx.pure.address(recipient),
75
+ tx.pure.u64(shareBps),
76
+ ],
77
+ });
78
+ };
79
+ }
80
+
81
+ /** `schedule_share` (`PartnerAdmin`): the share takes effect after the partner delay. */
82
+ scheduleShare(options: {
83
+ partner: SharedObjectReference<true>;
84
+ adminWitness: TransactionObjectInput;
85
+ shareBps: bigint;
86
+ }) {
87
+ const shareBps = requireBasisPoints("shareBps", options.shareBps);
88
+ return (tx: Transaction): void => {
89
+ tx.moveCall({
90
+ target: this.#target("schedule_share"),
91
+ arguments: [
92
+ partner(tx, options.partner),
93
+ tx.object(options.adminWitness),
94
+ tx.pure.u64(shareBps),
95
+ tx.object.clock(),
96
+ ],
97
+ });
98
+ };
99
+ }
100
+
101
+ /** `schedule_cap` (`PartnerAdmin`): moves the partner to the cap `capId` after the delay. */
102
+ scheduleCap(options: {
103
+ partner: SharedObjectReference<true>;
104
+ adminWitness: TransactionObjectInput;
105
+ capId: string;
106
+ }) {
107
+ const capId = normalizeSuiObjectId(options.capId);
108
+ return (tx: Transaction): void => {
109
+ tx.moveCall({
110
+ target: this.#target("schedule_cap"),
111
+ arguments: [
112
+ partner(tx, options.partner),
113
+ tx.object(options.adminWitness),
114
+ tx.pure.id(capId),
115
+ tx.object.clock(),
116
+ ],
117
+ });
118
+ };
119
+ }
120
+
121
+ /** `set_recipient`: the partner's live cap sets where it is paid, at once. */
122
+ setRecipient(options: {
123
+ partner: SharedObjectReference<true>;
124
+ partnerCap: TransactionObjectInput;
125
+ recipient: string;
126
+ }) {
127
+ const recipient = normalizeSuiAddress(options.recipient);
128
+ return (tx: Transaction): void => {
129
+ tx.moveCall({
130
+ target: this.#target("set_recipient"),
131
+ arguments: [
132
+ partner(tx, options.partner),
133
+ tx.object(options.partnerCap),
134
+ tx.pure.address(recipient),
135
+ tx.object.clock(),
136
+ ],
137
+ });
138
+ };
139
+ }
140
+
141
+ destroyCap(options: { partnerCap: TransactionObjectInput }) {
142
+ return (tx: Transaction): void => {
143
+ tx.moveCall({
144
+ target: this.#target("destroy_cap"),
145
+ arguments: [tx.object(options.partnerCap)],
146
+ });
147
+ };
148
+ }
149
+
150
+ #target(functionName: string): string {
151
+ return `${this.deployment.packageId}::blast_partners::${functionName}`;
152
+ }
153
+ }
154
+
155
+ export class PartnersTransactions {
156
+ readonly call: PartnersCalls;
157
+
158
+ constructor(deployment: PartnersDeployment) {
159
+ this.call = new PartnersCalls(deployment);
160
+ }
161
+
162
+ /** Mints a `PartnerCap` and sends it to `recipient`. */
163
+ newCap(options: { recipient: string }): Transaction {
164
+ const tx = new Transaction();
165
+ const cap = tx.add(this.call.newCap());
166
+ tx.transferObjects([cap], normalizeSuiAddress(options.recipient));
167
+ return tx;
168
+ }
169
+
170
+ register(options: {
171
+ adminCapId: string;
172
+ capId: string;
173
+ recipient: string;
174
+ shareBps: bigint;
175
+ }): Transaction {
176
+ return this.#asPartnerAdmin(options.adminCapId, (adminWitness) =>
177
+ this.call.register({ ...options, adminWitness }),
178
+ );
179
+ }
180
+
181
+ scheduleShare(options: {
182
+ adminCapId: string;
183
+ partner: SharedObjectReference<true>;
184
+ shareBps: bigint;
185
+ }): Transaction {
186
+ return this.#asPartnerAdmin(options.adminCapId, (adminWitness) =>
187
+ this.call.scheduleShare({ ...options, adminWitness }),
188
+ );
189
+ }
190
+
191
+ scheduleCap(options: {
192
+ adminCapId: string;
193
+ partner: SharedObjectReference<true>;
194
+ capId: string;
195
+ }): Transaction {
196
+ return this.#asPartnerAdmin(options.adminCapId, (adminWitness) =>
197
+ this.call.scheduleCap({ ...options, adminWitness }),
198
+ );
199
+ }
200
+
201
+ setRecipient(options: {
202
+ partner: SharedObjectReference<true>;
203
+ partnerCapId: string;
204
+ recipient: string;
205
+ }): Transaction {
206
+ const tx = new Transaction();
207
+ tx.add(
208
+ this.call.setRecipient({ ...options, partnerCap: options.partnerCapId }),
209
+ );
210
+ return tx;
211
+ }
212
+
213
+ destroyCap(options: { partnerCapId: string }): Transaction {
214
+ const tx = new Transaction();
215
+ tx.add(this.call.destroyCap({ partnerCap: options.partnerCapId }));
216
+ return tx;
217
+ }
218
+
219
+ #asPartnerAdmin(
220
+ adminCapId: string,
221
+ call: (
222
+ adminWitness: TransactionObjectArgument,
223
+ ) => (tx: Transaction) => void,
224
+ ): Transaction {
225
+ const tx = new Transaction();
226
+ const adminWitness = tx.add(this.call.signIn({ adminCap: adminCapId }));
227
+ tx.add(call(adminWitness));
228
+ return tx;
229
+ }
230
+ }
231
+
232
+ function partner(
233
+ tx: Transaction,
234
+ reference: SharedObjectReference<true>,
235
+ ): TransactionObjectArgument {
236
+ return tx.sharedObjectRef(
237
+ normalizeSharedObjectReference("partner", reference, true),
238
+ );
239
+ }