런치 유형

본딩 커브 스왑 통합

Genesis SDK를 사용하여 본딩 커브 상태를 읽고, 스왑 견적을 계산하고, 온체인에서 매수 및 매도 트랜잭션을 실행하고, 슬리피지를 처리하고, 창작자 수수료를 청구합니다.

빌드할 내용

이 가이드는 다음을 다룹니다:

  • BondingCurveBucketV2 계정 상태 가져오기 및 해석
  • isSwappable, isSoldOut, isGraduated를 사용한 라이프사이클 상태 확인
  • getSwapResult를 사용한 정확한 스왑 견적 계산
  • applySlippage로 사용자 보호
  • swapBondingCurveV2를 사용한 매수 및 매도 트랜잭션 구성
  • 커브 및 졸업 후 Raydium 풀에서 창작자 수수료 청구

요약

본딩 커브 스왑은 Genesis SDK를 사용하여 BondingCurveBucketV2 온체인 계정과 상호작용합니다 — SOL을 받아 토큰을 반환하거나(매수), 토큰을 받아 SOL을 반환하는(매도) 상수 곱 AMM입니다. 기본 가격 산정 수학에 대해서는 운영 이론을 참조하세요.

  • 전송 전 견적 계산getSwapResult를 호출하여 수수료가 조정된 정확한 입출력 금액을 확인하세요
  • 슬리피지 보호applySlippageminAmountOutScaled를 계산하여 명령어에 전달하세요
  • wSOL은 수동 처리 — 스왑 명령어는 네이티브 SOL을 자동으로 래핑하거나 언래핑하지 않으므로, 호출자가 wSOL ATA를 직접 처리해야 합니다
  • 프로그램 ID — Solana 메인넷의 GNS1S5J5AspKXgpjz6SvKL66kPaKWAhaGRhCqPRxii2B

빠른 시작

바로 가기: 설치 · 설정 · 커브 가져오기 · 라이프사이클 헬퍼 · 견적 · 슬리피지 · 스왑 실행 · 창작자 수수료 · 오류 · API 참조

  1. 패키지를 설치하고 genesis() 플러그인으로 Umi 인스턴스를 구성하세요
  2. BondingCurveBucketV2Pda를 파생하고 계정을 가져오세요
  3. isSwappable(bucket)을 확인하세요 — false이면 중단하세요
  4. getSwapResult(bucket, amountIn, SwapDirection.Buy)를 호출하여 수수료 조정 견적을 구하세요
  5. applySlippage(quote.amountOut, slippageBps)를 적용하여 minAmountOutScaled를 계산하세요
  6. wSOL 래핑을 수동으로 처리한 후 swapBondingCurveV2를 전송하고 확인하세요

사전 요구 사항

  • Node.js 18+ — 네이티브 BigInt 지원에 필요합니다
  • SOL로 자금이 충전된 Solana 지갑 (트랜잭션 수수료 및 스왑 입력용)
  • Solana RPC 엔드포인트 (메인넷-베타 또는 데브넷)
  • Umi 프레임워크 및 async/await 패턴에 대한 이해

테스트된 구성

도구버전
@metaplex-foundation/genesis1.x
@metaplex-foundation/umi1.x
@metaplex-foundation/umi-bundle-defaults1.x
Node.js18+

설치

Terminal
npm install @metaplex-foundation/genesis \
@metaplex-foundation/umi \
@metaplex-foundation/umi-bundle-defaults

Umi 및 Genesis 플러그인 설정

SDK 함수를 호출하기 전에 Umi 인스턴스를 구성하고 genesis() 플러그인을 등록하세요.

setup.ts
1import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
2import { genesis } from '@metaplex-foundation/genesis';
3import { keypairIdentity } from '@metaplex-foundation/umi';
4import { readFileSync } from 'fs';
5
6const keypairFile = JSON.parse(readFileSync('/path/to/keypair.json', 'utf-8'));
7
8const umi = createUmi('https://api.mainnet-beta.solana.com')
9 .use(genesis());
10
11const keypair = umi.eddsa.createKeypairFromSecretKey(Uint8Array.from(keypairFile));
12umi.use(keypairIdentity(keypair));

본딩 커브 BucketV2 가져오기

이미 알고 있는 정보에 따라 세 가지 검색 전략을 사용할 수 있습니다.

알려진 Genesis 계정에서 가져오기

1import {
2 findBondingCurveBucketV2Pda,
3 fetchBondingCurveBucketV2,
4 genesis,
5} from '@metaplex-foundation/genesis';
6import { publicKey } from '@metaplex-foundation/umi';
7import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
8
9const umi = createUmi('https://api.mainnet-beta.solana.com').use(genesis());
10
11const genesisAccount = publicKey('YOUR_GENESIS_ACCOUNT_PUBKEY');
12
13const [bucketPda] = findBondingCurveBucketV2Pda(umi, {
14 genesisAccount,
15 bucketIndex: 0,
16});
17
18const bucket = await fetchBondingCurveBucketV2(umi, bucketPda);

토큰 민트에서 가져오기

fetch-from-mint.ts
1import {
2 findGenesisAccountV2Pda,
3 findBondingCurveBucketV2Pda,
4 fetchBondingCurveBucketV2,
5} from '@metaplex-foundation/genesis';
6import { publicKey } from '@metaplex-foundation/umi';
7
8const baseMint = publicKey('TOKEN_MINT_PUBKEY');
9
10const [genesisAccount] = findGenesisAccountV2Pda(umi, {
11 baseMint,
12 genesisIndex: 0,
13});
14
15const [bucketPda] = findBondingCurveBucketV2Pda(umi, {
16 genesisAccount,
17 bucketIndex: 0,
18});
19
20const bucket = await fetchBondingCurveBucketV2(umi, bucketPda);

본딩 커브 BucketV2 상태 읽기

필드유형설명
baseTokenBalancebigint커브에 남아 있는 토큰. 0이면 소진됨.
baseTokenAllocationbigint생성 시 이 커브에 할당된 총 토큰.
quoteTokenDepositTotalbigint구매자가 예치한 실제 SOL (lamports). 0에서 시작.
virtualSolbigint초기화 시 추가된 가상 SOL 리저브 (가격 책정 전용).
virtualTokensbigint초기화 시 추가된 가상 토큰 리저브 (가격 책정 전용).
depositFeenumber모든 스왑의 SOL 측에 적용되는 프로토콜 수수료율.
withdrawFeenumber매도의 SOL 출력 측에 적용되는 프로토콜 수수료율.
creatorFeeAccruedbigint마지막 청구 이후 누적된 창작자 수수료 (lamports).
creatorFeeClaimedbigint현재까지 청구된 누적 창작자 수수료 (lamports).
swapStartConditionobject거래가 허용되기 전에 충족되어야 하는 조건.
swapEndConditionobject발동 시 거래를 종료하는 조건.

virtualSolvirtualTokens는 가격 산정 수학에만 존재합니다 — 실제 자산으로 온체인에 예치되지 않습니다. 가상 리저브가 상수 곱 커브를 형성하는 방법은 운영 이론을 참조하세요.

본딩 커브 라이프사이클 헬퍼

다섯 가지 헬퍼 함수는 추가 RPC 호출 없이 커브 상태를 검사합니다 (isGraduated 제외).

lifecycle-helpers.ts
1import {
2 isSwappable,
3 isFirstBuyPending,
4 isSoldOut,
5 getFillPercentage,
6 isGraduated,
7} from '@metaplex-foundation/genesis';
8
9const canSwap = isSwappable(bucket);
10const firstBuyPending = isFirstBuyPending(bucket);
11const soldOut = isSoldOut(bucket);
12const fillPercent = getFillPercentage(bucket);
13const graduated = await isGraduated(umi, bucket); // 비동기 RPC 호출
헬퍼비동기반환값설명
isSwappable(bucket)아니오boolean공개 거래를 수락 중일 때 true
isFirstBuyPending(bucket)아니오boolean지정된 첫 번째 구매가 아직 완료되지 않았을 때 true
isSoldOut(bucket)아니오booleanbaseTokenBalance === 0n일 때 true
getFillPercentage(bucket)아니오number판매된 할당량의 0–100 백분율
isGraduated(umi, bucket)booleanRaydium CPMM 풀이 온체인에 존재할 때 true

스왑 견적 계산

getSwapResult(bucket, amountIn, swapDirection, isFirstBuy?)는 트랜잭션 전송 없이 스왑에 대한 정확한 수수료 조정 금액을 계산합니다.

반환값 { amountIn, fee, creatorFee, amountOut }:

  • amountIn — 조정 후 실제 입력 금액
  • fee — 부과된 프로토콜 수수료, lamports 단위
  • creatorFee — 부과된 창작자 수수료, lamports 단위 (창작자 수수료가 구성되지 않은 경우 0)
  • amountOut — 받는 토큰(매수) 또는 받는 SOL(매도)

매수 견적 (SOL → 토큰)

1import {
2 genesis,
3 findBondingCurveBucketV2Pda,
4 fetchBondingCurveBucketV2,
5 getSwapResult,
6} from '@metaplex-foundation/genesis';
7import { publicKey } from '@metaplex-foundation/umi';
8import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
9
10const umi = createUmi('https://api.mainnet-beta.solana.com').use(genesis());
11
12const genesisAccount = publicKey('YOUR_GENESIS_ACCOUNT_PUBKEY');
13const [bucketPda] = findBondingCurveBucketV2Pda(umi, { genesisAccount, bucketIndex: 0 });
14const bucket = await fetchBondingCurveBucketV2(umi, bucketPda);
15
16const SOL_IN = 1_000_000_000n; // 1 SOL in lamports
17
18const buyQuote = getSwapResult(bucket, SOL_IN, 'buy');
19
20console.log('SOL input: ', buyQuote.amountIn.toString(), 'lamports');
21console.log('Total fee: ', buyQuote.fee.toString(), 'lamports');
22console.log('Tokens out: ', buyQuote.amountOut.toString());
23
24// SOL input: 1000000000 lamports
25// Total fee: 10000000 lamports
26// Tokens out: <calculated token amount>

매도 견적 (토큰 → SOL)

1import {
2 genesis,
3 findBondingCurveBucketV2Pda,
4 fetchBondingCurveBucketV2,
5 getSwapResult,
6} from '@metaplex-foundation/genesis';
7import { publicKey } from '@metaplex-foundation/umi';
8import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
9
10const umi = createUmi('https://api.mainnet-beta.solana.com').use(genesis());
11
12const genesisAccount = publicKey('YOUR_GENESIS_ACCOUNT_PUBKEY');
13const [bucketPda] = findBondingCurveBucketV2Pda(umi, { genesisAccount, bucketIndex: 0 });
14const bucket = await fetchBondingCurveBucketV2(umi, bucketPda);
15
16const TOKENS_IN = 500_000_000_000n; // 500 tokens (9 decimals)
17
18const sellQuote = getSwapResult(bucket, TOKENS_IN, 'sell');
19
20console.log('Tokens input: ', sellQuote.amountIn.toString());
21console.log('Total fee: ', sellQuote.fee.toString(), 'lamports');
22console.log('SOL out: ', sellQuote.amountOut.toString(), 'lamports');
23
24// Tokens input: 500000000000
25// Total fee: <fee in lamports>
26// SOL out: <calculated SOL amount>

첫 번째 구매 수수료 면제

수수료가 면제된 첫 번째 구매를 견적하려면 네 번째 인수로 true를 전달하세요:

first-buy-quote.ts
1const firstBuyQuote = getSwapResult(bucket, SOL_IN, SwapDirection.Buy, true);
2console.log('Fee (waived): ', firstBuyQuote.fee.toString()); // 0n

현재 가격 헬퍼

current-price.ts
1import {
2 getCurrentPrice,
3 getCurrentPriceQuotePerBase,
4 getCurrentPriceComponents,
5} from '@metaplex-foundation/genesis';
6
7const tokensPerSol = getCurrentPrice(bucket); // bigint
8const lamportsPerToken = getCurrentPriceQuotePerBase(bucket); // bigint
9const { baseReserves, quoteReserves } = getCurrentPriceComponents(bucket);

슬리피지 보호

applySlippage(expectedAmountOut, slippageBps)는 슬리피지 허용 오차만큼 예상 출력을 줄입니다. 결과를 minAmountOutScaled로 스왑 명령어에 전달하세요 — 실제 출력이 이 값 미만으로 떨어지면 온체인 프로그램이 트랜잭션을 거부합니다.

slippage.ts
1import { getSwapResult, applySlippage, SwapDirection } from '@metaplex-foundation/genesis';
2
3const quote = getSwapResult(bucket, 1_000_000_000n, SwapDirection.Buy);
4const minAmountOutScaled = applySlippage(quote.amountOut, 100); // 1% 슬리피지

applySlippage에서 계산된 minAmountOutScaled 없이 스왑을 전송하지 마세요. 본딩 커브 가격은 모든 거래마다 변동합니다. 슬리피지 보호 없이는 사용자가 견적보다 훨씬 적은 토큰을 받을 수 있습니다.

일반적인 값: 안정적인 조건에서 50 bps (0.5%), 변동성이 높은 런칭에서 200 bps (2%).

스왑 트랜잭션 구성

swapBondingCurveV2(umi, accounts)는 스왑 명령어를 빌드합니다. 호출자는 트랜잭션 전후로 래핑된 SOL(wSOL)을 처리할 책임이 있습니다.

매수 트랜잭션 (SOL → 토큰)

1import {
2 genesis,
3 findBondingCurveBucketV2Pda,
4 fetchBondingCurveBucketV2,
5 getSwapResult,
6 applySlippage,
7 swapBondingCurveV2,
8 isSwappable,
9} from '@metaplex-foundation/genesis';
10import { findAssociatedTokenPda } from '@metaplex-foundation/mpl-toolbox';
11import { publicKey } from '@metaplex-foundation/umi';
12import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
13
14const umi = createUmi('https://api.mainnet-beta.solana.com').use(genesis());
15
16const genesisAccount = publicKey('YOUR_GENESIS_ACCOUNT_PUBKEY');
17const baseMint = publicKey('TOKEN_MINT_PUBKEY');
18const quoteMint = publicKey('So11111111111111111111111111111111111111112'); // wSOL
19
20const [bucketPda] = findBondingCurveBucketV2Pda(umi, { genesisAccount, bucketIndex: 0 });
21const bucket = await fetchBondingCurveBucketV2(umi, bucketPda);
22
23if (!isSwappable(bucket)) throw new Error('Curve is not currently accepting swaps');
24
25const SOL_IN = 1_000_000_000n; // 1 SOL in lamports
26const quote = getSwapResult(bucket, SOL_IN, 'buy');
27const minAmountOut = applySlippage(quote.amountOut, 100); // 1% slippage
28
29const [userBaseTokenAccount] = findAssociatedTokenPda(umi, { mint: baseMint, owner: umi.identity.publicKey });
30const [userQuoteTokenAccount] = findAssociatedTokenPda(umi, { mint: quoteMint, owner: umi.identity.publicKey });
31
32// NOTE: Fund the wSOL ATA before this call — see the wSOL Wrapping Note on this page.
33const result = await swapBondingCurveV2(umi, {
34 genesisAccount,
35 bucketPda,
36 baseMint,
37 quoteMint,
38 userBaseTokenAccount,
39 userQuoteTokenAccount,
40 amountIn: quote.amountIn,
41 minAmountOut,
42 direction: 'buy',
43}).sendAndConfirm(umi);
44
45console.log('Buy confirmed:', result.signature);
46
47// Buy confirmed: <base58 transaction signature>

매도 트랜잭션 (토큰 → SOL)

1import {
2 genesis,
3 findBondingCurveBucketV2Pda,
4 fetchBondingCurveBucketV2,
5 getSwapResult,
6 applySlippage,
7 swapBondingCurveV2,
8} from '@metaplex-foundation/genesis';
9import { findAssociatedTokenPda } from '@metaplex-foundation/mpl-toolbox';
10import { publicKey } from '@metaplex-foundation/umi';
11import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
12
13const umi = createUmi('https://api.mainnet-beta.solana.com').use(genesis());
14
15const genesisAccount = publicKey('YOUR_GENESIS_ACCOUNT_PUBKEY');
16const baseMint = publicKey('TOKEN_MINT_PUBKEY');
17const quoteMint = publicKey('So11111111111111111111111111111111111111112'); // wSOL
18
19const [bucketPda] = findBondingCurveBucketV2Pda(umi, { genesisAccount, bucketIndex: 0 });
20const bucket = await fetchBondingCurveBucketV2(umi, bucketPda);
21
22const [userBaseTokenAccount] = findAssociatedTokenPda(umi, { mint: baseMint, owner: umi.identity.publicKey });
23const [userQuoteTokenAccount] = findAssociatedTokenPda(umi, { mint: quoteMint, owner: umi.identity.publicKey });
24
25const TOKENS_IN = 500_000_000_000n; // 500 tokens (9 decimals)
26const quote = getSwapResult(bucket, TOKENS_IN, 'sell');
27const minAmountOut = applySlippage(quote.amountOut, 100); // 1% slippage
28
29const result = await swapBondingCurveV2(umi, {
30 genesisAccount,
31 bucketPda,
32 baseMint,
33 quoteMint,
34 userBaseTokenAccount,
35 userQuoteTokenAccount,
36 amountIn: quote.amountIn,
37 minAmountOut,
38 direction: 'sell',
39}).sendAndConfirm(umi);
40
41// NOTE: Close the wSOL ATA after confirming to unwrap back to native SOL.
42console.log('Sell confirmed:', result.signature);
43
44// Sell confirmed: <base58 transaction signature>

wSOL 래핑 참고 사항

수동 wSOL 처리 필요

swapBondingCurveV2는 래핑된 SOL(wSOL)을 견적 토큰으로 사용하며 네이티브 SOL을 자동으로 래핑하거나 언래핑하지 않습니다.

매수의 경우: wSOL ATA를 생성하고, 필요한 lamports를 전송한 후 스왑 전에 syncNative를 호출하세요.

매도의 경우: 스왑이 확인된 후 wSOL ATA를 닫아 네이티브 SOL로 다시 언래핑하세요.

현재 버전에서는 wSOL만 견적 토큰으로 허용됩니다.

wsol-wrap-unwrap.ts
1import {
2 findAssociatedTokenPda,
3 createAssociatedTokenAccountIdempotentInstruction,
4 syncNative,
5 closeToken,
6} from '@metaplex-foundation/mpl-toolbox';
7import { transactionBuilder, sol, publicKey } from '@metaplex-foundation/umi';
8
9const wSOL = publicKey('So11111111111111111111111111111111111111112');
10const [wSolAta] = findAssociatedTokenPda(umi, { mint: wSOL, owner: umi.identity.publicKey });
11
12// --- 매수 전 SOL 래핑 ---
13const wrapBuilder = transactionBuilder()
14 .add(createAssociatedTokenAccountIdempotentInstruction(umi, {
15 mint: wSOL,
16 owner: umi.identity.publicKey,
17 }))
18 .add(syncNative(umi, { account: wSolAta }));
19
20await wrapBuilder.sendAndConfirm(umi);
21
22// --- 매도 후 SOL 언래핑 ---
23const unwrapBuilder = closeToken(umi, {
24 account: wSolAta,
25 destination: umi.identity.publicKey,
26 authority: umi.identity,
27});
28
29await unwrapBuilder.sendAndConfirm(umi);

창작자 수수료 청구

창작자 수수료는 스왑에서 직접 전송되지 않고 버킷(creatorFeeAccrued)에 누적됩니다. 권한 없는 claimBondingCurveCreatorFeeV2 명령어로 커브가 활성 중에 수집하고, 졸업 후에는 claimRaydiumCreatorFeeV2로 수집합니다.

누적 잔액 확인 방법과 졸업 후 Raydium LP 수수료 처리를 포함한 전체 청구 흐름은 창작자 수수료를 참조하세요.

오류 처리

오류원인해결 방법
BondingCurveInsufficientFunds커브에 토큰(매수) 또는 SOL(매도)이 부족함버킷을 다시 가져와 재견적; 커브가 거의 소진 상태일 수 있습니다
InsufficientOutputAmount실제 출력이 minAmountOutScaled 미만으로 떨어짐slippageBps를 높이거나 즉시 재시도하세요
InvalidSwapDirectionswapDirection 값이 유효하지 않음@metaplex-foundation/genesis 임포트에서 SwapDirection.Buy 또는 SwapDirection.Sell을 전달하세요
BondingCurveNotStartedswapStartCondition이 아직 충족되지 않음bucket.swapStartCondition을 확인하고 대기하세요
BondingCurveEnded커브가 소진되거나 졸업됨사용자를 Raydium CPMM 풀로 안내하세요
error-handling.ts
1async function executeBuy(bucket, amountIn: bigint, slippageBps: number) {
2 if (!isSwappable(bucket)) {
3 if (isSoldOut(bucket)) throw new Error('Token sold out. Trade on Raydium.');
4 throw new Error('Curve not yet active. Check the start time.');
5 }
6
7 const quote = getSwapResult(bucket, amountIn, SwapDirection.Buy);
8 const minAmountOutScaled = applySlippage(quote.amountOut, slippageBps);
9
10 try {
11 return await swapBondingCurveV2(umi, {
12 amount: quote.amountIn,
13 minAmountOutScaled,
14 swapDirection: SwapDirection.Buy,
15 // ... accounts
16 }).sendAndConfirm(umi);
17 } catch (err: any) {
18 if (err.message?.includes('InsufficientOutputAmount'))
19 throw new Error('Price moved. Try again with higher slippage.');
20 if (err.message?.includes('BondingCurveInsufficientFunds'))
21 throw new Error('Not enough tokens remaining. Reduce amount.');
22 throw err;
23 }
24}

참고 사항

  • 프로덕션에서는 매 스왑 전에 버킷을 다시 가져오세요 — 가격은 모든 사용자의 거래마다 변동합니다
  • virtualSolvirtualTokens는 커브 생성 시 설정되며 불변입니다 — 이들을 캐시하세요; 실제 리저브 필드만 스왑당 변경됩니다
  • isGraduated는 매번 호출 시 RPC 호출을 수행합니다 — 인덱서에서 결과를 캐시하세요
  • isSoldOuttrue를 반환하고 isGraduatedtrue를 반환하기 전 사이에, 커브는 소진되었지만 Raydium에 아직 자금이 조달되지 않았습니다; isGraduated가 확인될 때까지 사용자를 Raydium으로 안내하지 마세요
  • 이벤트 디코딩 및 라이프사이클 인덱싱에 대해서는 인덱싱 및 이벤트를 참조하세요
  • 모든 수수료 금액은 lamports(SOL 측) 단위입니다; 현재 수수료율은 프로토콜 수수료를 참조하세요

API 참조

견적 및 가격 함수

함수비동기반환값설명
getSwapResult(bucket, amountIn, swapDirection, isFirstBuy?)아니오{ amountIn, fee, creatorFee, amountOut }수수료 조정 스왑 견적
getCurrentPrice(bucket)아니오bigintSOL 단위당 기본 토큰 수 (정수 나눗셈)
getCurrentPriceQuotePerBase(bucket)아니오bigint기본 토큰 단위당 lamports (정수 나눗셈)
getCurrentPriceComponents(bucket)아니오{ baseReserves, quoteReserves }bigint로 결합된 가상 + 실제 리저브

라이프사이클 함수

함수비동기반환값설명
isSwappable(bucket)아니오boolean공개 거래를 수락 중일 때 true
isFirstBuyPending(bucket)아니오boolean지정된 첫 번째 구매가 아직 완료되지 않았을 때 true
isSoldOut(bucket)아니오booleanbaseTokenBalance === 0n일 때 true
getFillPercentage(bucket)아니오number판매된 할당량의 0–100 백분율
isGraduated(umi, bucket)booleanRaydium CPMM 풀이 온체인에 존재할 때 true

슬리피지

함수반환값설명
applySlippage(amountOut, slippageBps)bigintamountOutslippageBps / 10_000만큼 감소

스왑 명령어 계정

계정쓰기 가능서명자설명
genesisAccount아니오Genesis 조정 PDA
bucket아니오BondingCurveBucketV2 PDA
baseMint아니오아니오SPL 토큰 민트
quoteMint아니오아니오wSOL 민트
baseTokenAccount아니오사용자의 기본 토큰 ATA
quoteTokenAccount아니오사용자의 wSOL ATA
payer트랜잭션 수수료 납부자

계정 검색

함수반환값설명
findBondingCurveBucketV2Pda(umi, { genesisAccount, bucketIndex })[PublicKey, bump]버킷 PDA 파생
findGenesisAccountV2Pda(umi, { baseMint, genesisIndex })[PublicKey, bump]genesis 계정 PDA 파생
fetchBondingCurveBucketV2(umi, pda)BondingCurveBucketV2계정 가져오기 및 역직렬화

FAQ

isSwappable과 isSoldOut의 차이는 무엇인가요?

isSwappable은 커브가 공개 거래를 활발히 수락하고 있을 때만 true를 반환합니다. isSoldOutbaseTokenBalance가 0에 도달하는 순간 true를 반환하며, 거래가 종료되고 졸업이 시작됩니다. 커브는 소진되었지만 아직 졸업하지 않은 상태일 수 있습니다.

swapBondingCurveV2를 호출하기 전에 SOL을 래핑해야 하나요?

네. 본딩 커브는 wSOL을 견적 토큰으로 사용하며 swapBondingCurveV2는 네이티브 SOL을 자동으로 래핑하거나 언래핑하지 않습니다. wSOL 래핑 참고 사항을 참조하세요.

getSwapResult는 무엇을 반환하며 수수료를 어떻게 처리하나요?

getSwapResult{ amountIn, fee, creatorFee, amountOut }을 반환합니다. 매수의 경우, AMM 공식이 실행되기 전에 SOL 입력에서 수수료가 공제됩니다. 매도의 경우, AMM 공식이 실행된 후 SOL 출력에서 수수료가 공제됩니다. 첫 번째 구매 수수료 면제를 시뮬레이션하려면 네 번째 인수로 true를 전달하세요.

슬리피지로부터 어떻게 보호하나요?

applySlippage(quote.amountOut, slippageBps)를 호출하여 minAmountOutScaled를 계산한 후 swapBondingCurveV2에 전달하세요. 온체인 프로그램은 실제 출력이 이 값 미만으로 떨어지면 트랜잭션을 거부합니다.