Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/dynamic-bonding-curve/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to the Dynamic Bonding Curve SDK will be documented in this file.

## [Unreleased]

### Added

- Optional `receiver` on `swap`, `swap2` and `swap2WithTransferHook`: the output goes to the receiver's associated token account while the input still comes from `owner`, who still signs. When the output is SOL and the receiver is not `owner`, the receiver keeps wrapped SOL (closing its account would need its signature). Without `receiver` the transaction is the same as before.

## [1.5.13] - 2026-09-24

### Changed
Expand Down
5 changes: 5 additions & 0 deletions packages/dynamic-bonding-curve/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2339,6 +2339,7 @@ interface SwapParams {
poolAddress: PublicKey // The pool address
referralTokenAccount: PublicKey | null // The referral token account (optional)
payer?: PublicKey // The payer of the transaction (optional)
receiver?: PublicKey // The owner of the output token account (optional, defaults to owner)
}
```

Expand Down Expand Up @@ -2393,6 +2394,7 @@ const transaction = await client.pool.swap({
- If the transaction fails with "insufficient balance", check that you have enough tokens plus fees for the transaction.
- The pool address can be derived using `deriveDbcPoolAddress`.
- The `payer` parameter is optional. If not provided, the owner will be used as the payer to fund ATA creation.
- The `receiver` parameter is optional. If provided, the output goes to the receiver's associated token account, which `payer` (or `owner`) creates if needed. The input still comes from `owner`'s account and `owner` still signs; the receiver does not sign. If the output is SOL and the receiver is not `owner`, the receiver gets wrapped SOL in its own wrapped SOL account, which it unwraps itself.

---

Expand Down Expand Up @@ -2480,6 +2482,7 @@ interface Swap2Params {
swapBaseForQuote: boolean // True for base->quote, false for quote->base
referralTokenAccount: PublicKey | null // The referral token account (optional)
payer?: PublicKey // The payer of the transaction (optional)
receiver?: PublicKey // The owner of the output token account (optional, defaults to owner)
} & (
| {
swapMode: SwapMode.ExactIn // Swap exact input amount
Expand Down Expand Up @@ -2551,6 +2554,7 @@ const transaction = await client.pool.swap2({
- The `maximumAmountIn` parameter protects against slippage. Set it to a value slightly higher than the expected input.
- The `referralTokenAccount` parameter is an optional token account. If provided, the referral fee will be applied to the transaction.
- The `payer` parameter is optional. If not provided, the owner will be used as the payer to fund ATA creation.
- The `receiver` parameter is optional. If provided, the output goes to the receiver's associated token account, which `payer` (or `owner`) creates if needed. The input still comes from `owner`'s account and `owner` still signs; the receiver does not sign. If the output is SOL and the receiver is not `owner`, the receiver gets wrapped SOL in its own wrapped SOL account, which it unwraps itself.

---

Expand All @@ -2573,6 +2577,7 @@ interface Swap2Params {
swapBaseForQuote: boolean // True for base->quote, false for quote->base
referralTokenAccount: PublicKey | null // The referral token account (optional)
payer?: PublicKey // The payer of the transaction (optional)
receiver?: PublicKey // The owner of the output token account (optional, defaults to owner)
} & (
| {
swapMode: SwapMode.ExactIn // Swap exact input amount
Expand Down
36 changes: 24 additions & 12 deletions packages/dynamic-bonding-curve/src/services/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class PoolService extends DynamicBondingCurveProgram {
swapBaseForQuote,
owner,
payer,
receiver,
pool,
referralTokenAccount,
} = params
Expand Down Expand Up @@ -108,7 +109,8 @@ export class PoolService extends DynamicBondingCurveProgram {
inputMint,
outputMint,
inputTokenProgram,
outputTokenProgram
outputTokenProgram,
receiver
)

// add SOL wrapping instructions if needed
Expand All @@ -125,10 +127,12 @@ export class PoolService extends DynamicBondingCurveProgram {
// add postInstructions for SOL unwrapping
const postInstructions: TransactionInstruction[] = []
if (
[inputMint.toBase58(), outputMint.toBase58()].includes(
NATIVE_MINT.toBase58()
)
inputMint.equals(NATIVE_MINT) ||
(outputMint.equals(NATIVE_MINT) &&
(!receiver || receiver.equals(owner)))
) {
// A receiver other than the owner keeps its wrapped SOL: closing
// that account needs the receiver's signature.
const unwrapIx = unwrapSOLInstruction(owner, owner)
unwrapIx && postInstructions.push(unwrapIx)
}
Expand Down Expand Up @@ -187,6 +191,7 @@ export class PoolService extends DynamicBondingCurveProgram {
swapMode,
owner,
payer,
receiver,
referralTokenAccount,
} = params

Expand Down Expand Up @@ -252,7 +257,8 @@ export class PoolService extends DynamicBondingCurveProgram {
inputMint,
outputMint,
inputTokenProgram,
outputTokenProgram
outputTokenProgram,
receiver
)

// add SOL wrapping instructions if needed
Expand All @@ -274,10 +280,12 @@ export class PoolService extends DynamicBondingCurveProgram {
// add postInstructions for SOL unwrapping
const postInstructions: TransactionInstruction[] = []
if (
[inputMint.toBase58(), outputMint.toBase58()].includes(
NATIVE_MINT.toBase58()
)
inputMint.equals(NATIVE_MINT) ||
(outputMint.equals(NATIVE_MINT) &&
(!receiver || receiver.equals(owner)))
) {
// A receiver other than the owner keeps its wrapped SOL: closing
// that account needs the receiver's signature.
const unwrapIx = unwrapSOLInstruction(owner, owner)
unwrapIx && postInstructions.push(unwrapIx)
}
Expand Down Expand Up @@ -334,6 +342,7 @@ export class PoolService extends DynamicBondingCurveProgram {
swapMode,
owner,
payer,
receiver,
referralTokenAccount,
} = params

Expand Down Expand Up @@ -392,7 +401,8 @@ export class PoolService extends DynamicBondingCurveProgram {
inputMint,
outputMint,
inputTokenProgram,
outputTokenProgram
outputTokenProgram,
receiver
)

if (inputMint.equals(NATIVE_MINT)) {
Expand All @@ -412,10 +422,12 @@ export class PoolService extends DynamicBondingCurveProgram {

const postInstructions: TransactionInstruction[] = []
if (
[inputMint.toBase58(), outputMint.toBase58()].includes(
NATIVE_MINT.toBase58()
)
inputMint.equals(NATIVE_MINT) ||
(outputMint.equals(NATIVE_MINT) &&
(!receiver || receiver.equals(owner)))
) {
// A receiver other than the owner keeps its wrapped SOL: closing
// that account needs the receiver's signature.
const unwrapIx = unwrapSOLInstruction(owner, owner)
unwrapIx && postInstructions.push(unwrapIx)
}
Expand Down
10 changes: 8 additions & 2 deletions packages/dynamic-bonding-curve/src/services/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -986,13 +986,19 @@ export class DynamicBondingCurveProgram {
.transaction()
}

/**
* Get or create `owner`'s token accounts for both mints. When `tokenBOwner`
* is given, the token B account belongs to it instead (for example a swap's
* output going to another wallet).
*/
protected async prepareTokenAccounts(
owner: PublicKey,
payer: PublicKey,
tokenAMint: PublicKey,
tokenBMint: PublicKey,
tokenAProgram: PublicKey,
tokenBProgram: PublicKey
tokenBProgram: PublicKey,
tokenBOwner?: PublicKey
): Promise<{
ataTokenA: PublicKey
ataTokenB: PublicKey
Expand All @@ -1015,7 +1021,7 @@ export class DynamicBondingCurveProgram {
getOrCreateATAInstruction(
this.connection,
tokenBMint,
owner,
tokenBOwner ?? owner,
payer,
true,
tokenBProgram,
Expand Down
14 changes: 14 additions & 0 deletions packages/dynamic-bonding-curve/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,13 @@ export type SwapParams = {
swapBaseForQuote: boolean
referralTokenAccount: PublicKey | null
payer?: PublicKey
/**
* Owner of the output token account. Defaults to `owner`. The input still
* comes from `owner`'s token account and `owner` still signs; the receiver
* does not sign. When the output is SOL and the receiver is not `owner`,
* the receiver gets wrapped SOL in its own wrapped SOL account.
*/
receiver?: PublicKey
}

export type Swap2Params = {
Expand All @@ -537,6 +544,13 @@ export type Swap2Params = {
swapBaseForQuote: boolean
referralTokenAccount: PublicKey | null
payer?: PublicKey
/**
* Owner of the output token account. Defaults to `owner`. The input still
* comes from `owner`'s token account and `owner` still signs; the receiver
* does not sign. When the output is SOL and the receiver is not `owner`,
* the receiver gets wrapped SOL in its own wrapped SOL account.
*/
receiver?: PublicKey
} & (
| {
swapMode: SwapMode.ExactIn
Expand Down
Loading