@fogo/sessions-sdk 0.0.20 → 0.0.21

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/cjs/index.d.ts CHANGED
@@ -1310,6 +1310,20 @@ export type Session = {
1310
1310
  sendTransaction: (instructions: Parameters<SessionContext["sendTransaction"]>[1]) => Promise<TransactionResult>;
1311
1311
  sessionInfo: NonNullable<z.infer<typeof sessionInfoSchema>>;
1312
1312
  };
1313
+ export declare const getTransferFee: (context: SessionContext) => Promise<{
1314
+ fee: bigint;
1315
+ metadata: import("@metaplex-foundation/umi").PublicKey<string>;
1316
+ mint: PublicKey;
1317
+ symbolOrMint: string;
1318
+ decimals: number;
1319
+ }>;
1320
+ export declare const getBridgeOutFee: (context: SessionContext) => Promise<{
1321
+ fee: bigint;
1322
+ metadata: import("@metaplex-foundation/umi").PublicKey<string>;
1323
+ mint: PublicKey;
1324
+ symbolOrMint: string;
1325
+ decimals: number;
1326
+ }>;
1313
1327
  type SendTransferOptions = {
1314
1328
  context: SessionContext;
1315
1329
  walletPublicKey: PublicKey;
@@ -1317,6 +1331,7 @@ type SendTransferOptions = {
1317
1331
  mint: PublicKey;
1318
1332
  amount: bigint;
1319
1333
  recipient: PublicKey;
1334
+ feeConfig: Awaited<ReturnType<typeof getTransferFee>>;
1320
1335
  };
1321
1336
  export declare const sendTransfer: (options: SendTransferOptions) => Promise<TransactionResult>;
1322
1337
  type SendBridgeOutOptions = {
@@ -1332,6 +1347,7 @@ type SendBridgeOutOptions = {
1332
1347
  toToken: WormholeToken & {
1333
1348
  chain: "Solana";
1334
1349
  };
1350
+ feeConfig: Awaited<ReturnType<typeof getBridgeOutFee>>;
1335
1351
  };
1336
1352
  type WormholeToken = {
1337
1353
  chain: Chain;
package/cjs/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.verifyLogInToken = exports.createLogInToken = exports.bridgeIn = exports.bridgeOut = exports.sendTransfer = exports.SessionResultType = exports.getDomainRecordAddress = exports.AuthorizedTokens = exports.AuthorizedProgramsType = exports.getSessionAccount = exports.reestablishSession = exports.revokeSession = exports.replaceSession = exports.establishSession = exports.createSessionConnection = exports.TransactionResultType = exports.Network = exports.createSessionContext = void 0;
6
+ exports.verifyLogInToken = exports.createLogInToken = exports.bridgeIn = exports.bridgeOut = exports.sendTransfer = exports.getBridgeOutFee = exports.getTransferFee = exports.SessionResultType = exports.getDomainRecordAddress = exports.AuthorizedTokens = exports.AuthorizedProgramsType = exports.getSessionAccount = exports.reestablishSession = exports.revokeSession = exports.replaceSession = exports.establishSession = exports.createSessionConnection = exports.TransactionResultType = exports.Network = exports.createSessionContext = void 0;
7
7
  const anchor_1 = require("@coral-xyz/anchor");
8
8
  const sessions_idls_1 = require("@fogo/sessions-idls");
9
9
  const mpl_token_metadata_1 = require("@metaplex-foundation/mpl-token-metadata");
@@ -39,9 +39,9 @@ const TOKENLESS_PERMISSIONS_VALUE = "this app may not spend any tokens";
39
39
  const CURRENT_MAJOR = "0";
40
40
  const CURRENT_MINOR = "3";
41
41
  const CURRENT_INTENT_TRANSFER_MAJOR = "0";
42
- const CURRENT_INTENT_TRANSFER_MINOR = "1";
42
+ const CURRENT_INTENT_TRANSFER_MINOR = "2";
43
43
  const CURRENT_BRIDGE_OUT_MAJOR = "0";
44
- const CURRENT_BRIDGE_OUT_MINOR = "1";
44
+ const CURRENT_BRIDGE_OUT_MINOR = "2";
45
45
  const SESSION_ESTABLISHMENT_LOOKUP_TABLE_ADDRESS = {
46
46
  [connection_js_1.Network.Testnet]: "B8cUjJMqaWWTNNSTXBmeptjWswwCH1gTSCRYv4nu7kJW",
47
47
  [connection_js_1.Network.Mainnet]: undefined,
@@ -448,7 +448,7 @@ const BRIDGING_ADDRESS_LOOKUP_TABLE = {
448
448
  },
449
449
  [connection_js_1.Network.Mainnet]: {
450
450
  // USDC
451
- UsdcSt7U9H5bVy4WaWgeqoowe8RgXpLShCmxUFgZssx: "DjM31fhuQsjxLmpRFQpFUpZvyXzwQeNvyR1DUd8GMVmo",
451
+ uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG: "84k3mfNjmyinpZwyev7F15ChEW3Kqa3NoUkCJXXs4qkw",
452
452
  },
453
453
  };
454
454
  const buildStartSessionInstruction = async (options, sessionKey, tokens) => {
@@ -502,12 +502,52 @@ const EstablishSessionResult = {
502
502
  error,
503
503
  }),
504
504
  };
505
+ const USDC_MINT = {
506
+ [connection_js_1.Network.Mainnet]: "uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG",
507
+ [connection_js_1.Network.Testnet]: "ELNbJ1RtERV2fjtuZjbTscDekWhVzkQ1LjmiPsxp5uND",
508
+ };
509
+ const USDC_DECIMALS = 6;
510
+ const getTransferFee = async (context) => {
511
+ const { fee, ...config } = await getFee(context);
512
+ return {
513
+ ...config,
514
+ fee: fee.intrachainTransfer,
515
+ };
516
+ };
517
+ exports.getTransferFee = getTransferFee;
518
+ const getBridgeOutFee = async (context) => {
519
+ const { fee, ...config } = await getFee(context);
520
+ return {
521
+ ...config,
522
+ fee: fee.bridgeTransfer,
523
+ };
524
+ };
525
+ exports.getBridgeOutFee = getBridgeOutFee;
526
+ const getFee = async (context) => {
527
+ const program = new sessions_idls_1.IntentTransferProgram(new anchor_1.AnchorProvider(context.connection, {}, {}));
528
+ const umi = (0, umi_bundle_defaults_1.createUmi)(context.connection.rpcEndpoint);
529
+ const usdcMintAddress = USDC_MINT[context.network];
530
+ const usdcMint = new web3_js_1.PublicKey(usdcMintAddress);
531
+ const [feeConfigPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), usdcMint.toBytes()], program.programId);
532
+ const feeConfig = await program.account.feeConfig.fetch(feeConfigPda);
533
+ return {
534
+ metadata: (0, mpl_token_metadata_1.findMetadataPda)(umi, {
535
+ mint: (0, umi_1.publicKey)(usdcMintAddress),
536
+ })[0],
537
+ mint: usdcMint,
538
+ symbolOrMint: context.network === connection_js_1.Network.Mainnet ? "USDC.s" : "USDC",
539
+ decimals: USDC_DECIMALS,
540
+ fee: {
541
+ intrachainTransfer: BigInt(feeConfig.intrachainTransferFee.toString()),
542
+ bridgeTransfer: BigInt(feeConfig.bridgeTransferFee.toString()),
543
+ },
544
+ };
545
+ };
505
546
  const TRANSFER_MESSAGE_HEADER = `Fogo Transfer:
506
547
  Signing this intent will transfer the tokens as described below.
507
548
  `;
508
549
  const sendTransfer = async (options) => {
509
550
  const sourceAta = (0, spl_token_1.getAssociatedTokenAddressSync)(options.mint, options.walletPublicKey);
510
- const destinationAta = (0, spl_token_1.getAssociatedTokenAddressSync)(options.mint, options.recipient);
511
551
  const program = new sessions_idls_1.IntentTransferProgram(new anchor_1.AnchorProvider(options.context.connection, {}, {}));
512
552
  const umi = (0, umi_bundle_defaults_1.createUmi)(options.context.connection.rpcEndpoint);
513
553
  const metaplexMint = (0, umi_1.publicKey)(options.mint.toBase58());
@@ -515,12 +555,14 @@ const sendTransfer = async (options) => {
515
555
  const metadata = await (0, mpl_token_metadata_1.safeFetchMetadata)(umi, metadataAddress);
516
556
  const symbol = metadata?.symbol ?? undefined;
517
557
  return options.context.sendTransaction(undefined, [
518
- (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(options.context.payer, destinationAta, options.recipient, options.mint),
519
- await buildTransferIntentInstruction(program, options, symbol),
558
+ await buildTransferIntentInstruction(program, options, symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
520
559
  await program.methods
521
560
  .sendTokens()
522
561
  .accounts({
523
- destination: destinationAta,
562
+ destinationOwner: options.recipient,
563
+ feeMetadata: options.feeConfig.metadata,
564
+ feeMint: options.feeConfig.mint,
565
+ feeSource: (0, spl_token_1.getAssociatedTokenAddressSync)(options.feeConfig.mint, options.walletPublicKey),
524
566
  mint: options.mint,
525
567
  source: sourceAta,
526
568
  sponsor: options.context.payer,
@@ -531,7 +573,7 @@ const sendTransfer = async (options) => {
531
573
  ]);
532
574
  };
533
575
  exports.sendTransfer = sendTransfer;
534
- const buildTransferIntentInstruction = async (program, options, symbol) => {
576
+ const buildTransferIntentInstruction = async (program, options, symbol, feeToken, feeAmount) => {
535
577
  const [nonce, { decimals }] = await Promise.all([
536
578
  getNonce(program, options.walletPublicKey, NonceType.Transfer),
537
579
  (0, spl_token_1.getMint)(options.context.connection, options.mint),
@@ -544,6 +586,8 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
544
586
  token: symbol ?? options.mint.toBase58(),
545
587
  amount: amountToString(options.amount, decimals),
546
588
  recipient: options.recipient.toBase58(),
589
+ fee_token: feeToken,
590
+ fee_amount: feeAmount,
547
591
  nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
548
592
  }),
549
593
  ].join("\n"));
@@ -557,6 +601,7 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
557
601
  const BRIDGE_OUT_MESSAGE_HEADER = `Fogo Bridge Transfer:
558
602
  Signing this intent will bridge out the tokens as described below.
559
603
  `;
604
+ const BRIDGE_OUT_CUS = 220_000;
560
605
  const bridgeOut = async (options) => {
561
606
  const { wh, route, transferRequest, transferParams, decimals } = await buildWormholeTransfer(options, options.context.connection);
562
607
  // @ts-expect-error the wormhole client types are incorrect and do not
@@ -567,16 +612,16 @@ const bridgeOut = async (options) => {
567
612
  const metaplexMint = (0, umi_1.publicKey)(options.fromToken.mint.toBase58());
568
613
  const metadataAddress = (0, mpl_token_metadata_1.findMetadataPda)(umi, { mint: metaplexMint })[0];
569
614
  const outboxItem = web3_js_1.Keypair.generate();
570
- const [metadata, nttPdas] = await Promise.all([
615
+ const [metadata, nttPdas, destinationAtaExists] = await Promise.all([
571
616
  (0, mpl_token_metadata_1.safeFetchMetadata)(umi, metadataAddress),
572
617
  getNttPdas(options, wh, program, outboxItem.publicKey, new web3_js_1.PublicKey(quote.payeeAddress)),
618
+ getDestinationAtaExists(options.context, options.toToken.mint, options.walletPublicKey),
573
619
  ]);
574
- return options.context.sendTransaction(options.sessionKey, await Promise.all([
575
- buildBridgeOutIntent(program, options, decimals, metadata?.symbol),
620
+ const instructions = await Promise.all([
621
+ buildBridgeOutIntent(program, options, decimals, metadata?.symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
576
622
  program.methods
577
623
  .bridgeNttTokens({
578
- execAmount: new bn_js_1.default(quote.estimatedCost.toString()),
579
- relayInstructions: Buffer.from(quote.relayInstructions),
624
+ payDestinationAtaRent: !destinationAtaExists,
580
625
  signedQuoteBytes: Buffer.from(quote.signedQuote),
581
626
  })
582
627
  .accounts({
@@ -588,14 +633,26 @@ const bridgeOut = async (options) => {
588
633
  : new web3_js_1.PublicKey(metadataAddress),
589
634
  source: (0, spl_token_1.getAssociatedTokenAddressSync)(options.fromToken.mint, options.walletPublicKey),
590
635
  ntt: nttPdas,
636
+ feeMetadata: options.feeConfig.metadata,
637
+ feeMint: options.feeConfig.mint,
638
+ feeSource: (0, spl_token_1.getAssociatedTokenAddressSync)(options.feeConfig.mint, options.walletPublicKey),
591
639
  })
592
640
  .instruction(),
593
- ]), {
641
+ ]);
642
+ return options.context.sendTransaction(options.sessionKey, [
643
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: BRIDGE_OUT_CUS }),
644
+ ...instructions,
645
+ ], {
594
646
  extraSigners: [outboxItem],
595
647
  addressLookupTable: BRIDGING_ADDRESS_LOOKUP_TABLE[options.context.network]?.[options.fromToken.mint.toBase58()],
596
648
  });
597
649
  };
598
650
  exports.bridgeOut = bridgeOut;
651
+ const getDestinationAtaExists = async (context, token, wallet) => {
652
+ const solanaConnection = await context.getSolanaConnection();
653
+ const ataAccount = await solanaConnection.getAccountInfo((0, spl_token_1.getAssociatedTokenAddressSync)(token, wallet));
654
+ return ataAccount !== null;
655
+ };
599
656
  // Here we use the Wormhole SDKs to produce the wormhole pdas that are needed
600
657
  // for the bridge out transaction. Currently this is using wormhole SDK apis
601
658
  // that are _technically_ public but it seems likely these are not considered to
@@ -613,6 +670,10 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
613
670
  const transceiverPdas = sdk_solana_ntt_1.NTT.transceiverPdas(options.fromToken.manager);
614
671
  const [intentTransferSetterPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("intent_transfer")], program.programId);
615
672
  const wormholePdas = sdk_solana_core_1.utils.getWormholeDerivedAccounts(options.fromToken.manager, coreBridgeContract);
673
+ const [registeredTransceiverPda] = web3_js_1.PublicKey.findProgramAddressSync([
674
+ Buffer.from("registered_transceiver"),
675
+ options.fromToken.manager.toBytes(),
676
+ ], options.fromToken.manager);
616
677
  return {
617
678
  emitter: transceiverPdas.emitterAccount(),
618
679
  nttConfig: pdas.configAccount(),
@@ -625,7 +686,7 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
625
686
  nttSessionAuthority: pdas.sessionAuthority(intentTransferSetterPda, sdk_solana_ntt_1.NTT.transferArgs(options.amount, sdk_1.Wormhole.chainAddress("Solana", options.walletPublicKey.toBase58()), false)),
626
687
  nttTokenAuthority: pdas.tokenAuthority(),
627
688
  payeeNttWithExecutor: quotePayeeAddress,
628
- transceiver: options.fromToken.transceiver,
689
+ transceiver: registeredTransceiverPda,
629
690
  wormholeProgram: coreBridgeContract,
630
691
  wormholeBridge: wormholePdas.wormholeBridge,
631
692
  wormholeFeeCollector: wormholePdas.wormholeFeeCollector,
@@ -633,7 +694,7 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
633
694
  wormholeSequence: wormholePdas.wormholeSequence,
634
695
  };
635
696
  };
636
- const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
697
+ const buildBridgeOutIntent = async (program, options, decimals, symbol, feeToken, feeAmount) => {
637
698
  const nonce = await getNonce(program, options.walletPublicKey, NonceType.Bridge);
638
699
  const message = new TextEncoder().encode([
639
700
  BRIDGE_OUT_MESSAGE_HEADER,
@@ -644,6 +705,8 @@ const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
644
705
  token: symbol ?? options.fromToken.mint.toBase58(),
645
706
  amount: amountToString(options.amount, decimals),
646
707
  recipient_address: options.walletPublicKey.toBase58(),
708
+ fee_token: feeToken,
709
+ fee_amount: feeAmount,
647
710
  nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
648
711
  }),
649
712
  ].join("\n"));
package/esm/index.d.ts CHANGED
@@ -1310,6 +1310,20 @@ export type Session = {
1310
1310
  sendTransaction: (instructions: Parameters<SessionContext["sendTransaction"]>[1]) => Promise<TransactionResult>;
1311
1311
  sessionInfo: NonNullable<z.infer<typeof sessionInfoSchema>>;
1312
1312
  };
1313
+ export declare const getTransferFee: (context: SessionContext) => Promise<{
1314
+ fee: bigint;
1315
+ metadata: import("@metaplex-foundation/umi").PublicKey<string>;
1316
+ mint: PublicKey;
1317
+ symbolOrMint: string;
1318
+ decimals: number;
1319
+ }>;
1320
+ export declare const getBridgeOutFee: (context: SessionContext) => Promise<{
1321
+ fee: bigint;
1322
+ metadata: import("@metaplex-foundation/umi").PublicKey<string>;
1323
+ mint: PublicKey;
1324
+ symbolOrMint: string;
1325
+ decimals: number;
1326
+ }>;
1313
1327
  type SendTransferOptions = {
1314
1328
  context: SessionContext;
1315
1329
  walletPublicKey: PublicKey;
@@ -1317,6 +1331,7 @@ type SendTransferOptions = {
1317
1331
  mint: PublicKey;
1318
1332
  amount: bigint;
1319
1333
  recipient: PublicKey;
1334
+ feeConfig: Awaited<ReturnType<typeof getTransferFee>>;
1320
1335
  };
1321
1336
  export declare const sendTransfer: (options: SendTransferOptions) => Promise<TransactionResult>;
1322
1337
  type SendBridgeOutOptions = {
@@ -1332,6 +1347,7 @@ type SendBridgeOutOptions = {
1332
1347
  toToken: WormholeToken & {
1333
1348
  chain: "Solana";
1334
1349
  };
1350
+ feeConfig: Awaited<ReturnType<typeof getBridgeOutFee>>;
1335
1351
  };
1336
1352
  type WormholeToken = {
1337
1353
  chain: Chain;
package/esm/index.js CHANGED
@@ -7,7 +7,7 @@ import { sha256 } from "@noble/hashes/sha2";
7
7
  import { fromLegacyPublicKey } from "@solana/compat";
8
8
  import { generateKeyPair, getAddressFromPublicKey, getProgramDerivedAddress, signatureBytes, verifySignature, } from "@solana/kit";
9
9
  import { createAssociatedTokenAccountIdempotentInstruction, getAssociatedTokenAddressSync, getMint, } from "@solana/spl-token";
10
- import { Connection, Ed25519Program, Keypair, PublicKey, } from "@solana/web3.js";
10
+ import { ComputeBudgetProgram, Connection, Ed25519Program, Keypair, PublicKey, } from "@solana/web3.js";
11
11
  import { Wormhole, wormhole, routes } from "@wormhole-foundation/sdk";
12
12
  import solanaSdk from "@wormhole-foundation/sdk/solana";
13
13
  import { contracts } from "@wormhole-foundation/sdk-base";
@@ -29,9 +29,9 @@ const TOKENLESS_PERMISSIONS_VALUE = "this app may not spend any tokens";
29
29
  const CURRENT_MAJOR = "0";
30
30
  const CURRENT_MINOR = "3";
31
31
  const CURRENT_INTENT_TRANSFER_MAJOR = "0";
32
- const CURRENT_INTENT_TRANSFER_MINOR = "1";
32
+ const CURRENT_INTENT_TRANSFER_MINOR = "2";
33
33
  const CURRENT_BRIDGE_OUT_MAJOR = "0";
34
- const CURRENT_BRIDGE_OUT_MINOR = "1";
34
+ const CURRENT_BRIDGE_OUT_MINOR = "2";
35
35
  const SESSION_ESTABLISHMENT_LOOKUP_TABLE_ADDRESS = {
36
36
  [Network.Testnet]: "B8cUjJMqaWWTNNSTXBmeptjWswwCH1gTSCRYv4nu7kJW",
37
37
  [Network.Mainnet]: undefined,
@@ -432,7 +432,7 @@ const BRIDGING_ADDRESS_LOOKUP_TABLE = {
432
432
  },
433
433
  [Network.Mainnet]: {
434
434
  // USDC
435
- UsdcSt7U9H5bVy4WaWgeqoowe8RgXpLShCmxUFgZssx: "DjM31fhuQsjxLmpRFQpFUpZvyXzwQeNvyR1DUd8GMVmo",
435
+ uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG: "84k3mfNjmyinpZwyev7F15ChEW3Kqa3NoUkCJXXs4qkw",
436
436
  },
437
437
  };
438
438
  const buildStartSessionInstruction = async (options, sessionKey, tokens) => {
@@ -486,12 +486,50 @@ const EstablishSessionResult = {
486
486
  error,
487
487
  }),
488
488
  };
489
+ const USDC_MINT = {
490
+ [Network.Mainnet]: "uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG",
491
+ [Network.Testnet]: "ELNbJ1RtERV2fjtuZjbTscDekWhVzkQ1LjmiPsxp5uND",
492
+ };
493
+ const USDC_DECIMALS = 6;
494
+ export const getTransferFee = async (context) => {
495
+ const { fee, ...config } = await getFee(context);
496
+ return {
497
+ ...config,
498
+ fee: fee.intrachainTransfer,
499
+ };
500
+ };
501
+ export const getBridgeOutFee = async (context) => {
502
+ const { fee, ...config } = await getFee(context);
503
+ return {
504
+ ...config,
505
+ fee: fee.bridgeTransfer,
506
+ };
507
+ };
508
+ const getFee = async (context) => {
509
+ const program = new IntentTransferProgram(new AnchorProvider(context.connection, {}, {}));
510
+ const umi = createUmi(context.connection.rpcEndpoint);
511
+ const usdcMintAddress = USDC_MINT[context.network];
512
+ const usdcMint = new PublicKey(usdcMintAddress);
513
+ const [feeConfigPda] = PublicKey.findProgramAddressSync([Buffer.from("fee_config"), usdcMint.toBytes()], program.programId);
514
+ const feeConfig = await program.account.feeConfig.fetch(feeConfigPda);
515
+ return {
516
+ metadata: findMetadataPda(umi, {
517
+ mint: metaplexPublicKey(usdcMintAddress),
518
+ })[0],
519
+ mint: usdcMint,
520
+ symbolOrMint: context.network === Network.Mainnet ? "USDC.s" : "USDC",
521
+ decimals: USDC_DECIMALS,
522
+ fee: {
523
+ intrachainTransfer: BigInt(feeConfig.intrachainTransferFee.toString()),
524
+ bridgeTransfer: BigInt(feeConfig.bridgeTransferFee.toString()),
525
+ },
526
+ };
527
+ };
489
528
  const TRANSFER_MESSAGE_HEADER = `Fogo Transfer:
490
529
  Signing this intent will transfer the tokens as described below.
491
530
  `;
492
531
  export const sendTransfer = async (options) => {
493
532
  const sourceAta = getAssociatedTokenAddressSync(options.mint, options.walletPublicKey);
494
- const destinationAta = getAssociatedTokenAddressSync(options.mint, options.recipient);
495
533
  const program = new IntentTransferProgram(new AnchorProvider(options.context.connection, {}, {}));
496
534
  const umi = createUmi(options.context.connection.rpcEndpoint);
497
535
  const metaplexMint = metaplexPublicKey(options.mint.toBase58());
@@ -499,12 +537,14 @@ export const sendTransfer = async (options) => {
499
537
  const metadata = await safeFetchMetadata(umi, metadataAddress);
500
538
  const symbol = metadata?.symbol ?? undefined;
501
539
  return options.context.sendTransaction(undefined, [
502
- createAssociatedTokenAccountIdempotentInstruction(options.context.payer, destinationAta, options.recipient, options.mint),
503
- await buildTransferIntentInstruction(program, options, symbol),
540
+ await buildTransferIntentInstruction(program, options, symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
504
541
  await program.methods
505
542
  .sendTokens()
506
543
  .accounts({
507
- destination: destinationAta,
544
+ destinationOwner: options.recipient,
545
+ feeMetadata: options.feeConfig.metadata,
546
+ feeMint: options.feeConfig.mint,
547
+ feeSource: getAssociatedTokenAddressSync(options.feeConfig.mint, options.walletPublicKey),
508
548
  mint: options.mint,
509
549
  source: sourceAta,
510
550
  sponsor: options.context.payer,
@@ -514,7 +554,7 @@ export const sendTransfer = async (options) => {
514
554
  .instruction(),
515
555
  ]);
516
556
  };
517
- const buildTransferIntentInstruction = async (program, options, symbol) => {
557
+ const buildTransferIntentInstruction = async (program, options, symbol, feeToken, feeAmount) => {
518
558
  const [nonce, { decimals }] = await Promise.all([
519
559
  getNonce(program, options.walletPublicKey, NonceType.Transfer),
520
560
  getMint(options.context.connection, options.mint),
@@ -527,6 +567,8 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
527
567
  token: symbol ?? options.mint.toBase58(),
528
568
  amount: amountToString(options.amount, decimals),
529
569
  recipient: options.recipient.toBase58(),
570
+ fee_token: feeToken,
571
+ fee_amount: feeAmount,
530
572
  nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
531
573
  }),
532
574
  ].join("\n"));
@@ -540,6 +582,7 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
540
582
  const BRIDGE_OUT_MESSAGE_HEADER = `Fogo Bridge Transfer:
541
583
  Signing this intent will bridge out the tokens as described below.
542
584
  `;
585
+ const BRIDGE_OUT_CUS = 220_000;
543
586
  export const bridgeOut = async (options) => {
544
587
  const { wh, route, transferRequest, transferParams, decimals } = await buildWormholeTransfer(options, options.context.connection);
545
588
  // @ts-expect-error the wormhole client types are incorrect and do not
@@ -550,16 +593,16 @@ export const bridgeOut = async (options) => {
550
593
  const metaplexMint = metaplexPublicKey(options.fromToken.mint.toBase58());
551
594
  const metadataAddress = findMetadataPda(umi, { mint: metaplexMint })[0];
552
595
  const outboxItem = Keypair.generate();
553
- const [metadata, nttPdas] = await Promise.all([
596
+ const [metadata, nttPdas, destinationAtaExists] = await Promise.all([
554
597
  safeFetchMetadata(umi, metadataAddress),
555
598
  getNttPdas(options, wh, program, outboxItem.publicKey, new PublicKey(quote.payeeAddress)),
599
+ getDestinationAtaExists(options.context, options.toToken.mint, options.walletPublicKey),
556
600
  ]);
557
- return options.context.sendTransaction(options.sessionKey, await Promise.all([
558
- buildBridgeOutIntent(program, options, decimals, metadata?.symbol),
601
+ const instructions = await Promise.all([
602
+ buildBridgeOutIntent(program, options, decimals, metadata?.symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
559
603
  program.methods
560
604
  .bridgeNttTokens({
561
- execAmount: new BN(quote.estimatedCost.toString()),
562
- relayInstructions: Buffer.from(quote.relayInstructions),
605
+ payDestinationAtaRent: !destinationAtaExists,
563
606
  signedQuoteBytes: Buffer.from(quote.signedQuote),
564
607
  })
565
608
  .accounts({
@@ -571,13 +614,25 @@ export const bridgeOut = async (options) => {
571
614
  : new PublicKey(metadataAddress),
572
615
  source: getAssociatedTokenAddressSync(options.fromToken.mint, options.walletPublicKey),
573
616
  ntt: nttPdas,
617
+ feeMetadata: options.feeConfig.metadata,
618
+ feeMint: options.feeConfig.mint,
619
+ feeSource: getAssociatedTokenAddressSync(options.feeConfig.mint, options.walletPublicKey),
574
620
  })
575
621
  .instruction(),
576
- ]), {
622
+ ]);
623
+ return options.context.sendTransaction(options.sessionKey, [
624
+ ComputeBudgetProgram.setComputeUnitLimit({ units: BRIDGE_OUT_CUS }),
625
+ ...instructions,
626
+ ], {
577
627
  extraSigners: [outboxItem],
578
628
  addressLookupTable: BRIDGING_ADDRESS_LOOKUP_TABLE[options.context.network]?.[options.fromToken.mint.toBase58()],
579
629
  });
580
630
  };
631
+ const getDestinationAtaExists = async (context, token, wallet) => {
632
+ const solanaConnection = await context.getSolanaConnection();
633
+ const ataAccount = await solanaConnection.getAccountInfo(getAssociatedTokenAddressSync(token, wallet));
634
+ return ataAccount !== null;
635
+ };
581
636
  // Here we use the Wormhole SDKs to produce the wormhole pdas that are needed
582
637
  // for the bridge out transaction. Currently this is using wormhole SDK apis
583
638
  // that are _technically_ public but it seems likely these are not considered to
@@ -595,6 +650,10 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
595
650
  const transceiverPdas = NTT.transceiverPdas(options.fromToken.manager);
596
651
  const [intentTransferSetterPda] = PublicKey.findProgramAddressSync([Buffer.from("intent_transfer")], program.programId);
597
652
  const wormholePdas = utils.getWormholeDerivedAccounts(options.fromToken.manager, coreBridgeContract);
653
+ const [registeredTransceiverPda] = PublicKey.findProgramAddressSync([
654
+ Buffer.from("registered_transceiver"),
655
+ options.fromToken.manager.toBytes(),
656
+ ], options.fromToken.manager);
598
657
  return {
599
658
  emitter: transceiverPdas.emitterAccount(),
600
659
  nttConfig: pdas.configAccount(),
@@ -607,7 +666,7 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
607
666
  nttSessionAuthority: pdas.sessionAuthority(intentTransferSetterPda, NTT.transferArgs(options.amount, Wormhole.chainAddress("Solana", options.walletPublicKey.toBase58()), false)),
608
667
  nttTokenAuthority: pdas.tokenAuthority(),
609
668
  payeeNttWithExecutor: quotePayeeAddress,
610
- transceiver: options.fromToken.transceiver,
669
+ transceiver: registeredTransceiverPda,
611
670
  wormholeProgram: coreBridgeContract,
612
671
  wormholeBridge: wormholePdas.wormholeBridge,
613
672
  wormholeFeeCollector: wormholePdas.wormholeFeeCollector,
@@ -615,7 +674,7 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
615
674
  wormholeSequence: wormholePdas.wormholeSequence,
616
675
  };
617
676
  };
618
- const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
677
+ const buildBridgeOutIntent = async (program, options, decimals, symbol, feeToken, feeAmount) => {
619
678
  const nonce = await getNonce(program, options.walletPublicKey, NonceType.Bridge);
620
679
  const message = new TextEncoder().encode([
621
680
  BRIDGE_OUT_MESSAGE_HEADER,
@@ -626,6 +685,8 @@ const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
626
685
  token: symbol ?? options.fromToken.mint.toBase58(),
627
686
  amount: amountToString(options.amount, decimals),
628
687
  recipient_address: options.walletPublicKey.toBase58(),
688
+ fee_token: feeToken,
689
+ fee_amount: feeAmount,
629
690
  nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
630
691
  }),
631
692
  ].join("\n"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fogo/sessions-sdk",
3
- "version": "0.0.20",
3
+ "version": "0.0.21",
4
4
  "description": "A set of utilities for integrating with Fogo sessions",
5
5
  "keywords": [
6
6
  "fogo",
@@ -44,6 +44,6 @@
44
44
  "bn.js": "^5.1.2",
45
45
  "bs58": "^6.0.0",
46
46
  "zod": "^3.25.62",
47
- "@fogo/sessions-idls": "^0.0.8"
47
+ "@fogo/sessions-idls": "^0.0.9"
48
48
  }
49
49
  }