@interest-protocol/blast-migrators-sdk 0.0.0-stage → 0.1.1

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/package.json CHANGED
@@ -1,6 +1,70 @@
1
1
  {
2
2
  "name": "@interest-protocol/blast-migrators-sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Blast migrators SDK",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/interest-protocol/blast-v2-sdk.git",
9
+ "directory": "packages/migrators"
10
+ },
11
+ "sideEffects": false,
12
+ "files": [
13
+ "dist",
14
+ "src"
15
+ ],
16
+ "exports": {
17
+ "./bluefin-pool": {
18
+ "types": "./dist/bluefin-pool.d.ts",
19
+ "default": "./dist/bluefin-pool.js"
20
+ },
21
+ "./cetus-pool": {
22
+ "types": "./dist/cetus-pool.d.ts",
23
+ "default": "./dist/cetus-pool.js"
24
+ },
25
+ "./client": {
26
+ "types": "./dist/client.d.ts",
27
+ "default": "./dist/client.js"
28
+ },
29
+ "./events": {
30
+ "types": "./dist/events.d.ts",
31
+ "default": "./dist/events.js"
32
+ },
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.js"
36
+ },
37
+ "./reducer": {
38
+ "types": "./dist/reducer.d.ts",
39
+ "default": "./dist/reducer.js"
40
+ },
41
+ "./state": {
42
+ "types": "./dist/state.d.ts",
43
+ "default": "./dist/state.js"
44
+ },
45
+ "./transactions": {
46
+ "types": "./dist/transactions.d.ts",
47
+ "default": "./dist/transactions.js"
48
+ },
49
+ "./package.json": "./package.json"
50
+ },
51
+ "engines": {
52
+ "node": ">=22"
53
+ },
54
+ "scripts": {
55
+ "build": "node --eval \"fs.rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json",
56
+ "typecheck": "tsc -p tsconfig.json --noEmit",
57
+ "check:package": "publint --strict && attw --pack . --profile esm-only",
58
+ "prepack": "npm run build"
59
+ },
60
+ "publishConfig": {
61
+ "access": "public"
62
+ },
63
+ "dependencies": {
64
+ "@interest-protocol/blast-core": "0.1.0",
65
+ "@interest-protocol/blast-launchpad-sdk": "0.1.1"
66
+ },
67
+ "peerDependencies": {
68
+ "@mysten/sui": "^2.26.2"
69
+ }
70
+ }
@@ -0,0 +1,180 @@
1
+ import { requireU64 } from "@interest-protocol/blast-core/integers";
2
+ import { moveResults } from "@interest-protocol/blast-core/ptb";
3
+ import type { SharedObjectReference } from "@interest-protocol/blast-core/sui";
4
+ import { normalizeSharedObjectReference } from "@interest-protocol/blast-core/sui";
5
+ import type { LaunchTypes } from "@interest-protocol/blast-launchpad-sdk/transactions";
6
+ import { normalizeLaunchTypes } from "@interest-protocol/blast-launchpad-sdk/transactions";
7
+ import type {
8
+ Transaction,
9
+ TransactionObjectArgument,
10
+ TransactionObjectInput,
11
+ } from "@mysten/sui/transactions";
12
+ import { normalizeSuiAddress, SUI_TYPE_ARG } from "@mysten/sui/utils";
13
+
14
+ /** Bluefin's pool metadata; amounts and prices always use atomic coin units. */
15
+ export type BluefinCoinMetadata = {
16
+ symbol: string;
17
+ decimals: number;
18
+ iconUrl?: string;
19
+ };
20
+
21
+ export type BluefinPoolCreation = LaunchTypes & {
22
+ /** Runtime package and config from the deployment's Bluefin venue identities. */
23
+ bluefinPackageId: string;
24
+ bluefinConfig: SharedObjectReference<true>;
25
+ memeMetadata: BluefinCoinMetadata;
26
+ quoteMetadata: BluefinCoinMetadata;
27
+ /** Optional pool NFT icon. The name is `<Meme symbol>-<Quote symbol>`. */
28
+ iconUrl?: string;
29
+ tickSpacing: number;
30
+ /** Bluefin fee units: 1,000,000 is 100%. */
31
+ feeRate: bigint;
32
+ /** Q64.64 square root of Quote atoms per Meme atom. */
33
+ sqrtPriceX64: bigint;
34
+ /** SUI atoms (MIST), read from the current config; never assumed zero. */
35
+ creationFee: bigint;
36
+ /** Signed ticks, aligned to tickSpacing; encoded as i32 two's-complement bits. */
37
+ lowerTick: number;
38
+ upperTick: number;
39
+ /** Coins consumed; their balances bound each side's maximum contribution. */
40
+ memeCoin: TransactionObjectInput;
41
+ quoteCoin: TransactionObjectInput;
42
+ /** Atomic amount on the fixed side. Bluefin calculates the other side. */
43
+ fixedAmount: bigint;
44
+ fixedMeme: boolean;
45
+ };
46
+
47
+ export type BluefinPoolCreationResult = {
48
+ /** Fresh owned Pool<Meme, Quote>. Call shareBluefinPool after composing. */
49
+ pool: TransactionObjectArgument;
50
+ /** Position to pass to PresaleCalls.newBluefinVault, or transfer to its owner. */
51
+ position: TransactionObjectArgument;
52
+ /** Residual coins, including zero coins; callers must consume or transfer both. */
53
+ memeResidual: TransactionObjectArgument;
54
+ quoteResidual: TransactionObjectArgument;
55
+ };
56
+
57
+ /**
58
+ * Creates Pool<Meme, Quote>, opens a position, and adds fixed-side liquidity.
59
+ * Mirrors the pinned Bluefin Spot interface (91ce930). No presale is needed.
60
+ * The fresh pool remains usable in this PTB, e.g. by newBluefinVault; the
61
+ * caller must then share it, consume the position, and handle both residuals.
62
+ * The input coins cap spending; Bluefin aborts if the other side is too small.
63
+ */
64
+ export function addBluefinPoolWithLiquidity(
65
+ tx: Transaction,
66
+ options: BluefinPoolCreation,
67
+ ): BluefinPoolCreationResult {
68
+ const types = normalizeLaunchTypes(options);
69
+ const typeArguments = [types.memeType, types.quoteType];
70
+ const target = (name: string) =>
71
+ `${normalizeSuiAddress(options.bluefinPackageId)}::pool::${name}`;
72
+ const config = tx.sharedObjectRef(
73
+ normalizeSharedObjectReference(
74
+ "bluefinConfig",
75
+ options.bluefinConfig,
76
+ true,
77
+ ),
78
+ );
79
+ const bytes = (value: string) =>
80
+ tx.pure.vector("u8", new TextEncoder().encode(value));
81
+ const balance = (coin: TransactionObjectInput, type: string) =>
82
+ tx.moveCall({
83
+ target: "0x2::coin::into_balance",
84
+ typeArguments: [type],
85
+ arguments: [tx.object(coin)],
86
+ });
87
+ const creationFee = requireU64("creationFee", options.creationFee);
88
+ const fee =
89
+ creationFee === 0n
90
+ ? tx.moveCall({
91
+ target: "0x2::balance::zero",
92
+ typeArguments: [SUI_TYPE_ARG],
93
+ })
94
+ : balance(
95
+ tx.coin({ type: SUI_TYPE_ARG, balance: creationFee }),
96
+ SUI_TYPE_ARG,
97
+ );
98
+ const pool = tx.moveCall({
99
+ target: target("create_pool_and_get_object"),
100
+ typeArguments: [...typeArguments, SUI_TYPE_ARG],
101
+ arguments: [
102
+ tx.object.clock(),
103
+ config,
104
+ bytes(`${options.memeMetadata.symbol}-${options.quoteMetadata.symbol}`),
105
+ bytes(options.iconUrl ?? ""),
106
+ bytes(options.memeMetadata.symbol),
107
+ tx.pure.u8(options.memeMetadata.decimals),
108
+ bytes(options.memeMetadata.iconUrl ?? ""),
109
+ bytes(options.quoteMetadata.symbol),
110
+ tx.pure.u8(options.quoteMetadata.decimals),
111
+ bytes(options.quoteMetadata.iconUrl ?? ""),
112
+ tx.pure.u32(options.tickSpacing),
113
+ tx.pure.u64(options.feeRate),
114
+ tx.pure.u128(options.sqrtPriceX64),
115
+ fee,
116
+ ],
117
+ });
118
+ const position = tx.moveCall({
119
+ target: target("open_position"),
120
+ typeArguments,
121
+ arguments: [
122
+ config,
123
+ pool,
124
+ tx.pure.u32(tickBits(options.lowerTick)),
125
+ tx.pure.u32(tickBits(options.upperTick)),
126
+ ],
127
+ });
128
+ const [, , memeResidual, quoteResidual] = moveResults(
129
+ tx.moveCall({
130
+ target: target("add_liquidity_with_fixed_amount"),
131
+ typeArguments,
132
+ arguments: [
133
+ tx.object.clock(),
134
+ config,
135
+ pool,
136
+ position,
137
+ balance(options.memeCoin, types.memeType),
138
+ balance(options.quoteCoin, types.quoteType),
139
+ tx.pure.u64(options.fixedAmount),
140
+ tx.pure.bool(options.fixedMeme),
141
+ ],
142
+ }),
143
+ 4,
144
+ );
145
+ const coin = (residual: TransactionObjectArgument, type: string) =>
146
+ tx.moveCall({
147
+ target: "0x2::coin::from_balance",
148
+ typeArguments: [type],
149
+ arguments: [residual],
150
+ });
151
+ return {
152
+ pool,
153
+ position,
154
+ memeResidual: coin(memeResidual, types.memeType),
155
+ quoteResidual: coin(quoteResidual, types.quoteType),
156
+ };
157
+ }
158
+
159
+ /** Shares a fresh Bluefin pool after all calls that borrow it in this PTB. */
160
+ export function shareBluefinPool(
161
+ tx: Transaction,
162
+ options: LaunchTypes & {
163
+ bluefinPackageId: string;
164
+ pool: TransactionObjectInput;
165
+ },
166
+ ): void {
167
+ const { memeType, quoteType } = normalizeLaunchTypes(options);
168
+ tx.moveCall({
169
+ target: `${normalizeSuiAddress(options.bluefinPackageId)}::pool::share_pool_object`,
170
+ typeArguments: [memeType, quoteType],
171
+ arguments: [tx.object(options.pool)],
172
+ });
173
+ }
174
+
175
+ function tickBits(tick: number): number {
176
+ if (!Number.isInteger(tick) || tick < -(2 ** 31) || tick > 2 ** 31 - 1) {
177
+ throw new RangeError("tick must be an i32");
178
+ }
179
+ return tick >>> 0;
180
+ }
@@ -0,0 +1,107 @@
1
+ import { cetusMemeSide } from "@interest-protocol/blast-core/cetus";
2
+ import { grossGasBudget } from "@interest-protocol/blast-core/gas";
3
+ import { moveResults } from "@interest-protocol/blast-core/ptb";
4
+ import type { SharedObjectReference } from "@interest-protocol/blast-core/sui";
5
+ import { normalizeSharedObjectReference } from "@interest-protocol/blast-core/sui";
6
+ import type { LaunchTypes } from "@interest-protocol/blast-launchpad-sdk/transactions";
7
+ import { normalizeLaunchTypes } from "@interest-protocol/blast-launchpad-sdk/transactions";
8
+ import type {
9
+ Transaction,
10
+ TransactionObjectArgument,
11
+ TransactionObjectInput,
12
+ } from "@mysten/sui/transactions";
13
+ import { normalizeSuiAddress } from "@mysten/sui/utils";
14
+
15
+ export type CetusPoolCreation = LaunchTypes & {
16
+ /** Runtime package and shared objects from the deployment's Cetus identities. */
17
+ cetusPackageId: string;
18
+ cetusConfig: SharedObjectReference<false>;
19
+ cetusPools: SharedObjectReference<true>;
20
+ tickSpacing: number;
21
+ /** Q64.64 square root of coin B atoms per coin A atom, in Cetus coin order. */
22
+ sqrtPriceX64: bigint;
23
+ /** Pool/position metadata URL; empty by default. */
24
+ url?: string;
25
+ /** Signed ticks aligned to tickSpacing, encoded as i32 two's-complement bits. */
26
+ lowerTick: number;
27
+ upperTick: number;
28
+ /**
29
+ * Coins consumed. The fixed side uses this coin's entire balance; the other
30
+ * coin caps the maximum contribution. Split exact input coins beforehand.
31
+ */
32
+ memeCoin: TransactionObjectInput;
33
+ quoteCoin: TransactionObjectInput;
34
+ fixedMeme: boolean;
35
+ /** Required only for a pair/tier already reserved by this Cetus creation cap. */
36
+ poolCreationCap?: TransactionObjectInput;
37
+ };
38
+
39
+ export type CetusPoolCreationResult = {
40
+ /** Transfer to its owner; newCetusVault can consume it in the next PTB. */
41
+ position: TransactionObjectArgument;
42
+ /** Residual Coins, including zero coins; consume or transfer both. */
43
+ memeResidual: TransactionObjectArgument;
44
+ quoteResidual: TransactionObjectArgument;
45
+ };
46
+
47
+ /**
48
+ * Creates and seeds a Cetus pool through the revision-15 pool_creator v3 API.
49
+ * Coin types, input coins, fixed side and residuals follow cetusMemeSide.
50
+ * Cetus shares the pool internally; its public API returns no pool handle.
51
+ * Execute this PTB, read the shared pool/position IDs, then call newCetusVault
52
+ * in a later PTB. No presale or TreasuryCap is needed for an unreserved pair.
53
+ * ABI: CetusProtocol/cetus-contracts ddfbdeba, pool_creator.move and factory.move;
54
+ * runtime package 0x260693ec785a6e6c9d81d58c7d2ff72f1288ae0fa6a9725abe05a6478b11f084.
55
+ */
56
+ export function addCetusPoolWithLiquidity(
57
+ tx: Transaction,
58
+ options: CetusPoolCreation,
59
+ ): CetusPoolCreationResult {
60
+ const { memeType, quoteType } = normalizeLaunchTypes(options);
61
+ const memeIsA = cetusMemeSide(memeType, quoteType) === "a";
62
+ const typeArguments = memeIsA ? [memeType, quoteType] : [quoteType, memeType];
63
+ const lowerTick = tickBits(options.lowerTick);
64
+ const upperTick = tickBits(options.upperTick);
65
+ const config = tx.sharedObjectRef(
66
+ normalizeSharedObjectReference("cetusConfig", options.cetusConfig, false),
67
+ );
68
+ const pools = tx.sharedObjectRef(
69
+ normalizeSharedObjectReference("cetusPools", options.cetusPools, true),
70
+ );
71
+ tx.addBuildPlugin(grossGasBudget());
72
+ const [position, residualA, residualB] = moveResults(
73
+ tx.moveCall({
74
+ target: `${normalizeSuiAddress(options.cetusPackageId)}::pool_creator::${options.poolCreationCap === undefined ? "create_pool_v3" : "create_pool_v3_with_creation_cap"}`,
75
+ typeArguments,
76
+ arguments: [
77
+ config,
78
+ pools,
79
+ ...(options.poolCreationCap === undefined
80
+ ? []
81
+ : [tx.object(options.poolCreationCap)]),
82
+ tx.pure.u32(options.tickSpacing),
83
+ tx.pure.u128(options.sqrtPriceX64),
84
+ tx.pure.string(options.url ?? ""),
85
+ tx.pure.u32(lowerTick),
86
+ tx.pure.u32(upperTick),
87
+ tx.object(memeIsA ? options.memeCoin : options.quoteCoin),
88
+ tx.object(memeIsA ? options.quoteCoin : options.memeCoin),
89
+ tx.pure.bool(memeIsA === options.fixedMeme),
90
+ tx.object.clock(),
91
+ ],
92
+ }),
93
+ 3,
94
+ );
95
+ return {
96
+ position,
97
+ memeResidual: memeIsA ? residualA : residualB,
98
+ quoteResidual: memeIsA ? residualB : residualA,
99
+ };
100
+ }
101
+
102
+ function tickBits(tick: number): number {
103
+ if (!Number.isInteger(tick) || tick < -(2 ** 31) || tick > 2 ** 31 - 1) {
104
+ throw new RangeError("tick must be an i32");
105
+ }
106
+ return tick >>> 0;
107
+ }
package/src/client.ts ADDED
@@ -0,0 +1,76 @@
1
+ import type { CoinSourceOptions } from "@interest-protocol/blast-core/coins";
2
+ import type { RewardRoundState } from "@interest-protocol/blast-core/rewards/state";
3
+ import { getRewardRound } from "@interest-protocol/blast-core/rewards/state";
4
+ import type {
5
+ ClientWithCoreApi,
6
+ SuiClientRegistration,
7
+ } from "@mysten/sui/client";
8
+ import type { LpFeePolicyState, LpFeeShares, LpVaultState } from "./state.ts";
9
+ import { getLpFeePolicy, getLpFeeShares, getLpVault } from "./state.ts";
10
+ import type { MigratorsDeployment } from "./transactions.ts";
11
+ import { MigratorCalls, MigratorTransactions } from "./transactions.ts";
12
+
13
+ export class MigratorsClient {
14
+ readonly call: MigratorCalls;
15
+ readonly tx: MigratorTransactions;
16
+ readonly #client: ClientWithCoreApi;
17
+
18
+ constructor(
19
+ client: ClientWithCoreApi,
20
+ deployment: MigratorsDeployment,
21
+ options: CoinSourceOptions = {},
22
+ ) {
23
+ this.#client = client;
24
+ this.call = new MigratorCalls(deployment);
25
+ this.tx = new MigratorTransactions(deployment, options);
26
+ }
27
+
28
+ async getLpVault(objectId: string): Promise<LpVaultState> {
29
+ return getLpVault(
30
+ this.#client,
31
+ objectId,
32
+ this.call.deployment.originalPackageId,
33
+ );
34
+ }
35
+
36
+ async getLpFeePolicy(): Promise<LpFeePolicyState> {
37
+ return getLpFeePolicy(
38
+ this.#client,
39
+ this.call.deployment.lpFeePolicy.objectId,
40
+ this.call.deployment.originalPackageId,
41
+ );
42
+ }
43
+
44
+ /** Blast's live LP-fee shares for `memeType`: its own pair, else the defaults. */
45
+ async getLpFeeShares(
46
+ memeType: string,
47
+ ): Promise<LpFeeShares & { source: "coin" | "default" }> {
48
+ return getLpFeeShares(this.#client, await this.getLpFeePolicy(), memeType);
49
+ }
50
+
51
+ async getHolderRewardRound(objectId: string): Promise<RewardRoundState> {
52
+ return getRewardRound(
53
+ this.#client,
54
+ objectId,
55
+ this.call.deployment.originalPackageId,
56
+ );
57
+ }
58
+ }
59
+
60
+ export type MigratorsExtensionOptions<Name extends string> =
61
+ MigratorsDeployment & { name?: Name };
62
+
63
+ /** Adds a typed `blastMigrators` extension to any Sui Core API client. */
64
+ export function blastMigrators<const Name extends string = "blastMigrators">({
65
+ name = "blastMigrators" as Name,
66
+ ...deployment
67
+ }: MigratorsExtensionOptions<Name>): SuiClientRegistration<
68
+ ClientWithCoreApi,
69
+ Name,
70
+ MigratorsClient
71
+ > {
72
+ return {
73
+ name,
74
+ register: (client) => new MigratorsClient(client, deployment),
75
+ };
76
+ }