naracli 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 +155 -0
- package/bin/nara-cli.ts +32 -0
- package/dist/nara-cli.mjs +2631 -0
- package/dist/quest/nara_quest.json +534 -0
- package/dist/zk/answer_proof.wasm +0 -0
- package/dist/zk/answer_proof_final.zkey +0 -0
- package/index.ts +76 -0
- package/package.json +54 -0
- package/src/cli/commands/config.ts +125 -0
- package/src/cli/commands/migrate.ts +270 -0
- package/src/cli/commands/pool.ts +364 -0
- package/src/cli/commands/quest.ts +312 -0
- package/src/cli/commands/swap.ts +349 -0
- package/src/cli/commands/wallet.ts +719 -0
- package/src/cli/index.ts +25 -0
- package/src/cli/quest/nara_quest.json +534 -0
- package/src/cli/quest/nara_quest_types.ts +540 -0
- package/src/cli/types.ts +207 -0
- package/src/cli/utils/output.ts +110 -0
- package/src/cli/utils/transaction.ts +146 -0
- package/src/cli/utils/validation.ts +120 -0
- package/src/cli/utils/wallet.ts +72 -0
- package/src/cli/zk/answer_proof.wasm +0 -0
- package/src/cli/zk/answer_proof_final.zkey +0 -0
- package/src/client.ts +96 -0
- package/src/config.ts +132 -0
- package/src/constants.ts +29 -0
- package/src/migrate.ts +222 -0
- package/src/pool.ts +259 -0
- package/src/quest.ts +379 -0
- package/src/swap.ts +608 -0
- package/src/types/snarkjs.d.ts +9 -0
package/src/client.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Connection,
|
|
3
|
+
PublicKey,
|
|
4
|
+
Transaction,
|
|
5
|
+
AddressLookupTableAccount,
|
|
6
|
+
MessageV0,
|
|
7
|
+
VersionedTransaction,
|
|
8
|
+
} from "@solana/web3.js";
|
|
9
|
+
import { DynamicBondingCurveClient } from "@meteora-ag/dynamic-bonding-curve-sdk";
|
|
10
|
+
|
|
11
|
+
export interface NaraSDKConfig {
|
|
12
|
+
rpcUrl: string;
|
|
13
|
+
commitment?: "processed" | "confirmed" | "finalized";
|
|
14
|
+
/** Address Lookup Table addresses array for compressing transaction size */
|
|
15
|
+
addressLookupTableAddresses?: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class NaraSDK {
|
|
19
|
+
private connection: Connection;
|
|
20
|
+
private client: DynamicBondingCurveClient;
|
|
21
|
+
private addressLookupTableAddresses: PublicKey[];
|
|
22
|
+
|
|
23
|
+
constructor(config: NaraSDKConfig) {
|
|
24
|
+
this.connection = new Connection(
|
|
25
|
+
config.rpcUrl,
|
|
26
|
+
config.commitment || "confirmed"
|
|
27
|
+
);
|
|
28
|
+
this.client = new DynamicBondingCurveClient(
|
|
29
|
+
this.connection,
|
|
30
|
+
config.commitment || "confirmed"
|
|
31
|
+
);
|
|
32
|
+
this.addressLookupTableAddresses = (
|
|
33
|
+
config.addressLookupTableAddresses || []
|
|
34
|
+
).map((addr) => new PublicKey(addr));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getConnection(): Connection {
|
|
38
|
+
return this.connection;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getClient(): DynamicBondingCurveClient {
|
|
42
|
+
return this.client;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getAddressLookupTableAddresses(): PublicKey[] {
|
|
46
|
+
return this.addressLookupTableAddresses;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Compile transaction with Address Lookup Tables
|
|
51
|
+
* Converts Transaction to VersionedTransaction if ALT is configured
|
|
52
|
+
* @param transaction Original transaction
|
|
53
|
+
* @param feePayer Fee payer public key
|
|
54
|
+
* @returns VersionedTransaction or original Transaction
|
|
55
|
+
*/
|
|
56
|
+
async compileTransactionWithALT(
|
|
57
|
+
transaction: Transaction,
|
|
58
|
+
feePayer: PublicKey
|
|
59
|
+
): Promise<Transaction | VersionedTransaction> {
|
|
60
|
+
if (this.addressLookupTableAddresses.length === 0) {
|
|
61
|
+
// No ALT configured, return original transaction
|
|
62
|
+
return transaction;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Fetch Address Lookup Table accounts
|
|
66
|
+
const lookupTableAccounts: AddressLookupTableAccount[] = [];
|
|
67
|
+
for (const address of this.addressLookupTableAddresses) {
|
|
68
|
+
const accountInfo =
|
|
69
|
+
await this.connection.getAddressLookupTable(address);
|
|
70
|
+
if (accountInfo.value) {
|
|
71
|
+
lookupTableAccounts.push(accountInfo.value);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (lookupTableAccounts.length === 0) {
|
|
76
|
+
// No valid ALT accounts, return original transaction
|
|
77
|
+
return transaction;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Get latest blockhash
|
|
81
|
+
const { blockhash } = await this.connection.getLatestBlockhash(
|
|
82
|
+
this.connection.commitment
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Create MessageV0
|
|
86
|
+
const messageV0 = MessageV0.compile({
|
|
87
|
+
payerKey: feePayer,
|
|
88
|
+
instructions: transaction.instructions,
|
|
89
|
+
recentBlockhash: blockhash,
|
|
90
|
+
addressLookupTableAccounts: lookupTableAccounts,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Create VersionedTransaction
|
|
94
|
+
return new VersionedTransaction(messageV0);
|
|
95
|
+
}
|
|
96
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Keypair, PublicKey, Transaction } from "@solana/web3.js";
|
|
2
|
+
import {
|
|
3
|
+
buildCurveWithMarketCap,
|
|
4
|
+
ActivationType,
|
|
5
|
+
CollectFeeMode,
|
|
6
|
+
BaseFeeMode,
|
|
7
|
+
MigrationFeeOption,
|
|
8
|
+
MigrationOption,
|
|
9
|
+
TokenDecimal,
|
|
10
|
+
TokenType,
|
|
11
|
+
TokenUpdateAuthorityOption,
|
|
12
|
+
} from "@meteora-ag/dynamic-bonding-curve-sdk";
|
|
13
|
+
import { NATIVE_MINT } from "@solana/spl-token";
|
|
14
|
+
import { NaraSDK } from "./client";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Options for creating configuration
|
|
18
|
+
*/
|
|
19
|
+
export interface CreateConfigOptions {
|
|
20
|
+
/** Fee claimer wallet address */
|
|
21
|
+
feeClaimer: PublicKey;
|
|
22
|
+
/** Leftover token receiver wallet address */
|
|
23
|
+
leftoverReceiver: PublicKey;
|
|
24
|
+
/** Payer wallet address */
|
|
25
|
+
payer: PublicKey;
|
|
26
|
+
// Curve parameters (all optional with defaults)
|
|
27
|
+
/** Total token supply (default: 1,000,000,000) */
|
|
28
|
+
totalTokenSupply?: number;
|
|
29
|
+
/** Initial market cap (default: 30) */
|
|
30
|
+
initialMarketCap?: number;
|
|
31
|
+
/** Migration market cap threshold (default: 540) */
|
|
32
|
+
migrationMarketCap?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Return type for bonding curve config transaction creation
|
|
37
|
+
*/
|
|
38
|
+
export interface CreateConfigResult {
|
|
39
|
+
/** Config address */
|
|
40
|
+
configAddress: string;
|
|
41
|
+
/** Unsigned transaction */
|
|
42
|
+
transaction: Transaction;
|
|
43
|
+
/** Config keypair (requires partial signature) */
|
|
44
|
+
configKeypair: Keypair;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create bonding curve config transaction (returns unsigned transaction)
|
|
49
|
+
* @param sdk NaraSDK SDK instance
|
|
50
|
+
* @param options Configuration options
|
|
51
|
+
* @returns Config address, unsigned transaction, and config keypair
|
|
52
|
+
*/
|
|
53
|
+
export async function createConfig(
|
|
54
|
+
sdk: NaraSDK,
|
|
55
|
+
options: CreateConfigOptions
|
|
56
|
+
): Promise<CreateConfigResult> {
|
|
57
|
+
const connection = sdk.getConnection();
|
|
58
|
+
const client = sdk.getClient();
|
|
59
|
+
|
|
60
|
+
// Generate new config keypair
|
|
61
|
+
const config = Keypair.generate();
|
|
62
|
+
|
|
63
|
+
// Build bonding curve configuration
|
|
64
|
+
const curveConfig = buildCurveWithMarketCap({
|
|
65
|
+
totalTokenSupply: options.totalTokenSupply ?? 1_000_000_000, // Total token supply
|
|
66
|
+
initialMarketCap: options.initialMarketCap ?? 30, // Initial market cap
|
|
67
|
+
migrationMarketCap: options.migrationMarketCap ?? 540, // Migration market cap threshold
|
|
68
|
+
migrationOption: MigrationOption.MET_DAMM_V2, // Migration option: use Meteora DAMM V2
|
|
69
|
+
tokenBaseDecimal: TokenDecimal.SIX, // Base token decimals: 6
|
|
70
|
+
tokenQuoteDecimal: TokenDecimal.NINE, // Quote token decimals: 9 (SOL)
|
|
71
|
+
// Locked vesting parameters
|
|
72
|
+
lockedVestingParams: {
|
|
73
|
+
totalLockedVestingAmount: 0, // Total locked vesting amount
|
|
74
|
+
numberOfVestingPeriod: 0, // Number of vesting periods
|
|
75
|
+
cliffUnlockAmount: 0, // Cliff unlock amount
|
|
76
|
+
totalVestingDuration: 0, // Total vesting duration
|
|
77
|
+
cliffDurationFromMigrationTime: 0, // Cliff duration from migration time
|
|
78
|
+
},
|
|
79
|
+
// Base fee parameters
|
|
80
|
+
baseFeeParams: {
|
|
81
|
+
baseFeeMode: BaseFeeMode.FeeSchedulerLinear, // Fee mode: linear scheduler
|
|
82
|
+
feeSchedulerParam: {
|
|
83
|
+
startingFeeBps: 100, // Starting fee (basis points): 1%
|
|
84
|
+
endingFeeBps: 100, // Ending fee (basis points): 1%
|
|
85
|
+
numberOfPeriod: 0, // Number of periods
|
|
86
|
+
totalDuration: 0, // Total duration
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
dynamicFeeEnabled: true, // Enable dynamic fees
|
|
90
|
+
activationType: ActivationType.Slot, // Activation type: slot-based
|
|
91
|
+
collectFeeMode: CollectFeeMode.QuoteToken, // Fee collection mode: quote token
|
|
92
|
+
migrationFeeOption: MigrationFeeOption.FixedBps25, // Migration fee option: fixed 1%
|
|
93
|
+
tokenType: TokenType.SPL, // Token type: SPL token
|
|
94
|
+
partnerLiquidityPercentage: 0, // Partner liquidity percentage
|
|
95
|
+
creatorLiquidityPercentage: 0, // Creator liquidity percentage
|
|
96
|
+
partnerPermanentLockedLiquidityPercentage: 100, // Partner permanent locked liquidity: 100%
|
|
97
|
+
creatorPermanentLockedLiquidityPercentage: 0, // Creator permanent locked liquidity: 0%
|
|
98
|
+
creatorTradingFeePercentage: 0, // Creator trading fee percentage: 0%
|
|
99
|
+
leftover: 0, // Leftover token amount
|
|
100
|
+
tokenUpdateAuthority: TokenUpdateAuthorityOption.Immutable, // Token update authority: immutable
|
|
101
|
+
// Migration fee
|
|
102
|
+
migrationFee: {
|
|
103
|
+
feePercentage: 0, // Fee percentage
|
|
104
|
+
creatorFeePercentage: 0, // Creator fee percentage
|
|
105
|
+
},
|
|
106
|
+
poolCreationFee: 0.1, // Pool creation fee
|
|
107
|
+
enableFirstSwapWithMinFee: true, // Enable first swap with minimum fee
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Create config transaction
|
|
111
|
+
const transaction = await client.partner.createConfig({
|
|
112
|
+
config: config.publicKey, // Config public key
|
|
113
|
+
feeClaimer: options.feeClaimer, // Fee claimer
|
|
114
|
+
leftoverReceiver: options.leftoverReceiver, // Leftover receiver
|
|
115
|
+
payer: options.payer, // Payer
|
|
116
|
+
quoteMint: NATIVE_MINT, // Quote mint: native SOL
|
|
117
|
+
...curveConfig, // Curve config parameters
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Get latest blockhash
|
|
121
|
+
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
122
|
+
transaction.recentBlockhash = blockhash;
|
|
123
|
+
transaction.feePayer = options.payer;
|
|
124
|
+
// Config account partial signature
|
|
125
|
+
transaction.partialSign(config);
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
configAddress: config.publicKey.toBase58(), // Config address
|
|
129
|
+
transaction, // Unsigned transaction (already has config's partial signature)
|
|
130
|
+
configKeypair: config, // Config keypair
|
|
131
|
+
};
|
|
132
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK and CLI default constants
|
|
3
|
+
*
|
|
4
|
+
* Priority for all values: CLI flag > env variable > default value
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Default RPC URL for Nara testnet
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_RPC_URL =
|
|
11
|
+
process.env.RPC_URL || "https://mainnet-api.nara.build/";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Default bonding curve config address
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_DBC_CONFIG_ADDRESS =
|
|
17
|
+
process.env.DBC_CONFIG_ADDRESS || "";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Default wallet path
|
|
21
|
+
*/
|
|
22
|
+
export const DEFAULT_WALLET_PATH =
|
|
23
|
+
process.env.WALLET_PATH || "~/.config/nara/id.json";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default quest relay URL
|
|
27
|
+
*/
|
|
28
|
+
export const DEFAULT_QUEST_RELAY_URL =
|
|
29
|
+
process.env.QUEST_RELAY_URL || "https://quest-api.nara.build/";
|
package/src/migrate.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PublicKey,
|
|
3
|
+
Transaction,
|
|
4
|
+
Keypair,
|
|
5
|
+
VersionedTransaction,
|
|
6
|
+
} from "@solana/web3.js";
|
|
7
|
+
import BN from "bn.js";
|
|
8
|
+
import { NaraSDK } from "./client";
|
|
9
|
+
import { DAMM_V2_MIGRATION_FEE_ADDRESS } from "@meteora-ag/dynamic-bonding-curve-sdk";
|
|
10
|
+
|
|
11
|
+
export interface MigrateToDAMMV2Params {
|
|
12
|
+
/** Token address (baseMint) */
|
|
13
|
+
tokenAddress: string;
|
|
14
|
+
/** Payer */
|
|
15
|
+
payer: PublicKey;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MigrateToDAMMV2Result {
|
|
19
|
+
/** Migration transaction (returns VersionedTransaction if ALT is configured) */
|
|
20
|
+
transaction: Transaction | VersionedTransaction;
|
|
21
|
+
/** First Position NFT keypair (requires signature) */
|
|
22
|
+
firstPositionNftKeypair: Keypair;
|
|
23
|
+
/** Second Position NFT keypair (requires signature) */
|
|
24
|
+
secondPositionNftKeypair: Keypair;
|
|
25
|
+
/** Pool address */
|
|
26
|
+
poolAddress: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface CreateLockerParams {
|
|
30
|
+
/** Token address (baseMint) */
|
|
31
|
+
tokenAddress: string;
|
|
32
|
+
/** Payer */
|
|
33
|
+
payer: PublicKey;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CreateLockerResult {
|
|
37
|
+
/** Locker creation transaction (returns VersionedTransaction if ALT is configured) */
|
|
38
|
+
transaction: Transaction | VersionedTransaction;
|
|
39
|
+
/** Pool address */
|
|
40
|
+
poolAddress: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Launch token pool to DAMM V2 (graduation)
|
|
45
|
+
*
|
|
46
|
+
* Call this function to migrate the pool to DAMM V2 after the bonding curve is complete (100%)
|
|
47
|
+
*
|
|
48
|
+
* Notes:
|
|
49
|
+
* - dammConfig address is automatically derived from pool config's migrationFeeOption
|
|
50
|
+
* - If token has locked vesting parameters, may need to call createLocker() first
|
|
51
|
+
* - Requires signatures from three keypairs: payer, firstPositionNftKeypair, secondPositionNftKeypair
|
|
52
|
+
*
|
|
53
|
+
* @param sdk NaraSDK SDK instance
|
|
54
|
+
* @param params Migration parameters
|
|
55
|
+
* @returns Migration transaction, Position NFT keypairs, and pool address
|
|
56
|
+
*/
|
|
57
|
+
export async function migrateToDAMMV2(
|
|
58
|
+
sdk: NaraSDK,
|
|
59
|
+
params: MigrateToDAMMV2Params
|
|
60
|
+
): Promise<MigrateToDAMMV2Result> {
|
|
61
|
+
const connection = sdk.getConnection();
|
|
62
|
+
const client = sdk.getClient();
|
|
63
|
+
const tokenPubkey = new PublicKey(params.tokenAddress);
|
|
64
|
+
|
|
65
|
+
// Get pool account
|
|
66
|
+
const poolAccount = await client.state.getPoolByBaseMint(tokenPubkey);
|
|
67
|
+
if (!poolAccount) {
|
|
68
|
+
throw new Error(`Pool not found for token: ${params.tokenAddress}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Check if pool has already been migrated
|
|
72
|
+
if (poolAccount.account.isMigrated) {
|
|
73
|
+
throw new Error("Pool has already been migrated");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Get pool config and read migrationFeeOption
|
|
77
|
+
const virtualPool = poolAccount.account;
|
|
78
|
+
const poolConfig = await client.state.getPoolConfig(virtualPool.config);
|
|
79
|
+
|
|
80
|
+
// Get corresponding config address from DAMM_V2_MIGRATION_FEE_ADDRESS array
|
|
81
|
+
// Using 'any' type since poolConfig's migrationFeeOption is IDL-derived
|
|
82
|
+
const migrationFeeOption = (poolConfig as any).migrationFeeOption || 0;
|
|
83
|
+
const dammConfig = DAMM_V2_MIGRATION_FEE_ADDRESS[migrationFeeOption];
|
|
84
|
+
|
|
85
|
+
if (!dammConfig) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Invalid migration fee option: ${migrationFeeOption}. Cannot determine DAMM config address.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Note: If the pool has locked vesting parameters, a locker might need to be created first
|
|
92
|
+
// Use createLocker() if the migration fails due to missing locker
|
|
93
|
+
|
|
94
|
+
// Call SDK's migrateToDammV2 method
|
|
95
|
+
const result = await client.migration.migrateToDammV2({
|
|
96
|
+
payer: params.payer,
|
|
97
|
+
virtualPool: poolAccount.publicKey,
|
|
98
|
+
dammConfig,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Get latest blockhash
|
|
102
|
+
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
103
|
+
result.transaction.recentBlockhash = blockhash;
|
|
104
|
+
result.transaction.feePayer = params.payer;
|
|
105
|
+
|
|
106
|
+
// Compile transaction with ALT if configured
|
|
107
|
+
const compiledTx = await sdk.compileTransactionWithALT(
|
|
108
|
+
result.transaction,
|
|
109
|
+
params.payer
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
transaction: compiledTx,
|
|
114
|
+
firstPositionNftKeypair: result.firstPositionNftKeypair,
|
|
115
|
+
secondPositionNftKeypair: result.secondPositionNftKeypair,
|
|
116
|
+
poolAddress: poolAccount.publicKey.toBase58(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Create Locker (for token pools with locked vesting parameters)
|
|
122
|
+
*
|
|
123
|
+
* If the token pool has locked vesting parameters (amountPerPeriod > 0 or cliffUnlockAmount > 0),
|
|
124
|
+
* a locker must be created before migrating to DAMM V2
|
|
125
|
+
*
|
|
126
|
+
* @param sdk NaraSDK SDK instance
|
|
127
|
+
* @param params Locker parameters
|
|
128
|
+
* @returns Locker creation transaction and pool address
|
|
129
|
+
*/
|
|
130
|
+
export async function createLocker(
|
|
131
|
+
sdk: NaraSDK,
|
|
132
|
+
params: CreateLockerParams
|
|
133
|
+
): Promise<CreateLockerResult> {
|
|
134
|
+
const connection = sdk.getConnection();
|
|
135
|
+
const client = sdk.getClient();
|
|
136
|
+
const tokenPubkey = new PublicKey(params.tokenAddress);
|
|
137
|
+
|
|
138
|
+
// Get pool account
|
|
139
|
+
const poolAccount = await client.state.getPoolByBaseMint(tokenPubkey);
|
|
140
|
+
if (!poolAccount) {
|
|
141
|
+
throw new Error(`Pool not found for token: ${params.tokenAddress}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Create locker
|
|
145
|
+
const transaction = await client.migration.createLocker({
|
|
146
|
+
payer: params.payer,
|
|
147
|
+
virtualPool: poolAccount.publicKey,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Get latest blockhash
|
|
151
|
+
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
152
|
+
transaction.recentBlockhash = blockhash;
|
|
153
|
+
transaction.feePayer = params.payer;
|
|
154
|
+
|
|
155
|
+
// Compile transaction with ALT if configured
|
|
156
|
+
const compiledTx = await sdk.compileTransactionWithALT(
|
|
157
|
+
transaction,
|
|
158
|
+
params.payer
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
transaction: compiledTx,
|
|
163
|
+
poolAddress: poolAccount.publicKey.toBase58(),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Check if pool can be launched to DAMM V2
|
|
169
|
+
*
|
|
170
|
+
* @param sdk NaraSDK SDK instance
|
|
171
|
+
* @param tokenAddress Token address (baseMint)
|
|
172
|
+
* @returns Whether pool can be launched
|
|
173
|
+
*/
|
|
174
|
+
export async function canMigrate(
|
|
175
|
+
sdk: NaraSDK,
|
|
176
|
+
tokenAddress: string
|
|
177
|
+
): Promise<{
|
|
178
|
+
canMigrate: boolean;
|
|
179
|
+
reason?: string;
|
|
180
|
+
progress: number;
|
|
181
|
+
}> {
|
|
182
|
+
const client = sdk.getClient();
|
|
183
|
+
const tokenPubkey = new PublicKey(tokenAddress);
|
|
184
|
+
|
|
185
|
+
// Get pool account
|
|
186
|
+
const poolAccount = await client.state.getPoolByBaseMint(tokenPubkey);
|
|
187
|
+
if (!poolAccount) {
|
|
188
|
+
return {
|
|
189
|
+
canMigrate: false,
|
|
190
|
+
reason: "Pool not found",
|
|
191
|
+
progress: 0,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Check if already migrated
|
|
196
|
+
if (poolAccount.account.isMigrated) {
|
|
197
|
+
return {
|
|
198
|
+
canMigrate: false,
|
|
199
|
+
reason: "Pool has already been migrated",
|
|
200
|
+
progress: 1,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Get curve progress
|
|
205
|
+
const progress = await client.state.getPoolCurveProgress(
|
|
206
|
+
poolAccount.publicKey
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// Check if 100% complete
|
|
210
|
+
if (progress >= 1.0) {
|
|
211
|
+
return {
|
|
212
|
+
canMigrate: true,
|
|
213
|
+
progress,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
canMigrate: false,
|
|
219
|
+
reason: `Curve not complete. Current progress: ${(progress * 100).toFixed(2)}%`,
|
|
220
|
+
progress,
|
|
221
|
+
};
|
|
222
|
+
}
|