@fogo/sessions-sdk 0.0.20 → 0.0.22

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.
@@ -1,4 +1,4 @@
1
- import type { Transaction, Instruction, TransactionWithLifetime } from "@solana/kit";
1
+ import type { Transaction, Instruction, TransactionWithLifetime, Rpc, GetLatestBlockhashApi } from "@solana/kit";
2
2
  import type { TransactionError } from "@solana/web3.js";
3
3
  import { Keypair, Connection as Web3Connection, TransactionInstruction, VersionedTransaction, PublicKey } from "@solana/web3.js";
4
4
  export declare enum Network {
@@ -33,15 +33,18 @@ export declare const createSessionConnection: (options: {
33
33
  sendToPaymaster: (transaction: Transaction) => Promise<TransactionResult>;
34
34
  sponsor: PublicKey;
35
35
  })) => {
36
- rpc: import("@solana/kit").Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & import("@solana/kit").GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
36
+ rpc: Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
37
37
  connection: Web3Connection;
38
38
  network: Network;
39
39
  getSolanaConnection: () => Promise<Web3Connection>;
40
- sendToPaymaster: (domain: string, sponsor: PublicKey, sessionKey: CryptoKeyPair | undefined, instructions: (TransactionInstruction | Instruction)[] | VersionedTransaction | (Transaction & TransactionWithLifetime), extraConfig?: {
41
- addressLookupTable?: string | undefined;
42
- extraSigners?: (CryptoKeyPair | Keypair)[] | undefined;
43
- }) => Promise<TransactionResult>;
40
+ sendToPaymaster: (domain: string, sessionKey: CryptoKeyPair | undefined, instructions: TransactionOrInstructions, extraConfig?: SendTransactionOptions) => Promise<TransactionResult>;
44
41
  getSponsor: (domain: string) => Promise<PublicKey>;
45
42
  };
43
+ export type TransactionOrInstructions = (TransactionInstruction | Instruction)[] | VersionedTransaction | (Transaction & TransactionWithLifetime);
44
+ export type SendTransactionOptions = {
45
+ variation?: string | undefined;
46
+ addressLookupTable?: string | undefined;
47
+ extraSigners?: (CryptoKeyPair | Keypair)[] | undefined;
48
+ };
46
49
  export type Connection = ReturnType<typeof createSessionConnection>;
47
50
  export {};
package/cjs/connection.js CHANGED
@@ -39,17 +39,14 @@ const createSessionConnection = (options) => {
39
39
  const rpc = (0, kit_1.createSolanaRpc)(rpcUrl);
40
40
  const connection = new web3_js_1.Connection(rpcUrl, "confirmed");
41
41
  const addressLookupTableCache = new Map();
42
+ const sponsorCache = new Map();
42
43
  return {
43
44
  rpc,
44
45
  connection,
45
46
  network: options.network,
46
47
  getSolanaConnection: createSolanaConnectionGetter(options.network),
47
- sendToPaymaster: async (domain, sponsor, sessionKey, instructions, extraConfig) => {
48
- const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
49
- const transaction = await buildTransaction(connection, latestBlockhash, sessionKey, sponsor, instructions, addressLookupTableCache, extraConfig);
50
- return sendToPaymaster(options, domain, transaction);
51
- },
52
- getSponsor: (domain) => getSponsor(options, domain),
48
+ sendToPaymaster: async (domain, sessionKey, instructions, extraConfig) => sendToPaymaster({ ...options, rpc, connection, addressLookupTableCache, sponsorCache }, domain, sessionKey, instructions, extraConfig),
49
+ getSponsor: (domain) => getSponsor(options, sponsorCache, domain),
53
50
  };
54
51
  };
55
52
  exports.createSessionConnection = createSessionConnection;
@@ -75,10 +72,17 @@ const NETWORK_TO_QUERY_PARAM = {
75
72
  [Network.Mainnet]: "mainnet",
76
73
  [Network.Testnet]: "testnet",
77
74
  };
78
- const sendToPaymaster = async (options, domain, transaction) => {
79
- if (options.sendToPaymaster === undefined) {
80
- const url = new URL("/api/sponsor_and_send", options.paymaster ?? DEFAULT_PAYMASTER[options.network]);
75
+ const sendToPaymaster = async (connection, domain, sessionKey, instructions, extraConfig) => {
76
+ const signerKeys = await getSignerKeys(sessionKey, extraConfig?.extraSigners);
77
+ const transaction = Array.isArray(instructions)
78
+ ? await buildTransaction(connection, domain, signerKeys, instructions, extraConfig)
79
+ : await addSignaturesToExistingTransaction(instructions, signerKeys);
80
+ if (connection.sendToPaymaster === undefined) {
81
+ const url = new URL("/api/sponsor_and_send", connection.paymaster ?? DEFAULT_PAYMASTER[connection.network]);
81
82
  url.searchParams.set("domain", domain);
83
+ if (extraConfig?.variation !== undefined) {
84
+ url.searchParams.set("variation", extraConfig.variation);
85
+ }
82
86
  const response = await fetch(url, {
83
87
  method: "POST",
84
88
  headers: {
@@ -96,33 +100,31 @@ const sendToPaymaster = async (options, domain, transaction) => {
96
100
  }
97
101
  }
98
102
  else {
99
- return options.sendToPaymaster(transaction);
103
+ return connection.sendToPaymaster(transaction);
100
104
  }
101
105
  };
102
- const buildTransaction = async (connection, latestBlockhash, sessionKey, sponsor, instructions, addressLookupTableCache, extraConfig) => {
103
- const [signerKeys, addressLookupTable] = await Promise.all([
104
- getSignerKeys(sessionKey, extraConfig?.extraSigners),
106
+ const buildTransaction = async (connection, domain, signerKeys, instructions, extraConfig) => {
107
+ const [{ value: latestBlockhash }, sponsor, addressLookupTable, signers] = await Promise.all([
108
+ connection.rpc.getLatestBlockhash().send(),
109
+ connection.sponsor === undefined
110
+ ? getSponsor(connection, connection.sponsorCache, domain)
111
+ : Promise.resolve(connection.sponsor),
105
112
  extraConfig?.addressLookupTable === undefined
106
113
  ? Promise.resolve(undefined)
107
- : getAddressLookupTable(connection, addressLookupTableCache, extraConfig.addressLookupTable),
114
+ : getAddressLookupTable(connection.connection, connection.addressLookupTableCache, extraConfig.addressLookupTable),
115
+ Promise.all(signerKeys.map((signer) => (0, kit_1.createSignerFromKeyPair)(signer))),
108
116
  ]);
109
- if (Array.isArray(instructions)) {
110
- const signers = await Promise.all(signerKeys.map((signer) => (0, kit_1.createSignerFromKeyPair)(signer)));
111
- return (0, kit_1.partiallySignTransactionMessageWithSigners)((0, kit_1.pipe)((0, kit_1.createTransactionMessage)({ version: 0 }), (tx) => (0, kit_1.setTransactionMessageFeePayer)((0, compat_1.fromLegacyPublicKey)(sponsor), tx), (tx) => (0, kit_1.setTransactionMessageLifetimeUsingBlockhash)(latestBlockhash, tx), (tx) => (0, kit_1.appendTransactionMessageInstructions)(instructions.map((instruction) => instruction instanceof web3_js_1.TransactionInstruction
112
- ? (0, compat_1.fromLegacyTransactionInstruction)(instruction)
113
- : instruction), tx), (tx) => addressLookupTable === undefined
114
- ? tx
115
- : (0, kit_1.compressTransactionMessageUsingAddressLookupTables)(tx, {
116
- [(0, compat_1.fromLegacyPublicKey)(addressLookupTable.key)]: addressLookupTable.state.addresses.map((address) => (0, compat_1.fromLegacyPublicKey)(address)),
117
- }), (tx) => (0, kit_1.addSignersToTransactionMessage)(signers, tx)));
118
- }
119
- else {
120
- const tx = instructions instanceof web3_js_1.VersionedTransaction
121
- ? (0, compat_1.fromVersionedTransaction)(instructions) // VersionedTransaction has a lifetime so it's fine to cast it so we can call partiallySignTransaction
122
- : instructions;
123
- return (0, kit_1.partiallySignTransaction)(signerKeys, tx);
124
- }
117
+ return (0, kit_1.partiallySignTransactionMessageWithSigners)((0, kit_1.pipe)((0, kit_1.createTransactionMessage)({ version: 0 }), (tx) => (0, kit_1.setTransactionMessageFeePayer)((0, compat_1.fromLegacyPublicKey)(sponsor), tx), (tx) => (0, kit_1.setTransactionMessageLifetimeUsingBlockhash)(latestBlockhash, tx), (tx) => (0, kit_1.appendTransactionMessageInstructions)(instructions.map((instruction) => instruction instanceof web3_js_1.TransactionInstruction
118
+ ? (0, compat_1.fromLegacyTransactionInstruction)(instruction)
119
+ : instruction), tx), (tx) => addressLookupTable === undefined
120
+ ? tx
121
+ : (0, kit_1.compressTransactionMessageUsingAddressLookupTables)(tx, {
122
+ [(0, compat_1.fromLegacyPublicKey)(addressLookupTable.key)]: addressLookupTable.state.addresses.map((address) => (0, compat_1.fromLegacyPublicKey)(address)),
123
+ }), (tx) => (0, kit_1.addSignersToTransactionMessage)(signers, tx)));
125
124
  };
125
+ const addSignaturesToExistingTransaction = (transaction, signerKeys) => (0, kit_1.partiallySignTransaction)(signerKeys, transaction instanceof web3_js_1.VersionedTransaction
126
+ ? (0, compat_1.fromVersionedTransaction)(transaction) // VersionedTransaction has a lifetime so it's fine to cast it so we can call partiallySignTransaction
127
+ : transaction);
126
128
  const getSignerKeys = async (sessionKey, extraSigners) => {
127
129
  const extraSignerKeys = extraSigners === undefined
128
130
  ? []
@@ -151,20 +153,24 @@ const sponsorAndSendResponseSchema = zod_1.z
151
153
  ? TransactionResult.Success(data.signature)
152
154
  : TransactionResult.Failed(data.signature, data.error);
153
155
  });
154
- const getSponsor = async (options, domain) => {
155
- if (options.sponsor === undefined) {
156
+ const getSponsor = async (options, sponsorCache, domain) => {
157
+ const value = sponsorCache.get(domain);
158
+ if (value === undefined) {
156
159
  const url = new URL("/api/sponsor_pubkey", options.paymaster ?? DEFAULT_PAYMASTER[options.network]);
157
160
  url.searchParams.set("domain", domain);
161
+ url.searchParams.set("index", "autoassign");
158
162
  const response = await fetch(url);
159
163
  if (response.status === 200) {
160
- return new web3_js_1.PublicKey(zod_1.z.string().parse(await response.text()));
164
+ const sponsor = new web3_js_1.PublicKey(zod_1.z.string().parse(await response.text()));
165
+ sponsorCache.set(domain, sponsor);
166
+ return sponsor;
161
167
  }
162
168
  else {
163
169
  throw new PaymasterResponseError(response.status, await response.text());
164
170
  }
165
171
  }
166
172
  else {
167
- return options.sponsor;
173
+ return value;
168
174
  }
169
175
  };
170
176
  const getAddressLookupTable = async (connection, addressLookupTableCache, addressLookupTableAddress) => {
package/cjs/context.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Connection as Web3Connection } from "@solana/web3.js";
2
- import type { Connection } from "./connection.js";
2
+ import type { Connection, SendTransactionOptions as SendTransactionBaseOptions, TransactionOrInstructions } from "./connection.js";
3
+ export declare const SESSIONS_INTERNAL_PAYMASTER_DOMAIN = "sessions";
3
4
  export declare const createSessionContext: (options: {
4
5
  connection: Connection;
5
6
  defaultAddressLookupTableAddress?: string | undefined;
@@ -8,10 +9,14 @@ export declare const createSessionContext: (options: {
8
9
  chainId: string;
9
10
  domain: string;
10
11
  payer: import("@solana/web3.js").PublicKey;
12
+ internalPayer: import("@solana/web3.js").PublicKey;
11
13
  getSolanaConnection: () => Promise<Web3Connection>;
12
14
  connection: Web3Connection;
13
15
  rpc: import("@solana/kit").Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & import("@solana/kit").GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
14
16
  network: import("./connection.js").Network;
15
- sendTransaction: (sessionKey: CryptoKeyPair | undefined, instructions: Parameters<typeof options.connection.sendToPaymaster>[3], extraConfig?: Parameters<typeof options.connection.sendToPaymaster>[4]) => Promise<import("./connection.js").TransactionResult>;
17
+ sendTransaction: (sessionKey: CryptoKeyPair | undefined, instructions: TransactionOrInstructions, sendTxOptions?: SendTransactionOptions) => Promise<import("./connection.js").TransactionResult>;
16
18
  }>;
19
+ export type SendTransactionOptions = SendTransactionBaseOptions & {
20
+ paymasterDomain?: string | undefined;
21
+ };
17
22
  export type SessionContext = Awaited<ReturnType<typeof createSessionContext>>;
package/cjs/context.js CHANGED
@@ -1,26 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSessionContext = void 0;
3
+ exports.createSessionContext = exports.SESSIONS_INTERNAL_PAYMASTER_DOMAIN = void 0;
4
4
  const anchor_1 = require("@coral-xyz/anchor");
5
5
  const sessions_idls_1 = require("@fogo/sessions-idls");
6
6
  const web3_js_1 = require("@solana/web3.js");
7
7
  // eslint-disable-next-line unicorn/no-typeof-undefined
8
8
  const IS_BROWSER = typeof globalThis.window !== "undefined";
9
+ exports.SESSIONS_INTERNAL_PAYMASTER_DOMAIN = "sessions";
9
10
  const createSessionContext = async (options) => {
10
11
  const domain = getDomain(options.domain);
11
- const sponsor = await options.connection.getSponsor(domain);
12
+ const [sponsor, internalSponsor] = await Promise.all([
13
+ options.connection.getSponsor(domain),
14
+ options.connection.getSponsor(exports.SESSIONS_INTERNAL_PAYMASTER_DOMAIN),
15
+ ]);
12
16
  return {
13
17
  chainId: await fetchChainId(options.connection.connection),
14
18
  domain: getDomain(options.domain),
15
19
  payer: sponsor,
20
+ internalPayer: internalSponsor,
16
21
  getSolanaConnection: options.connection.getSolanaConnection,
17
22
  connection: options.connection.connection,
18
23
  rpc: options.connection.rpc,
19
24
  network: options.connection.network,
20
- sendTransaction: (sessionKey, instructions, extraConfig) => options.connection.sendToPaymaster(domain, sponsor, sessionKey, instructions, {
21
- addressLookupTable: extraConfig?.addressLookupTable ??
25
+ sendTransaction: (sessionKey, instructions, sendTxOptions) => options.connection.sendToPaymaster(sendTxOptions?.paymasterDomain ?? domain, sessionKey, instructions, {
26
+ ...sendTxOptions,
27
+ addressLookupTable: sendTxOptions?.addressLookupTable ??
22
28
  options.defaultAddressLookupTableAddress,
23
- extraSigners: extraConfig?.extraSigners,
24
29
  }),
25
30
  };
26
31
  };
package/cjs/index.d.ts CHANGED
@@ -4,10 +4,10 @@ import { Connection, PublicKey } from "@solana/web3.js";
4
4
  import type { Chain } from "@wormhole-foundation/sdk";
5
5
  import BN from "bn.js";
6
6
  import { z } from "zod";
7
- import type { TransactionResult } from "./connection.js";
8
- import type { SessionContext } from "./context.js";
9
- export { type SessionContext, createSessionContext } from "./context.js";
10
- export { type TransactionResult, type Connection, Network, TransactionResultType, createSessionConnection, } from "./connection.js";
7
+ import type { TransactionOrInstructions, TransactionResult } from "./connection.js";
8
+ import type { SendTransactionOptions, SessionContext } from "./context.js";
9
+ export { type SessionContext, type SendTransactionOptions, createSessionContext, } from "./context.js";
10
+ export { type TransactionResult, type Connection, type TransactionOrInstructions, Network, TransactionResultType, createSessionConnection, } from "./connection.js";
11
11
  type EstablishSessionOptions = {
12
12
  context: SessionContext;
13
13
  walletPublicKey: PublicKey;
@@ -1307,9 +1307,23 @@ export type Session = {
1307
1307
  sessionKey: CryptoKeyPair;
1308
1308
  walletPublicKey: PublicKey;
1309
1309
  payer: PublicKey;
1310
- sendTransaction: (instructions: Parameters<SessionContext["sendTransaction"]>[1]) => Promise<TransactionResult>;
1310
+ sendTransaction: (instructions: TransactionOrInstructions, extraConfig?: SendTransactionOptions) => 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");
@@ -24,9 +24,10 @@ const bn_js_1 = __importDefault(require("bn.js"));
24
24
  const bs58_1 = __importDefault(require("bs58"));
25
25
  const zod_1 = require("zod");
26
26
  const connection_js_1 = require("./connection.js");
27
+ const context_js_1 = require("./context.js");
27
28
  const crypto_js_1 = require("./crypto.js");
28
- var context_js_1 = require("./context.js");
29
- Object.defineProperty(exports, "createSessionContext", { enumerable: true, get: function () { return context_js_1.createSessionContext; } });
29
+ var context_js_2 = require("./context.js");
30
+ Object.defineProperty(exports, "createSessionContext", { enumerable: true, get: function () { return context_js_2.createSessionContext; } });
30
31
  var connection_js_2 = require("./connection.js");
31
32
  Object.defineProperty(exports, "Network", { enumerable: true, get: function () { return connection_js_2.Network; } });
32
33
  Object.defineProperty(exports, "TransactionResultType", { enumerable: true, get: function () { return connection_js_2.TransactionResultType; } });
@@ -39,9 +40,9 @@ const TOKENLESS_PERMISSIONS_VALUE = "this app may not spend any tokens";
39
40
  const CURRENT_MAJOR = "0";
40
41
  const CURRENT_MINOR = "3";
41
42
  const CURRENT_INTENT_TRANSFER_MAJOR = "0";
42
- const CURRENT_INTENT_TRANSFER_MINOR = "1";
43
+ const CURRENT_INTENT_TRANSFER_MINOR = "2";
43
44
  const CURRENT_BRIDGE_OUT_MAJOR = "0";
44
- const CURRENT_BRIDGE_OUT_MINOR = "1";
45
+ const CURRENT_BRIDGE_OUT_MINOR = "2";
45
46
  const SESSION_ESTABLISHMENT_LOOKUP_TABLE_ADDRESS = {
46
47
  [connection_js_1.Network.Testnet]: "B8cUjJMqaWWTNNSTXBmeptjWswwCH1gTSCRYv4nu7kJW",
47
48
  [connection_js_1.Network.Mainnet]: undefined,
@@ -65,16 +66,13 @@ const establishSession = async (options) => {
65
66
  buildIntentInstruction(options, sessionKey, tokenInfo),
66
67
  buildStartSessionInstruction(options, sessionKey, tokenInfo),
67
68
  ]);
68
- return sendSessionEstablishTransaction(options, sessionKey, [
69
- ...buildCreateAssociatedTokenAccountInstructions(options, tokenInfo),
70
- intentInstruction,
71
- startSessionInstruction,
72
- ], options.sessionEstablishmentLookupTable);
69
+ return sendSessionEstablishTransaction(options, sessionKey, [intentInstruction, startSessionInstruction], options.sessionEstablishmentLookupTable);
73
70
  }
74
71
  };
75
72
  exports.establishSession = establishSession;
76
73
  const sendSessionEstablishTransaction = async (options, sessionKey, instructions, sessionEstablishmentLookupTable) => {
77
74
  const result = await options.context.sendTransaction(sessionKey, instructions, {
75
+ variation: "Session Establishment",
78
76
  addressLookupTable: sessionEstablishmentLookupTable ??
79
77
  SESSION_ESTABLISHMENT_LOOKUP_TABLE_ADDRESS[options.context.network],
80
78
  });
@@ -104,9 +102,9 @@ const revokeSession = async (options) => {
104
102
  session: options.session.sessionPublicKey,
105
103
  })
106
104
  .instruction();
107
- return options.context.sendTransaction(options.session.sessionKey, [
108
- instruction,
109
- ]);
105
+ return options.context.sendTransaction(options.session.sessionKey, [instruction], {
106
+ variation: "Session Revocation",
107
+ });
110
108
  }
111
109
  else {
112
110
  return;
@@ -132,7 +130,7 @@ const createSession = async (context, walletPublicKey, sessionKey) => {
132
130
  walletPublicKey,
133
131
  sessionKey,
134
132
  payer: context.payer,
135
- sendTransaction: (instructions) => context.sendTransaction(sessionKey, instructions),
133
+ sendTransaction: (instructions, extraConfig) => context.sendTransaction(sessionKey, instructions, extraConfig),
136
134
  sessionInfo,
137
135
  };
138
136
  };
@@ -435,7 +433,6 @@ const amountToString = (amount, decimals) => {
435
433
  ...(decimalTruncated === "" ? [] : [".", decimalTruncated]),
436
434
  ].join("");
437
435
  };
438
- const buildCreateAssociatedTokenAccountInstructions = (options, tokens) => tokens.map(({ mint }) => (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(options.context.payer, (0, spl_token_1.getAssociatedTokenAddressSync)(mint, options.walletPublicKey), options.walletPublicKey, mint));
439
436
  const getDomainRecordAddress = (domain) => {
440
437
  const hash = (0, sha2_1.sha256)(domain);
441
438
  return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("domain-record"), hash], new web3_js_1.PublicKey(sessions_idls_1.DomainRegistryIdl.address))[0];
@@ -448,7 +445,7 @@ const BRIDGING_ADDRESS_LOOKUP_TABLE = {
448
445
  },
449
446
  [connection_js_1.Network.Mainnet]: {
450
447
  // USDC
451
- UsdcSt7U9H5bVy4WaWgeqoowe8RgXpLShCmxUFgZssx: "DjM31fhuQsjxLmpRFQpFUpZvyXzwQeNvyR1DUd8GMVmo",
448
+ uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG: "84k3mfNjmyinpZwyev7F15ChEW3Kqa3NoUkCJXXs4qkw",
452
449
  },
453
450
  };
454
451
  const buildStartSessionInstruction = async (options, sessionKey, tokens) => {
@@ -502,12 +499,52 @@ const EstablishSessionResult = {
502
499
  error,
503
500
  }),
504
501
  };
502
+ const USDC_MINT = {
503
+ [connection_js_1.Network.Mainnet]: "uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG",
504
+ [connection_js_1.Network.Testnet]: "ELNbJ1RtERV2fjtuZjbTscDekWhVzkQ1LjmiPsxp5uND",
505
+ };
506
+ const USDC_DECIMALS = 6;
507
+ const getTransferFee = async (context) => {
508
+ const { fee, ...config } = await getFee(context);
509
+ return {
510
+ ...config,
511
+ fee: fee.intrachainTransfer,
512
+ };
513
+ };
514
+ exports.getTransferFee = getTransferFee;
515
+ const getBridgeOutFee = async (context) => {
516
+ const { fee, ...config } = await getFee(context);
517
+ return {
518
+ ...config,
519
+ fee: fee.bridgeTransfer,
520
+ };
521
+ };
522
+ exports.getBridgeOutFee = getBridgeOutFee;
523
+ const getFee = async (context) => {
524
+ const program = new sessions_idls_1.IntentTransferProgram(new anchor_1.AnchorProvider(context.connection, {}, {}));
525
+ const umi = (0, umi_bundle_defaults_1.createUmi)(context.connection.rpcEndpoint);
526
+ const usdcMintAddress = USDC_MINT[context.network];
527
+ const usdcMint = new web3_js_1.PublicKey(usdcMintAddress);
528
+ const [feeConfigPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fee_config"), usdcMint.toBytes()], program.programId);
529
+ const feeConfig = await program.account.feeConfig.fetch(feeConfigPda);
530
+ return {
531
+ metadata: (0, mpl_token_metadata_1.findMetadataPda)(umi, {
532
+ mint: (0, umi_1.publicKey)(usdcMintAddress),
533
+ })[0],
534
+ mint: usdcMint,
535
+ symbolOrMint: "USDC.s",
536
+ decimals: USDC_DECIMALS,
537
+ fee: {
538
+ intrachainTransfer: BigInt(feeConfig.intrachainTransferFee.toString()),
539
+ bridgeTransfer: BigInt(feeConfig.bridgeTransferFee.toString()),
540
+ },
541
+ };
542
+ };
505
543
  const TRANSFER_MESSAGE_HEADER = `Fogo Transfer:
506
544
  Signing this intent will transfer the tokens as described below.
507
545
  `;
508
546
  const sendTransfer = async (options) => {
509
547
  const sourceAta = (0, spl_token_1.getAssociatedTokenAddressSync)(options.mint, options.walletPublicKey);
510
- const destinationAta = (0, spl_token_1.getAssociatedTokenAddressSync)(options.mint, options.recipient);
511
548
  const program = new sessions_idls_1.IntentTransferProgram(new anchor_1.AnchorProvider(options.context.connection, {}, {}));
512
549
  const umi = (0, umi_bundle_defaults_1.createUmi)(options.context.connection.rpcEndpoint);
513
550
  const metaplexMint = (0, umi_1.publicKey)(options.mint.toBase58());
@@ -515,23 +552,29 @@ const sendTransfer = async (options) => {
515
552
  const metadata = await (0, mpl_token_metadata_1.safeFetchMetadata)(umi, metadataAddress);
516
553
  const symbol = metadata?.symbol ?? undefined;
517
554
  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),
555
+ await buildTransferIntentInstruction(program, options, symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
520
556
  await program.methods
521
557
  .sendTokens()
522
558
  .accounts({
523
- destination: destinationAta,
559
+ destinationOwner: options.recipient,
560
+ feeMetadata: options.feeConfig.metadata,
561
+ feeMint: options.feeConfig.mint,
562
+ feeSource: (0, spl_token_1.getAssociatedTokenAddressSync)(options.feeConfig.mint, options.walletPublicKey),
524
563
  mint: options.mint,
525
564
  source: sourceAta,
526
- sponsor: options.context.payer,
565
+ sponsor: options.context.internalPayer,
566
+ metadata:
527
567
  // eslint-disable-next-line unicorn/no-null
528
- metadata: symbol === undefined ? null : new web3_js_1.PublicKey(metadataAddress),
568
+ symbol === undefined ? null : new web3_js_1.PublicKey(metadataAddress),
529
569
  })
530
570
  .instruction(),
531
- ]);
571
+ ], {
572
+ variation: "Intent Transfer",
573
+ paymasterDomain: context_js_1.SESSIONS_INTERNAL_PAYMASTER_DOMAIN,
574
+ });
532
575
  };
533
576
  exports.sendTransfer = sendTransfer;
534
- const buildTransferIntentInstruction = async (program, options, symbol) => {
577
+ const buildTransferIntentInstruction = async (program, options, symbol, feeToken, feeAmount) => {
535
578
  const [nonce, { decimals }] = await Promise.all([
536
579
  getNonce(program, options.walletPublicKey, NonceType.Transfer),
537
580
  (0, spl_token_1.getMint)(options.context.connection, options.mint),
@@ -544,6 +587,8 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
544
587
  token: symbol ?? options.mint.toBase58(),
545
588
  amount: amountToString(options.amount, decimals),
546
589
  recipient: options.recipient.toBase58(),
590
+ fee_token: feeToken,
591
+ fee_amount: feeAmount,
547
592
  nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
548
593
  }),
549
594
  ].join("\n"));
@@ -557,6 +602,7 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
557
602
  const BRIDGE_OUT_MESSAGE_HEADER = `Fogo Bridge Transfer:
558
603
  Signing this intent will bridge out the tokens as described below.
559
604
  `;
605
+ const BRIDGE_OUT_CUS = 220_000;
560
606
  const bridgeOut = async (options) => {
561
607
  const { wh, route, transferRequest, transferParams, decimals } = await buildWormholeTransfer(options, options.context.connection);
562
608
  // @ts-expect-error the wormhole client types are incorrect and do not
@@ -567,20 +613,20 @@ const bridgeOut = async (options) => {
567
613
  const metaplexMint = (0, umi_1.publicKey)(options.fromToken.mint.toBase58());
568
614
  const metadataAddress = (0, mpl_token_metadata_1.findMetadataPda)(umi, { mint: metaplexMint })[0];
569
615
  const outboxItem = web3_js_1.Keypair.generate();
570
- const [metadata, nttPdas] = await Promise.all([
616
+ const [metadata, nttPdas, destinationAtaExists] = await Promise.all([
571
617
  (0, mpl_token_metadata_1.safeFetchMetadata)(umi, metadataAddress),
572
618
  getNttPdas(options, wh, program, outboxItem.publicKey, new web3_js_1.PublicKey(quote.payeeAddress)),
619
+ getDestinationAtaExists(options.context, options.toToken.mint, options.walletPublicKey),
573
620
  ]);
574
- return options.context.sendTransaction(options.sessionKey, await Promise.all([
575
- buildBridgeOutIntent(program, options, decimals, metadata?.symbol),
621
+ const instructions = await Promise.all([
622
+ buildBridgeOutIntent(program, options, decimals, metadata?.symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
576
623
  program.methods
577
624
  .bridgeNttTokens({
578
- execAmount: new bn_js_1.default(quote.estimatedCost.toString()),
579
- relayInstructions: Buffer.from(quote.relayInstructions),
580
- signedQuoteBytes: Buffer.from(quote.signedQuote),
625
+ payDestinationAtaRent: !destinationAtaExists,
626
+ signedQuoteBytes: [...quote.signedQuote],
581
627
  })
582
628
  .accounts({
583
- sponsor: options.context.payer,
629
+ sponsor: options.context.internalPayer,
584
630
  mint: options.fromToken.mint,
585
631
  metadata: metadata?.symbol === undefined
586
632
  ? // eslint-disable-next-line unicorn/no-null
@@ -588,14 +634,28 @@ const bridgeOut = async (options) => {
588
634
  : new web3_js_1.PublicKey(metadataAddress),
589
635
  source: (0, spl_token_1.getAssociatedTokenAddressSync)(options.fromToken.mint, options.walletPublicKey),
590
636
  ntt: nttPdas,
637
+ feeMetadata: options.feeConfig.metadata,
638
+ feeMint: options.feeConfig.mint,
639
+ feeSource: (0, spl_token_1.getAssociatedTokenAddressSync)(options.feeConfig.mint, options.walletPublicKey),
591
640
  })
592
641
  .instruction(),
593
- ]), {
642
+ ]);
643
+ return options.context.sendTransaction(options.sessionKey, [
644
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: BRIDGE_OUT_CUS }),
645
+ ...instructions,
646
+ ], {
647
+ variation: "Intent NTT Bridge",
648
+ paymasterDomain: context_js_1.SESSIONS_INTERNAL_PAYMASTER_DOMAIN,
594
649
  extraSigners: [outboxItem],
595
650
  addressLookupTable: BRIDGING_ADDRESS_LOOKUP_TABLE[options.context.network]?.[options.fromToken.mint.toBase58()],
596
651
  });
597
652
  };
598
653
  exports.bridgeOut = bridgeOut;
654
+ const getDestinationAtaExists = async (context, token, wallet) => {
655
+ const solanaConnection = await context.getSolanaConnection();
656
+ const ataAccount = await solanaConnection.getAccountInfo((0, spl_token_1.getAssociatedTokenAddressSync)(token, wallet));
657
+ return ataAccount !== null;
658
+ };
599
659
  // Here we use the Wormhole SDKs to produce the wormhole pdas that are needed
600
660
  // for the bridge out transaction. Currently this is using wormhole SDK apis
601
661
  // that are _technically_ public but it seems likely these are not considered to
@@ -613,6 +673,10 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
613
673
  const transceiverPdas = sdk_solana_ntt_1.NTT.transceiverPdas(options.fromToken.manager);
614
674
  const [intentTransferSetterPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("intent_transfer")], program.programId);
615
675
  const wormholePdas = sdk_solana_core_1.utils.getWormholeDerivedAccounts(options.fromToken.manager, coreBridgeContract);
676
+ const [registeredTransceiverPda] = web3_js_1.PublicKey.findProgramAddressSync([
677
+ Buffer.from("registered_transceiver"),
678
+ options.fromToken.manager.toBytes(),
679
+ ], options.fromToken.manager);
616
680
  return {
617
681
  emitter: transceiverPdas.emitterAccount(),
618
682
  nttConfig: pdas.configAccount(),
@@ -625,7 +689,7 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
625
689
  nttSessionAuthority: pdas.sessionAuthority(intentTransferSetterPda, sdk_solana_ntt_1.NTT.transferArgs(options.amount, sdk_1.Wormhole.chainAddress("Solana", options.walletPublicKey.toBase58()), false)),
626
690
  nttTokenAuthority: pdas.tokenAuthority(),
627
691
  payeeNttWithExecutor: quotePayeeAddress,
628
- transceiver: options.fromToken.transceiver,
692
+ transceiver: registeredTransceiverPda,
629
693
  wormholeProgram: coreBridgeContract,
630
694
  wormholeBridge: wormholePdas.wormholeBridge,
631
695
  wormholeFeeCollector: wormholePdas.wormholeFeeCollector,
@@ -633,7 +697,7 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
633
697
  wormholeSequence: wormholePdas.wormholeSequence,
634
698
  };
635
699
  };
636
- const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
700
+ const buildBridgeOutIntent = async (program, options, decimals, symbol, feeToken, feeAmount) => {
637
701
  const nonce = await getNonce(program, options.walletPublicKey, NonceType.Bridge);
638
702
  const message = new TextEncoder().encode([
639
703
  BRIDGE_OUT_MESSAGE_HEADER,
@@ -644,6 +708,8 @@ const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
644
708
  token: symbol ?? options.fromToken.mint.toBase58(),
645
709
  amount: amountToString(options.amount, decimals),
646
710
  recipient_address: options.walletPublicKey.toBase58(),
711
+ fee_token: feeToken,
712
+ fee_amount: feeAmount,
647
713
  nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
648
714
  }),
649
715
  ].join("\n"));
@@ -1,4 +1,4 @@
1
- import type { Transaction, Instruction, TransactionWithLifetime } from "@solana/kit";
1
+ import type { Transaction, Instruction, TransactionWithLifetime, Rpc, GetLatestBlockhashApi } from "@solana/kit";
2
2
  import type { TransactionError } from "@solana/web3.js";
3
3
  import { Keypair, Connection as Web3Connection, TransactionInstruction, VersionedTransaction, PublicKey } from "@solana/web3.js";
4
4
  export declare enum Network {
@@ -33,15 +33,18 @@ export declare const createSessionConnection: (options: {
33
33
  sendToPaymaster: (transaction: Transaction) => Promise<TransactionResult>;
34
34
  sponsor: PublicKey;
35
35
  })) => {
36
- rpc: import("@solana/kit").Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & import("@solana/kit").GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
36
+ rpc: Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
37
37
  connection: Web3Connection;
38
38
  network: Network;
39
39
  getSolanaConnection: () => Promise<Web3Connection>;
40
- sendToPaymaster: (domain: string, sponsor: PublicKey, sessionKey: CryptoKeyPair | undefined, instructions: (TransactionInstruction | Instruction)[] | VersionedTransaction | (Transaction & TransactionWithLifetime), extraConfig?: {
41
- addressLookupTable?: string | undefined;
42
- extraSigners?: (CryptoKeyPair | Keypair)[] | undefined;
43
- }) => Promise<TransactionResult>;
40
+ sendToPaymaster: (domain: string, sessionKey: CryptoKeyPair | undefined, instructions: TransactionOrInstructions, extraConfig?: SendTransactionOptions) => Promise<TransactionResult>;
44
41
  getSponsor: (domain: string) => Promise<PublicKey>;
45
42
  };
43
+ export type TransactionOrInstructions = (TransactionInstruction | Instruction)[] | VersionedTransaction | (Transaction & TransactionWithLifetime);
44
+ export type SendTransactionOptions = {
45
+ variation?: string | undefined;
46
+ addressLookupTable?: string | undefined;
47
+ extraSigners?: (CryptoKeyPair | Keypair)[] | undefined;
48
+ };
46
49
  export type Connection = ReturnType<typeof createSessionConnection>;
47
50
  export {};
package/esm/connection.js CHANGED
@@ -36,17 +36,14 @@ export const createSessionConnection = (options) => {
36
36
  const rpc = createSolanaRpc(rpcUrl);
37
37
  const connection = new Web3Connection(rpcUrl, "confirmed");
38
38
  const addressLookupTableCache = new Map();
39
+ const sponsorCache = new Map();
39
40
  return {
40
41
  rpc,
41
42
  connection,
42
43
  network: options.network,
43
44
  getSolanaConnection: createSolanaConnectionGetter(options.network),
44
- sendToPaymaster: async (domain, sponsor, sessionKey, instructions, extraConfig) => {
45
- const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
46
- const transaction = await buildTransaction(connection, latestBlockhash, sessionKey, sponsor, instructions, addressLookupTableCache, extraConfig);
47
- return sendToPaymaster(options, domain, transaction);
48
- },
49
- getSponsor: (domain) => getSponsor(options, domain),
45
+ sendToPaymaster: async (domain, sessionKey, instructions, extraConfig) => sendToPaymaster({ ...options, rpc, connection, addressLookupTableCache, sponsorCache }, domain, sessionKey, instructions, extraConfig),
46
+ getSponsor: (domain) => getSponsor(options, sponsorCache, domain),
50
47
  };
51
48
  };
52
49
  const createSolanaConnectionGetter = (network) => {
@@ -71,10 +68,17 @@ const NETWORK_TO_QUERY_PARAM = {
71
68
  [Network.Mainnet]: "mainnet",
72
69
  [Network.Testnet]: "testnet",
73
70
  };
74
- const sendToPaymaster = async (options, domain, transaction) => {
75
- if (options.sendToPaymaster === undefined) {
76
- const url = new URL("/api/sponsor_and_send", options.paymaster ?? DEFAULT_PAYMASTER[options.network]);
71
+ const sendToPaymaster = async (connection, domain, sessionKey, instructions, extraConfig) => {
72
+ const signerKeys = await getSignerKeys(sessionKey, extraConfig?.extraSigners);
73
+ const transaction = Array.isArray(instructions)
74
+ ? await buildTransaction(connection, domain, signerKeys, instructions, extraConfig)
75
+ : await addSignaturesToExistingTransaction(instructions, signerKeys);
76
+ if (connection.sendToPaymaster === undefined) {
77
+ const url = new URL("/api/sponsor_and_send", connection.paymaster ?? DEFAULT_PAYMASTER[connection.network]);
77
78
  url.searchParams.set("domain", domain);
79
+ if (extraConfig?.variation !== undefined) {
80
+ url.searchParams.set("variation", extraConfig.variation);
81
+ }
78
82
  const response = await fetch(url, {
79
83
  method: "POST",
80
84
  headers: {
@@ -92,33 +96,31 @@ const sendToPaymaster = async (options, domain, transaction) => {
92
96
  }
93
97
  }
94
98
  else {
95
- return options.sendToPaymaster(transaction);
99
+ return connection.sendToPaymaster(transaction);
96
100
  }
97
101
  };
98
- const buildTransaction = async (connection, latestBlockhash, sessionKey, sponsor, instructions, addressLookupTableCache, extraConfig) => {
99
- const [signerKeys, addressLookupTable] = await Promise.all([
100
- getSignerKeys(sessionKey, extraConfig?.extraSigners),
102
+ const buildTransaction = async (connection, domain, signerKeys, instructions, extraConfig) => {
103
+ const [{ value: latestBlockhash }, sponsor, addressLookupTable, signers] = await Promise.all([
104
+ connection.rpc.getLatestBlockhash().send(),
105
+ connection.sponsor === undefined
106
+ ? getSponsor(connection, connection.sponsorCache, domain)
107
+ : Promise.resolve(connection.sponsor),
101
108
  extraConfig?.addressLookupTable === undefined
102
109
  ? Promise.resolve(undefined)
103
- : getAddressLookupTable(connection, addressLookupTableCache, extraConfig.addressLookupTable),
110
+ : getAddressLookupTable(connection.connection, connection.addressLookupTableCache, extraConfig.addressLookupTable),
111
+ Promise.all(signerKeys.map((signer) => createSignerFromKeyPair(signer))),
104
112
  ]);
105
- if (Array.isArray(instructions)) {
106
- const signers = await Promise.all(signerKeys.map((signer) => createSignerFromKeyPair(signer)));
107
- return partiallySignTransactionMessageWithSigners(pipe(createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(fromLegacyPublicKey(sponsor), tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions.map((instruction) => instruction instanceof TransactionInstruction
108
- ? fromLegacyTransactionInstruction(instruction)
109
- : instruction), tx), (tx) => addressLookupTable === undefined
110
- ? tx
111
- : compressTransactionMessageUsingAddressLookupTables(tx, {
112
- [fromLegacyPublicKey(addressLookupTable.key)]: addressLookupTable.state.addresses.map((address) => fromLegacyPublicKey(address)),
113
- }), (tx) => addSignersToTransactionMessage(signers, tx)));
114
- }
115
- else {
116
- const tx = instructions instanceof VersionedTransaction
117
- ? fromVersionedTransaction(instructions) // VersionedTransaction has a lifetime so it's fine to cast it so we can call partiallySignTransaction
118
- : instructions;
119
- return partiallySignTransaction(signerKeys, tx);
120
- }
113
+ return partiallySignTransactionMessageWithSigners(pipe(createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(fromLegacyPublicKey(sponsor), tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions.map((instruction) => instruction instanceof TransactionInstruction
114
+ ? fromLegacyTransactionInstruction(instruction)
115
+ : instruction), tx), (tx) => addressLookupTable === undefined
116
+ ? tx
117
+ : compressTransactionMessageUsingAddressLookupTables(tx, {
118
+ [fromLegacyPublicKey(addressLookupTable.key)]: addressLookupTable.state.addresses.map((address) => fromLegacyPublicKey(address)),
119
+ }), (tx) => addSignersToTransactionMessage(signers, tx)));
121
120
  };
121
+ const addSignaturesToExistingTransaction = (transaction, signerKeys) => partiallySignTransaction(signerKeys, transaction instanceof VersionedTransaction
122
+ ? fromVersionedTransaction(transaction) // VersionedTransaction has a lifetime so it's fine to cast it so we can call partiallySignTransaction
123
+ : transaction);
122
124
  const getSignerKeys = async (sessionKey, extraSigners) => {
123
125
  const extraSignerKeys = extraSigners === undefined
124
126
  ? []
@@ -147,20 +149,24 @@ const sponsorAndSendResponseSchema = z
147
149
  ? TransactionResult.Success(data.signature)
148
150
  : TransactionResult.Failed(data.signature, data.error);
149
151
  });
150
- const getSponsor = async (options, domain) => {
151
- if (options.sponsor === undefined) {
152
+ const getSponsor = async (options, sponsorCache, domain) => {
153
+ const value = sponsorCache.get(domain);
154
+ if (value === undefined) {
152
155
  const url = new URL("/api/sponsor_pubkey", options.paymaster ?? DEFAULT_PAYMASTER[options.network]);
153
156
  url.searchParams.set("domain", domain);
157
+ url.searchParams.set("index", "autoassign");
154
158
  const response = await fetch(url);
155
159
  if (response.status === 200) {
156
- return new PublicKey(z.string().parse(await response.text()));
160
+ const sponsor = new PublicKey(z.string().parse(await response.text()));
161
+ sponsorCache.set(domain, sponsor);
162
+ return sponsor;
157
163
  }
158
164
  else {
159
165
  throw new PaymasterResponseError(response.status, await response.text());
160
166
  }
161
167
  }
162
168
  else {
163
- return options.sponsor;
169
+ return value;
164
170
  }
165
171
  };
166
172
  const getAddressLookupTable = async (connection, addressLookupTableCache, addressLookupTableAddress) => {
package/esm/context.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Connection as Web3Connection } from "@solana/web3.js";
2
- import type { Connection } from "./connection.js";
2
+ import type { Connection, SendTransactionOptions as SendTransactionBaseOptions, TransactionOrInstructions } from "./connection.js";
3
+ export declare const SESSIONS_INTERNAL_PAYMASTER_DOMAIN = "sessions";
3
4
  export declare const createSessionContext: (options: {
4
5
  connection: Connection;
5
6
  defaultAddressLookupTableAddress?: string | undefined;
@@ -8,10 +9,14 @@ export declare const createSessionContext: (options: {
8
9
  chainId: string;
9
10
  domain: string;
10
11
  payer: import("@solana/web3.js").PublicKey;
12
+ internalPayer: import("@solana/web3.js").PublicKey;
11
13
  getSolanaConnection: () => Promise<Web3Connection>;
12
14
  connection: Web3Connection;
13
15
  rpc: import("@solana/kit").Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & import("@solana/kit").GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
14
16
  network: import("./connection.js").Network;
15
- sendTransaction: (sessionKey: CryptoKeyPair | undefined, instructions: Parameters<typeof options.connection.sendToPaymaster>[3], extraConfig?: Parameters<typeof options.connection.sendToPaymaster>[4]) => Promise<import("./connection.js").TransactionResult>;
17
+ sendTransaction: (sessionKey: CryptoKeyPair | undefined, instructions: TransactionOrInstructions, sendTxOptions?: SendTransactionOptions) => Promise<import("./connection.js").TransactionResult>;
16
18
  }>;
19
+ export type SendTransactionOptions = SendTransactionBaseOptions & {
20
+ paymasterDomain?: string | undefined;
21
+ };
17
22
  export type SessionContext = Awaited<ReturnType<typeof createSessionContext>>;
package/esm/context.js CHANGED
@@ -3,21 +3,26 @@ import { ChainIdProgram } from "@fogo/sessions-idls";
3
3
  import { Connection as Web3Connection, Keypair } from "@solana/web3.js";
4
4
  // eslint-disable-next-line unicorn/no-typeof-undefined
5
5
  const IS_BROWSER = typeof globalThis.window !== "undefined";
6
+ export const SESSIONS_INTERNAL_PAYMASTER_DOMAIN = "sessions";
6
7
  export const createSessionContext = async (options) => {
7
8
  const domain = getDomain(options.domain);
8
- const sponsor = await options.connection.getSponsor(domain);
9
+ const [sponsor, internalSponsor] = await Promise.all([
10
+ options.connection.getSponsor(domain),
11
+ options.connection.getSponsor(SESSIONS_INTERNAL_PAYMASTER_DOMAIN),
12
+ ]);
9
13
  return {
10
14
  chainId: await fetchChainId(options.connection.connection),
11
15
  domain: getDomain(options.domain),
12
16
  payer: sponsor,
17
+ internalPayer: internalSponsor,
13
18
  getSolanaConnection: options.connection.getSolanaConnection,
14
19
  connection: options.connection.connection,
15
20
  rpc: options.connection.rpc,
16
21
  network: options.connection.network,
17
- sendTransaction: (sessionKey, instructions, extraConfig) => options.connection.sendToPaymaster(domain, sponsor, sessionKey, instructions, {
18
- addressLookupTable: extraConfig?.addressLookupTable ??
22
+ sendTransaction: (sessionKey, instructions, sendTxOptions) => options.connection.sendToPaymaster(sendTxOptions?.paymasterDomain ?? domain, sessionKey, instructions, {
23
+ ...sendTxOptions,
24
+ addressLookupTable: sendTxOptions?.addressLookupTable ??
19
25
  options.defaultAddressLookupTableAddress,
20
- extraSigners: extraConfig?.extraSigners,
21
26
  }),
22
27
  };
23
28
  };
package/esm/index.d.ts CHANGED
@@ -4,10 +4,10 @@ import { Connection, PublicKey } from "@solana/web3.js";
4
4
  import type { Chain } from "@wormhole-foundation/sdk";
5
5
  import BN from "bn.js";
6
6
  import { z } from "zod";
7
- import type { TransactionResult } from "./connection.js";
8
- import type { SessionContext } from "./context.js";
9
- export { type SessionContext, createSessionContext } from "./context.js";
10
- export { type TransactionResult, type Connection, Network, TransactionResultType, createSessionConnection, } from "./connection.js";
7
+ import type { TransactionOrInstructions, TransactionResult } from "./connection.js";
8
+ import type { SendTransactionOptions, SessionContext } from "./context.js";
9
+ export { type SessionContext, type SendTransactionOptions, createSessionContext, } from "./context.js";
10
+ export { type TransactionResult, type Connection, type TransactionOrInstructions, Network, TransactionResultType, createSessionConnection, } from "./connection.js";
11
11
  type EstablishSessionOptions = {
12
12
  context: SessionContext;
13
13
  walletPublicKey: PublicKey;
@@ -1307,9 +1307,23 @@ export type Session = {
1307
1307
  sessionKey: CryptoKeyPair;
1308
1308
  walletPublicKey: PublicKey;
1309
1309
  payer: PublicKey;
1310
- sendTransaction: (instructions: Parameters<SessionContext["sendTransaction"]>[1]) => Promise<TransactionResult>;
1310
+ sendTransaction: (instructions: TransactionOrInstructions, extraConfig?: SendTransactionOptions) => 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
@@ -6,8 +6,8 @@ import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
6
6
  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
- import { createAssociatedTokenAccountIdempotentInstruction, getAssociatedTokenAddressSync, getMint, } from "@solana/spl-token";
10
- import { Connection, Ed25519Program, Keypair, PublicKey, } from "@solana/web3.js";
9
+ import { getAssociatedTokenAddressSync, getMint } from "@solana/spl-token";
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";
@@ -18,8 +18,9 @@ import BN from "bn.js";
18
18
  import bs58 from "bs58";
19
19
  import { z } from "zod";
20
20
  import { Network, TransactionResultType } from "./connection.js";
21
+ import { SESSIONS_INTERNAL_PAYMASTER_DOMAIN } from "./context.js";
21
22
  import { importKey, signMessageWithKey, verifyMessageWithKey, } from "./crypto.js";
22
- export { createSessionContext } from "./context.js";
23
+ export { createSessionContext, } from "./context.js";
23
24
  export { Network, TransactionResultType, createSessionConnection, } from "./connection.js";
24
25
  const MESSAGE_HEADER = `Fogo Sessions:
25
26
  Signing this intent will allow this app to interact with your on-chain balances. Please make sure you trust this app and the domain in the message matches the domain of the current web application.
@@ -29,9 +30,9 @@ const TOKENLESS_PERMISSIONS_VALUE = "this app may not spend any tokens";
29
30
  const CURRENT_MAJOR = "0";
30
31
  const CURRENT_MINOR = "3";
31
32
  const CURRENT_INTENT_TRANSFER_MAJOR = "0";
32
- const CURRENT_INTENT_TRANSFER_MINOR = "1";
33
+ const CURRENT_INTENT_TRANSFER_MINOR = "2";
33
34
  const CURRENT_BRIDGE_OUT_MAJOR = "0";
34
- const CURRENT_BRIDGE_OUT_MINOR = "1";
35
+ const CURRENT_BRIDGE_OUT_MINOR = "2";
35
36
  const SESSION_ESTABLISHMENT_LOOKUP_TABLE_ADDRESS = {
36
37
  [Network.Testnet]: "B8cUjJMqaWWTNNSTXBmeptjWswwCH1gTSCRYv4nu7kJW",
37
38
  [Network.Mainnet]: undefined,
@@ -55,15 +56,12 @@ export const establishSession = async (options) => {
55
56
  buildIntentInstruction(options, sessionKey, tokenInfo),
56
57
  buildStartSessionInstruction(options, sessionKey, tokenInfo),
57
58
  ]);
58
- return sendSessionEstablishTransaction(options, sessionKey, [
59
- ...buildCreateAssociatedTokenAccountInstructions(options, tokenInfo),
60
- intentInstruction,
61
- startSessionInstruction,
62
- ], options.sessionEstablishmentLookupTable);
59
+ return sendSessionEstablishTransaction(options, sessionKey, [intentInstruction, startSessionInstruction], options.sessionEstablishmentLookupTable);
63
60
  }
64
61
  };
65
62
  const sendSessionEstablishTransaction = async (options, sessionKey, instructions, sessionEstablishmentLookupTable) => {
66
63
  const result = await options.context.sendTransaction(sessionKey, instructions, {
64
+ variation: "Session Establishment",
67
65
  addressLookupTable: sessionEstablishmentLookupTable ??
68
66
  SESSION_ESTABLISHMENT_LOOKUP_TABLE_ADDRESS[options.context.network],
69
67
  });
@@ -92,9 +90,9 @@ export const revokeSession = async (options) => {
92
90
  session: options.session.sessionPublicKey,
93
91
  })
94
92
  .instruction();
95
- return options.context.sendTransaction(options.session.sessionKey, [
96
- instruction,
97
- ]);
93
+ return options.context.sendTransaction(options.session.sessionKey, [instruction], {
94
+ variation: "Session Revocation",
95
+ });
98
96
  }
99
97
  else {
100
98
  return;
@@ -117,7 +115,7 @@ const createSession = async (context, walletPublicKey, sessionKey) => {
117
115
  walletPublicKey,
118
116
  sessionKey,
119
117
  payer: context.payer,
120
- sendTransaction: (instructions) => context.sendTransaction(sessionKey, instructions),
118
+ sendTransaction: (instructions, extraConfig) => context.sendTransaction(sessionKey, instructions, extraConfig),
121
119
  sessionInfo,
122
120
  };
123
121
  };
@@ -420,7 +418,6 @@ const amountToString = (amount, decimals) => {
420
418
  ...(decimalTruncated === "" ? [] : [".", decimalTruncated]),
421
419
  ].join("");
422
420
  };
423
- const buildCreateAssociatedTokenAccountInstructions = (options, tokens) => tokens.map(({ mint }) => createAssociatedTokenAccountIdempotentInstruction(options.context.payer, getAssociatedTokenAddressSync(mint, options.walletPublicKey), options.walletPublicKey, mint));
424
421
  export const getDomainRecordAddress = (domain) => {
425
422
  const hash = sha256(domain);
426
423
  return PublicKey.findProgramAddressSync([Buffer.from("domain-record"), hash], new PublicKey(DomainRegistryIdl.address))[0];
@@ -432,7 +429,7 @@ const BRIDGING_ADDRESS_LOOKUP_TABLE = {
432
429
  },
433
430
  [Network.Mainnet]: {
434
431
  // USDC
435
- UsdcSt7U9H5bVy4WaWgeqoowe8RgXpLShCmxUFgZssx: "DjM31fhuQsjxLmpRFQpFUpZvyXzwQeNvyR1DUd8GMVmo",
432
+ uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG: "84k3mfNjmyinpZwyev7F15ChEW3Kqa3NoUkCJXXs4qkw",
436
433
  },
437
434
  };
438
435
  const buildStartSessionInstruction = async (options, sessionKey, tokens) => {
@@ -486,12 +483,50 @@ const EstablishSessionResult = {
486
483
  error,
487
484
  }),
488
485
  };
486
+ const USDC_MINT = {
487
+ [Network.Mainnet]: "uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG",
488
+ [Network.Testnet]: "ELNbJ1RtERV2fjtuZjbTscDekWhVzkQ1LjmiPsxp5uND",
489
+ };
490
+ const USDC_DECIMALS = 6;
491
+ export const getTransferFee = async (context) => {
492
+ const { fee, ...config } = await getFee(context);
493
+ return {
494
+ ...config,
495
+ fee: fee.intrachainTransfer,
496
+ };
497
+ };
498
+ export const getBridgeOutFee = async (context) => {
499
+ const { fee, ...config } = await getFee(context);
500
+ return {
501
+ ...config,
502
+ fee: fee.bridgeTransfer,
503
+ };
504
+ };
505
+ const getFee = async (context) => {
506
+ const program = new IntentTransferProgram(new AnchorProvider(context.connection, {}, {}));
507
+ const umi = createUmi(context.connection.rpcEndpoint);
508
+ const usdcMintAddress = USDC_MINT[context.network];
509
+ const usdcMint = new PublicKey(usdcMintAddress);
510
+ const [feeConfigPda] = PublicKey.findProgramAddressSync([Buffer.from("fee_config"), usdcMint.toBytes()], program.programId);
511
+ const feeConfig = await program.account.feeConfig.fetch(feeConfigPda);
512
+ return {
513
+ metadata: findMetadataPda(umi, {
514
+ mint: metaplexPublicKey(usdcMintAddress),
515
+ })[0],
516
+ mint: usdcMint,
517
+ symbolOrMint: "USDC.s",
518
+ decimals: USDC_DECIMALS,
519
+ fee: {
520
+ intrachainTransfer: BigInt(feeConfig.intrachainTransferFee.toString()),
521
+ bridgeTransfer: BigInt(feeConfig.bridgeTransferFee.toString()),
522
+ },
523
+ };
524
+ };
489
525
  const TRANSFER_MESSAGE_HEADER = `Fogo Transfer:
490
526
  Signing this intent will transfer the tokens as described below.
491
527
  `;
492
528
  export const sendTransfer = async (options) => {
493
529
  const sourceAta = getAssociatedTokenAddressSync(options.mint, options.walletPublicKey);
494
- const destinationAta = getAssociatedTokenAddressSync(options.mint, options.recipient);
495
530
  const program = new IntentTransferProgram(new AnchorProvider(options.context.connection, {}, {}));
496
531
  const umi = createUmi(options.context.connection.rpcEndpoint);
497
532
  const metaplexMint = metaplexPublicKey(options.mint.toBase58());
@@ -499,22 +534,28 @@ export const sendTransfer = async (options) => {
499
534
  const metadata = await safeFetchMetadata(umi, metadataAddress);
500
535
  const symbol = metadata?.symbol ?? undefined;
501
536
  return options.context.sendTransaction(undefined, [
502
- createAssociatedTokenAccountIdempotentInstruction(options.context.payer, destinationAta, options.recipient, options.mint),
503
- await buildTransferIntentInstruction(program, options, symbol),
537
+ await buildTransferIntentInstruction(program, options, symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
504
538
  await program.methods
505
539
  .sendTokens()
506
540
  .accounts({
507
- destination: destinationAta,
541
+ destinationOwner: options.recipient,
542
+ feeMetadata: options.feeConfig.metadata,
543
+ feeMint: options.feeConfig.mint,
544
+ feeSource: getAssociatedTokenAddressSync(options.feeConfig.mint, options.walletPublicKey),
508
545
  mint: options.mint,
509
546
  source: sourceAta,
510
- sponsor: options.context.payer,
547
+ sponsor: options.context.internalPayer,
548
+ metadata:
511
549
  // eslint-disable-next-line unicorn/no-null
512
- metadata: symbol === undefined ? null : new PublicKey(metadataAddress),
550
+ symbol === undefined ? null : new PublicKey(metadataAddress),
513
551
  })
514
552
  .instruction(),
515
- ]);
553
+ ], {
554
+ variation: "Intent Transfer",
555
+ paymasterDomain: SESSIONS_INTERNAL_PAYMASTER_DOMAIN,
556
+ });
516
557
  };
517
- const buildTransferIntentInstruction = async (program, options, symbol) => {
558
+ const buildTransferIntentInstruction = async (program, options, symbol, feeToken, feeAmount) => {
518
559
  const [nonce, { decimals }] = await Promise.all([
519
560
  getNonce(program, options.walletPublicKey, NonceType.Transfer),
520
561
  getMint(options.context.connection, options.mint),
@@ -527,6 +568,8 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
527
568
  token: symbol ?? options.mint.toBase58(),
528
569
  amount: amountToString(options.amount, decimals),
529
570
  recipient: options.recipient.toBase58(),
571
+ fee_token: feeToken,
572
+ fee_amount: feeAmount,
530
573
  nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
531
574
  }),
532
575
  ].join("\n"));
@@ -540,6 +583,7 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
540
583
  const BRIDGE_OUT_MESSAGE_HEADER = `Fogo Bridge Transfer:
541
584
  Signing this intent will bridge out the tokens as described below.
542
585
  `;
586
+ const BRIDGE_OUT_CUS = 220_000;
543
587
  export const bridgeOut = async (options) => {
544
588
  const { wh, route, transferRequest, transferParams, decimals } = await buildWormholeTransfer(options, options.context.connection);
545
589
  // @ts-expect-error the wormhole client types are incorrect and do not
@@ -550,20 +594,20 @@ export const bridgeOut = async (options) => {
550
594
  const metaplexMint = metaplexPublicKey(options.fromToken.mint.toBase58());
551
595
  const metadataAddress = findMetadataPda(umi, { mint: metaplexMint })[0];
552
596
  const outboxItem = Keypair.generate();
553
- const [metadata, nttPdas] = await Promise.all([
597
+ const [metadata, nttPdas, destinationAtaExists] = await Promise.all([
554
598
  safeFetchMetadata(umi, metadataAddress),
555
599
  getNttPdas(options, wh, program, outboxItem.publicKey, new PublicKey(quote.payeeAddress)),
600
+ getDestinationAtaExists(options.context, options.toToken.mint, options.walletPublicKey),
556
601
  ]);
557
- return options.context.sendTransaction(options.sessionKey, await Promise.all([
558
- buildBridgeOutIntent(program, options, decimals, metadata?.symbol),
602
+ const instructions = await Promise.all([
603
+ buildBridgeOutIntent(program, options, decimals, metadata?.symbol, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
559
604
  program.methods
560
605
  .bridgeNttTokens({
561
- execAmount: new BN(quote.estimatedCost.toString()),
562
- relayInstructions: Buffer.from(quote.relayInstructions),
563
- signedQuoteBytes: Buffer.from(quote.signedQuote),
606
+ payDestinationAtaRent: !destinationAtaExists,
607
+ signedQuoteBytes: [...quote.signedQuote],
564
608
  })
565
609
  .accounts({
566
- sponsor: options.context.payer,
610
+ sponsor: options.context.internalPayer,
567
611
  mint: options.fromToken.mint,
568
612
  metadata: metadata?.symbol === undefined
569
613
  ? // eslint-disable-next-line unicorn/no-null
@@ -571,13 +615,27 @@ export const bridgeOut = async (options) => {
571
615
  : new PublicKey(metadataAddress),
572
616
  source: getAssociatedTokenAddressSync(options.fromToken.mint, options.walletPublicKey),
573
617
  ntt: nttPdas,
618
+ feeMetadata: options.feeConfig.metadata,
619
+ feeMint: options.feeConfig.mint,
620
+ feeSource: getAssociatedTokenAddressSync(options.feeConfig.mint, options.walletPublicKey),
574
621
  })
575
622
  .instruction(),
576
- ]), {
623
+ ]);
624
+ return options.context.sendTransaction(options.sessionKey, [
625
+ ComputeBudgetProgram.setComputeUnitLimit({ units: BRIDGE_OUT_CUS }),
626
+ ...instructions,
627
+ ], {
628
+ variation: "Intent NTT Bridge",
629
+ paymasterDomain: SESSIONS_INTERNAL_PAYMASTER_DOMAIN,
577
630
  extraSigners: [outboxItem],
578
631
  addressLookupTable: BRIDGING_ADDRESS_LOOKUP_TABLE[options.context.network]?.[options.fromToken.mint.toBase58()],
579
632
  });
580
633
  };
634
+ const getDestinationAtaExists = async (context, token, wallet) => {
635
+ const solanaConnection = await context.getSolanaConnection();
636
+ const ataAccount = await solanaConnection.getAccountInfo(getAssociatedTokenAddressSync(token, wallet));
637
+ return ataAccount !== null;
638
+ };
581
639
  // Here we use the Wormhole SDKs to produce the wormhole pdas that are needed
582
640
  // for the bridge out transaction. Currently this is using wormhole SDK apis
583
641
  // that are _technically_ public but it seems likely these are not considered to
@@ -595,6 +653,10 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
595
653
  const transceiverPdas = NTT.transceiverPdas(options.fromToken.manager);
596
654
  const [intentTransferSetterPda] = PublicKey.findProgramAddressSync([Buffer.from("intent_transfer")], program.programId);
597
655
  const wormholePdas = utils.getWormholeDerivedAccounts(options.fromToken.manager, coreBridgeContract);
656
+ const [registeredTransceiverPda] = PublicKey.findProgramAddressSync([
657
+ Buffer.from("registered_transceiver"),
658
+ options.fromToken.manager.toBytes(),
659
+ ], options.fromToken.manager);
598
660
  return {
599
661
  emitter: transceiverPdas.emitterAccount(),
600
662
  nttConfig: pdas.configAccount(),
@@ -607,7 +669,7 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
607
669
  nttSessionAuthority: pdas.sessionAuthority(intentTransferSetterPda, NTT.transferArgs(options.amount, Wormhole.chainAddress("Solana", options.walletPublicKey.toBase58()), false)),
608
670
  nttTokenAuthority: pdas.tokenAuthority(),
609
671
  payeeNttWithExecutor: quotePayeeAddress,
610
- transceiver: options.fromToken.transceiver,
672
+ transceiver: registeredTransceiverPda,
611
673
  wormholeProgram: coreBridgeContract,
612
674
  wormholeBridge: wormholePdas.wormholeBridge,
613
675
  wormholeFeeCollector: wormholePdas.wormholeFeeCollector,
@@ -615,7 +677,7 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
615
677
  wormholeSequence: wormholePdas.wormholeSequence,
616
678
  };
617
679
  };
618
- const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
680
+ const buildBridgeOutIntent = async (program, options, decimals, symbol, feeToken, feeAmount) => {
619
681
  const nonce = await getNonce(program, options.walletPublicKey, NonceType.Bridge);
620
682
  const message = new TextEncoder().encode([
621
683
  BRIDGE_OUT_MESSAGE_HEADER,
@@ -626,6 +688,8 @@ const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
626
688
  token: symbol ?? options.fromToken.mint.toBase58(),
627
689
  amount: amountToString(options.amount, decimals),
628
690
  recipient_address: options.walletPublicKey.toBase58(),
691
+ fee_token: feeToken,
692
+ fee_amount: feeAmount,
629
693
  nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
630
694
  }),
631
695
  ].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.22",
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
  }