@fogo/sessions-sdk 0.0.19 → 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,
@@ -446,7 +446,10 @@ const BRIDGING_ADDRESS_LOOKUP_TABLE = {
446
446
  // USDC
447
447
  ELNbJ1RtERV2fjtuZjbTscDekWhVzkQ1LjmiPsxp5uND: "4FCi6LptexBdZtaePsoCMeb1XpCijxnWu96g5LsSb6WP",
448
448
  },
449
- [connection_js_1.Network.Mainnet]: undefined,
449
+ [connection_js_1.Network.Mainnet]: {
450
+ // USDC
451
+ uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG: "84k3mfNjmyinpZwyev7F15ChEW3Kqa3NoUkCJXXs4qkw",
452
+ },
450
453
  };
451
454
  const buildStartSessionInstruction = async (options, sessionKey, tokens) => {
452
455
  const instruction = new sessions_idls_1.SessionManagerProgram(new anchor_1.AnchorProvider(options.context.connection, {}, {})).methods
@@ -499,12 +502,52 @@ const EstablishSessionResult = {
499
502
  error,
500
503
  }),
501
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
+ };
502
546
  const TRANSFER_MESSAGE_HEADER = `Fogo Transfer:
503
547
  Signing this intent will transfer the tokens as described below.
504
548
  `;
505
549
  const sendTransfer = async (options) => {
506
550
  const sourceAta = (0, spl_token_1.getAssociatedTokenAddressSync)(options.mint, options.walletPublicKey);
507
- const destinationAta = (0, spl_token_1.getAssociatedTokenAddressSync)(options.mint, options.recipient);
508
551
  const program = new sessions_idls_1.IntentTransferProgram(new anchor_1.AnchorProvider(options.context.connection, {}, {}));
509
552
  const umi = (0, umi_bundle_defaults_1.createUmi)(options.context.connection.rpcEndpoint);
510
553
  const metaplexMint = (0, umi_1.publicKey)(options.mint.toBase58());
@@ -512,12 +555,14 @@ const sendTransfer = async (options) => {
512
555
  const metadata = await (0, mpl_token_metadata_1.safeFetchMetadata)(umi, metadataAddress);
513
556
  const symbol = metadata?.symbol ?? undefined;
514
557
  return options.context.sendTransaction(undefined, [
515
- (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(options.context.payer, destinationAta, options.recipient, options.mint),
516
- await buildTransferIntentInstruction(program, options, symbol),
558
+ await buildTransferIntentInstruction(program, options, symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
517
559
  await program.methods
518
560
  .sendTokens()
519
561
  .accounts({
520
- 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),
521
566
  mint: options.mint,
522
567
  source: sourceAta,
523
568
  sponsor: options.context.payer,
@@ -528,7 +573,7 @@ const sendTransfer = async (options) => {
528
573
  ]);
529
574
  };
530
575
  exports.sendTransfer = sendTransfer;
531
- const buildTransferIntentInstruction = async (program, options, symbol) => {
576
+ const buildTransferIntentInstruction = async (program, options, symbol, feeToken, feeAmount) => {
532
577
  const [nonce, { decimals }] = await Promise.all([
533
578
  getNonce(program, options.walletPublicKey, NonceType.Transfer),
534
579
  (0, spl_token_1.getMint)(options.context.connection, options.mint),
@@ -541,6 +586,8 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
541
586
  token: symbol ?? options.mint.toBase58(),
542
587
  amount: amountToString(options.amount, decimals),
543
588
  recipient: options.recipient.toBase58(),
589
+ fee_token: feeToken,
590
+ fee_amount: feeAmount,
544
591
  nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
545
592
  }),
546
593
  ].join("\n"));
@@ -554,6 +601,7 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
554
601
  const BRIDGE_OUT_MESSAGE_HEADER = `Fogo Bridge Transfer:
555
602
  Signing this intent will bridge out the tokens as described below.
556
603
  `;
604
+ const BRIDGE_OUT_CUS = 220_000;
557
605
  const bridgeOut = async (options) => {
558
606
  const { wh, route, transferRequest, transferParams, decimals } = await buildWormholeTransfer(options, options.context.connection);
559
607
  // @ts-expect-error the wormhole client types are incorrect and do not
@@ -564,35 +612,47 @@ const bridgeOut = async (options) => {
564
612
  const metaplexMint = (0, umi_1.publicKey)(options.fromToken.mint.toBase58());
565
613
  const metadataAddress = (0, mpl_token_metadata_1.findMetadataPda)(umi, { mint: metaplexMint })[0];
566
614
  const outboxItem = web3_js_1.Keypair.generate();
567
- const [metadata, nttPdas] = await Promise.all([
615
+ const [metadata, nttPdas, destinationAtaExists] = await Promise.all([
568
616
  (0, mpl_token_metadata_1.safeFetchMetadata)(umi, metadataAddress),
569
617
  getNttPdas(options, wh, program, outboxItem.publicKey, new web3_js_1.PublicKey(quote.payeeAddress)),
618
+ getDestinationAtaExists(options.context, options.toToken.mint, options.walletPublicKey),
619
+ ]);
620
+ const instructions = await Promise.all([
621
+ buildBridgeOutIntent(program, options, decimals, metadata?.symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
622
+ program.methods
623
+ .bridgeNttTokens({
624
+ payDestinationAtaRent: !destinationAtaExists,
625
+ signedQuoteBytes: Buffer.from(quote.signedQuote),
626
+ })
627
+ .accounts({
628
+ sponsor: options.context.payer,
629
+ mint: options.fromToken.mint,
630
+ metadata: metadata?.symbol === undefined
631
+ ? // eslint-disable-next-line unicorn/no-null
632
+ null
633
+ : new web3_js_1.PublicKey(metadataAddress),
634
+ source: (0, spl_token_1.getAssociatedTokenAddressSync)(options.fromToken.mint, options.walletPublicKey),
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),
639
+ })
640
+ .instruction(),
570
641
  ]);
571
- const bridgeInstruction = await program.methods
572
- .bridgeNttTokens({
573
- execAmount: new bn_js_1.default(quote.estimatedCost.toString()),
574
- relayInstructions: Buffer.from(quote.relayInstructions),
575
- signedQuoteBytes: Buffer.from(quote.signedQuote),
576
- })
577
- .accounts({
578
- sponsor: options.context.payer,
579
- mint: options.fromToken.mint,
580
- metadata:
581
- // eslint-disable-next-line unicorn/no-null
582
- metadata?.symbol === undefined ? null : new web3_js_1.PublicKey(metadataAddress),
583
- source: (0, spl_token_1.getAssociatedTokenAddressSync)(options.fromToken.mint, options.walletPublicKey),
584
- ntt: nttPdas,
585
- })
586
- .instruction();
587
642
  return options.context.sendTransaction(options.sessionKey, [
588
- await buildBridgeOutIntent(program, options, decimals, metadata?.symbol),
589
- bridgeInstruction,
643
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: BRIDGE_OUT_CUS }),
644
+ ...instructions,
590
645
  ], {
591
646
  extraSigners: [outboxItem],
592
647
  addressLookupTable: BRIDGING_ADDRESS_LOOKUP_TABLE[options.context.network]?.[options.fromToken.mint.toBase58()],
593
648
  });
594
649
  };
595
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
+ };
596
656
  // Here we use the Wormhole SDKs to produce the wormhole pdas that are needed
597
657
  // for the bridge out transaction. Currently this is using wormhole SDK apis
598
658
  // that are _technically_ public but it seems likely these are not considered to
@@ -610,6 +670,10 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
610
670
  const transceiverPdas = sdk_solana_ntt_1.NTT.transceiverPdas(options.fromToken.manager);
611
671
  const [intentTransferSetterPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("intent_transfer")], program.programId);
612
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);
613
677
  return {
614
678
  emitter: transceiverPdas.emitterAccount(),
615
679
  nttConfig: pdas.configAccount(),
@@ -622,14 +686,15 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
622
686
  nttSessionAuthority: pdas.sessionAuthority(intentTransferSetterPda, sdk_solana_ntt_1.NTT.transferArgs(options.amount, sdk_1.Wormhole.chainAddress("Solana", options.walletPublicKey.toBase58()), false)),
623
687
  nttTokenAuthority: pdas.tokenAuthority(),
624
688
  payeeNttWithExecutor: quotePayeeAddress,
625
- transceiver: options.fromToken.transceiver,
689
+ transceiver: registeredTransceiverPda,
690
+ wormholeProgram: coreBridgeContract,
626
691
  wormholeBridge: wormholePdas.wormholeBridge,
627
692
  wormholeFeeCollector: wormholePdas.wormholeFeeCollector,
628
693
  wormholeMessage: transceiverPdas.wormholeMessageAccount(outboxItemPublicKey),
629
694
  wormholeSequence: wormholePdas.wormholeSequence,
630
695
  };
631
696
  };
632
- const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
697
+ const buildBridgeOutIntent = async (program, options, decimals, symbol, feeToken, feeAmount) => {
633
698
  const nonce = await getNonce(program, options.walletPublicKey, NonceType.Bridge);
634
699
  const message = new TextEncoder().encode([
635
700
  BRIDGE_OUT_MESSAGE_HEADER,
@@ -640,6 +705,8 @@ const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
640
705
  token: symbol ?? options.fromToken.mint.toBase58(),
641
706
  amount: amountToString(options.amount, decimals),
642
707
  recipient_address: options.walletPublicKey.toBase58(),
708
+ fee_token: feeToken,
709
+ fee_amount: feeAmount,
643
710
  nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
644
711
  }),
645
712
  ].join("\n"));
@@ -665,7 +732,7 @@ const bridgeIn = async (options) => {
665
732
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
666
733
  transaction.transaction, solanaConnection,
667
734
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
668
- { signers: transaction.signers, skipPreflight: true }))),
735
+ { signers: transaction.signers }))),
669
736
  }, quote, sdk_1.Wormhole.chainAddress("Fogo", options.walletPublicKey.toBase58())));
670
737
  }
671
738
  else {
@@ -674,8 +741,11 @@ const bridgeIn = async (options) => {
674
741
  };
675
742
  exports.bridgeIn = bridgeIn;
676
743
  const buildWormholeTransfer = async (options, connection) => {
744
+ const solanaConnection = await options.context.getSolanaConnection();
677
745
  const [wh, { decimals }] = await Promise.all([
678
- (0, sdk_1.wormhole)(NETWORK_TO_WORMHOLE_NETWORK[options.context.network], [solana_1.default]),
746
+ (0, sdk_1.wormhole)(NETWORK_TO_WORMHOLE_NETWORK[options.context.network], [solana_1.default], {
747
+ chains: { Solana: { rpc: solanaConnection.rpcEndpoint } },
748
+ }),
679
749
  (0, spl_token_1.getMint)(connection, options.fromToken.mint),
680
750
  ]);
681
751
  const Route = (0, sdk_route_ntt_1.nttExecutorRoute)({
@@ -710,6 +780,7 @@ const buildWormholeTransfer = async (options, connection) => {
710
780
  });
711
781
  const route = new Route(wh);
712
782
  const transferRequest = await sdk_1.routes.RouteTransferRequest.create(wh, {
783
+ recipient: sdk_1.Wormhole.chainAddress(options.toToken.chain, options.walletPublicKey.toBase58()),
713
784
  source: sdk_1.Wormhole.tokenId(options.fromToken.chain, options.fromToken.mint.toBase58()),
714
785
  destination: sdk_1.Wormhole.tokenId(options.toToken.chain, options.toToken.mint.toBase58()),
715
786
  });
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,
@@ -430,7 +430,10 @@ const BRIDGING_ADDRESS_LOOKUP_TABLE = {
430
430
  // USDC
431
431
  ELNbJ1RtERV2fjtuZjbTscDekWhVzkQ1LjmiPsxp5uND: "4FCi6LptexBdZtaePsoCMeb1XpCijxnWu96g5LsSb6WP",
432
432
  },
433
- [Network.Mainnet]: undefined,
433
+ [Network.Mainnet]: {
434
+ // USDC
435
+ uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG: "84k3mfNjmyinpZwyev7F15ChEW3Kqa3NoUkCJXXs4qkw",
436
+ },
434
437
  };
435
438
  const buildStartSessionInstruction = async (options, sessionKey, tokens) => {
436
439
  const instruction = new SessionManagerProgram(new AnchorProvider(options.context.connection, {}, {})).methods
@@ -483,12 +486,50 @@ const EstablishSessionResult = {
483
486
  error,
484
487
  }),
485
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
+ };
486
528
  const TRANSFER_MESSAGE_HEADER = `Fogo Transfer:
487
529
  Signing this intent will transfer the tokens as described below.
488
530
  `;
489
531
  export const sendTransfer = async (options) => {
490
532
  const sourceAta = getAssociatedTokenAddressSync(options.mint, options.walletPublicKey);
491
- const destinationAta = getAssociatedTokenAddressSync(options.mint, options.recipient);
492
533
  const program = new IntentTransferProgram(new AnchorProvider(options.context.connection, {}, {}));
493
534
  const umi = createUmi(options.context.connection.rpcEndpoint);
494
535
  const metaplexMint = metaplexPublicKey(options.mint.toBase58());
@@ -496,12 +537,14 @@ export const sendTransfer = async (options) => {
496
537
  const metadata = await safeFetchMetadata(umi, metadataAddress);
497
538
  const symbol = metadata?.symbol ?? undefined;
498
539
  return options.context.sendTransaction(undefined, [
499
- createAssociatedTokenAccountIdempotentInstruction(options.context.payer, destinationAta, options.recipient, options.mint),
500
- await buildTransferIntentInstruction(program, options, symbol),
540
+ await buildTransferIntentInstruction(program, options, symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
501
541
  await program.methods
502
542
  .sendTokens()
503
543
  .accounts({
504
- destination: destinationAta,
544
+ destinationOwner: options.recipient,
545
+ feeMetadata: options.feeConfig.metadata,
546
+ feeMint: options.feeConfig.mint,
547
+ feeSource: getAssociatedTokenAddressSync(options.feeConfig.mint, options.walletPublicKey),
505
548
  mint: options.mint,
506
549
  source: sourceAta,
507
550
  sponsor: options.context.payer,
@@ -511,7 +554,7 @@ export const sendTransfer = async (options) => {
511
554
  .instruction(),
512
555
  ]);
513
556
  };
514
- const buildTransferIntentInstruction = async (program, options, symbol) => {
557
+ const buildTransferIntentInstruction = async (program, options, symbol, feeToken, feeAmount) => {
515
558
  const [nonce, { decimals }] = await Promise.all([
516
559
  getNonce(program, options.walletPublicKey, NonceType.Transfer),
517
560
  getMint(options.context.connection, options.mint),
@@ -524,6 +567,8 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
524
567
  token: symbol ?? options.mint.toBase58(),
525
568
  amount: amountToString(options.amount, decimals),
526
569
  recipient: options.recipient.toBase58(),
570
+ fee_token: feeToken,
571
+ fee_amount: feeAmount,
527
572
  nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
528
573
  }),
529
574
  ].join("\n"));
@@ -537,6 +582,7 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
537
582
  const BRIDGE_OUT_MESSAGE_HEADER = `Fogo Bridge Transfer:
538
583
  Signing this intent will bridge out the tokens as described below.
539
584
  `;
585
+ const BRIDGE_OUT_CUS = 220_000;
540
586
  export const bridgeOut = async (options) => {
541
587
  const { wh, route, transferRequest, transferParams, decimals } = await buildWormholeTransfer(options, options.context.connection);
542
588
  // @ts-expect-error the wormhole client types are incorrect and do not
@@ -547,34 +593,46 @@ export const bridgeOut = async (options) => {
547
593
  const metaplexMint = metaplexPublicKey(options.fromToken.mint.toBase58());
548
594
  const metadataAddress = findMetadataPda(umi, { mint: metaplexMint })[0];
549
595
  const outboxItem = Keypair.generate();
550
- const [metadata, nttPdas] = await Promise.all([
596
+ const [metadata, nttPdas, destinationAtaExists] = await Promise.all([
551
597
  safeFetchMetadata(umi, metadataAddress),
552
598
  getNttPdas(options, wh, program, outboxItem.publicKey, new PublicKey(quote.payeeAddress)),
599
+ getDestinationAtaExists(options.context, options.toToken.mint, options.walletPublicKey),
600
+ ]);
601
+ const instructions = await Promise.all([
602
+ buildBridgeOutIntent(program, options, decimals, metadata?.symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
603
+ program.methods
604
+ .bridgeNttTokens({
605
+ payDestinationAtaRent: !destinationAtaExists,
606
+ signedQuoteBytes: Buffer.from(quote.signedQuote),
607
+ })
608
+ .accounts({
609
+ sponsor: options.context.payer,
610
+ mint: options.fromToken.mint,
611
+ metadata: metadata?.symbol === undefined
612
+ ? // eslint-disable-next-line unicorn/no-null
613
+ null
614
+ : new PublicKey(metadataAddress),
615
+ source: getAssociatedTokenAddressSync(options.fromToken.mint, options.walletPublicKey),
616
+ ntt: nttPdas,
617
+ feeMetadata: options.feeConfig.metadata,
618
+ feeMint: options.feeConfig.mint,
619
+ feeSource: getAssociatedTokenAddressSync(options.feeConfig.mint, options.walletPublicKey),
620
+ })
621
+ .instruction(),
553
622
  ]);
554
- const bridgeInstruction = await program.methods
555
- .bridgeNttTokens({
556
- execAmount: new BN(quote.estimatedCost.toString()),
557
- relayInstructions: Buffer.from(quote.relayInstructions),
558
- signedQuoteBytes: Buffer.from(quote.signedQuote),
559
- })
560
- .accounts({
561
- sponsor: options.context.payer,
562
- mint: options.fromToken.mint,
563
- metadata:
564
- // eslint-disable-next-line unicorn/no-null
565
- metadata?.symbol === undefined ? null : new PublicKey(metadataAddress),
566
- source: getAssociatedTokenAddressSync(options.fromToken.mint, options.walletPublicKey),
567
- ntt: nttPdas,
568
- })
569
- .instruction();
570
623
  return options.context.sendTransaction(options.sessionKey, [
571
- await buildBridgeOutIntent(program, options, decimals, metadata?.symbol),
572
- bridgeInstruction,
624
+ ComputeBudgetProgram.setComputeUnitLimit({ units: BRIDGE_OUT_CUS }),
625
+ ...instructions,
573
626
  ], {
574
627
  extraSigners: [outboxItem],
575
628
  addressLookupTable: BRIDGING_ADDRESS_LOOKUP_TABLE[options.context.network]?.[options.fromToken.mint.toBase58()],
576
629
  });
577
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
+ };
578
636
  // Here we use the Wormhole SDKs to produce the wormhole pdas that are needed
579
637
  // for the bridge out transaction. Currently this is using wormhole SDK apis
580
638
  // that are _technically_ public but it seems likely these are not considered to
@@ -592,6 +650,10 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
592
650
  const transceiverPdas = NTT.transceiverPdas(options.fromToken.manager);
593
651
  const [intentTransferSetterPda] = PublicKey.findProgramAddressSync([Buffer.from("intent_transfer")], program.programId);
594
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);
595
657
  return {
596
658
  emitter: transceiverPdas.emitterAccount(),
597
659
  nttConfig: pdas.configAccount(),
@@ -604,14 +666,15 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
604
666
  nttSessionAuthority: pdas.sessionAuthority(intentTransferSetterPda, NTT.transferArgs(options.amount, Wormhole.chainAddress("Solana", options.walletPublicKey.toBase58()), false)),
605
667
  nttTokenAuthority: pdas.tokenAuthority(),
606
668
  payeeNttWithExecutor: quotePayeeAddress,
607
- transceiver: options.fromToken.transceiver,
669
+ transceiver: registeredTransceiverPda,
670
+ wormholeProgram: coreBridgeContract,
608
671
  wormholeBridge: wormholePdas.wormholeBridge,
609
672
  wormholeFeeCollector: wormholePdas.wormholeFeeCollector,
610
673
  wormholeMessage: transceiverPdas.wormholeMessageAccount(outboxItemPublicKey),
611
674
  wormholeSequence: wormholePdas.wormholeSequence,
612
675
  };
613
676
  };
614
- const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
677
+ const buildBridgeOutIntent = async (program, options, decimals, symbol, feeToken, feeAmount) => {
615
678
  const nonce = await getNonce(program, options.walletPublicKey, NonceType.Bridge);
616
679
  const message = new TextEncoder().encode([
617
680
  BRIDGE_OUT_MESSAGE_HEADER,
@@ -622,6 +685,8 @@ const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
622
685
  token: symbol ?? options.fromToken.mint.toBase58(),
623
686
  amount: amountToString(options.amount, decimals),
624
687
  recipient_address: options.walletPublicKey.toBase58(),
688
+ fee_token: feeToken,
689
+ fee_amount: feeAmount,
625
690
  nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
626
691
  }),
627
692
  ].join("\n"));
@@ -647,7 +712,7 @@ export const bridgeIn = async (options) => {
647
712
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
648
713
  transaction.transaction, solanaConnection,
649
714
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
650
- { signers: transaction.signers, skipPreflight: true }))),
715
+ { signers: transaction.signers }))),
651
716
  }, quote, Wormhole.chainAddress("Fogo", options.walletPublicKey.toBase58())));
652
717
  }
653
718
  else {
@@ -655,8 +720,11 @@ export const bridgeIn = async (options) => {
655
720
  }
656
721
  };
657
722
  const buildWormholeTransfer = async (options, connection) => {
723
+ const solanaConnection = await options.context.getSolanaConnection();
658
724
  const [wh, { decimals }] = await Promise.all([
659
- wormhole(NETWORK_TO_WORMHOLE_NETWORK[options.context.network], [solanaSdk]),
725
+ wormhole(NETWORK_TO_WORMHOLE_NETWORK[options.context.network], [solanaSdk], {
726
+ chains: { Solana: { rpc: solanaConnection.rpcEndpoint } },
727
+ }),
660
728
  getMint(connection, options.fromToken.mint),
661
729
  ]);
662
730
  const Route = nttExecutorRoute({
@@ -691,6 +759,7 @@ const buildWormholeTransfer = async (options, connection) => {
691
759
  });
692
760
  const route = new Route(wh);
693
761
  const transferRequest = await routes.RouteTransferRequest.create(wh, {
762
+ recipient: Wormhole.chainAddress(options.toToken.chain, options.walletPublicKey.toBase58()),
694
763
  source: Wormhole.tokenId(options.fromToken.chain, options.fromToken.mint.toBase58()),
695
764
  destination: Wormhole.tokenId(options.toToken.chain, options.toToken.mint.toBase58()),
696
765
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fogo/sessions-sdk",
3
- "version": "0.0.19",
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
  }