@fogo/sessions-sdk 0.1.4 → 0.1.6

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/connection.js CHANGED
@@ -4,17 +4,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createSessionConnection = exports.TransactionResultType = void 0;
7
- const anchor_1 = require("@coral-xyz/anchor");
8
7
  const sessions_idls_1 = require("@fogo/sessions-idls");
9
- const sha2_1 = require("@noble/hashes/sha2");
10
8
  const compat_1 = require("@solana/compat");
11
9
  const kit_1 = require("@solana/kit");
12
- const spl_token_1 = require("@solana/spl-token");
13
10
  const web3_js_1 = require("@solana/web3.js");
14
11
  const bn_js_1 = __importDefault(require("bn.js"));
15
12
  const zod_1 = require("zod");
13
+ const instructions_js_1 = require("./instructions.js");
16
14
  const mints_js_1 = require("./mints.js");
17
15
  const network_js_1 = require("./network.js");
16
+ const paymaster_js_1 = require("./paymaster.js");
18
17
  const DEFAULT_RPC = {
19
18
  [network_js_1.Network.Testnet]: "https://testnet.fogo.io",
20
19
  [network_js_1.Network.Mainnet]: "https://mainnet.fogo.io",
@@ -101,7 +100,7 @@ const sendToPaymaster = async (connection, domain, sessionKey, instructions, wal
101
100
  return sponsorAndSendResponseSchema.parse(await response.json());
102
101
  }
103
102
  else {
104
- throw new PaymasterResponseError(response.status, await response.text());
103
+ throw new paymaster_js_1.PaymasterResponseError(response.status, await response.text());
105
104
  }
106
105
  }
107
106
  else {
@@ -119,7 +118,9 @@ const buildTransaction = async (connection, domain, sessionKey, signerKeys, inst
119
118
  ? Promise.resolve(undefined)
120
119
  : getAddressLookupTable(connection.connection, connection.addressLookupTableCache, extraConfig.addressLookupTable),
121
120
  Promise.all(signerKeys.map((signer) => (0, kit_1.createSignerFromKeyPair)(signer))),
122
- getFee(connection, domain, extraConfig?.variation, feeMint),
121
+ extraConfig?.variation === undefined
122
+ ? Promise.resolve(new bn_js_1.default(0))
123
+ : (0, paymaster_js_1.getPaymasterFee)(connection.paymaster ?? DEFAULT_PAYMASTER[connection.network], domain, extraConfig.variation, feeMint),
123
124
  sessionKey === undefined
124
125
  ? Promise.resolve(undefined)
125
126
  : (0, kit_1.getAddressFromPublicKey)(sessionKey.publicKey),
@@ -133,7 +134,8 @@ const buildTransaction = async (connection, domain, sessionKey, signerKeys, inst
133
134
  });
134
135
  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
135
136
  ? (0, compat_1.fromLegacyTransactionInstruction)(instruction)
136
- : instruction), tx), (tx) => tollboothInstruction === undefined
137
+ : instruction), tx), (tx) => tollboothInstruction === undefined ||
138
+ tx.instructions.some((instruction) => instruction.programAddress == sessions_idls_1.TollboothIdl.address)
137
139
  ? tx
138
140
  : (0, kit_1.appendTransactionMessageInstructions)([tollboothInstruction], tx), (tx) => addressLookupTable === undefined
139
141
  ? tx
@@ -141,24 +143,15 @@ const buildTransaction = async (connection, domain, sessionKey, signerKeys, inst
141
143
  [(0, compat_1.fromLegacyPublicKey)(addressLookupTable.key)]: addressLookupTable.state.addresses.map((address) => (0, compat_1.fromLegacyPublicKey)(address)),
142
144
  }), (tx) => (0, kit_1.addSignersToTransactionMessage)(signers, tx)));
143
145
  };
144
- const getDomainTollRecipientAddress = (domain) => {
145
- const hash = (0, sha2_1.sha256)(domain);
146
- return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("toll_recipient"), Buffer.from([0]), hash], new web3_js_1.PublicKey(sessions_idls_1.TollboothIdl.address))[0];
147
- };
148
- const buildTollboothInstructionIfNeeded = async ({ sessionKeyAddress, walletPublicKey, domain, feeMint, feeAmount, }) => {
146
+ const buildTollboothInstructionIfNeeded = ({ sessionKeyAddress, walletPublicKey, domain, feeMint, feeAmount, }) => {
149
147
  if (feeAmount.gt(new bn_js_1.default(0)) && sessionKeyAddress !== undefined) {
150
- const userTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(feeMint, walletPublicKey);
151
- const recipient = getDomainTollRecipientAddress(domain);
152
- const instruction = await new sessions_idls_1.TollboothProgram(new anchor_1.AnchorProvider({}, {})).methods
153
- .payToll(feeAmount, 0)
154
- .accounts({
155
- session: new web3_js_1.PublicKey(sessionKeyAddress),
156
- source: userTokenAccount,
157
- destination: (0, spl_token_1.getAssociatedTokenAddressSync)(feeMint, recipient, true),
158
- mint: feeMint,
159
- })
160
- .instruction();
161
- return (0, compat_1.fromLegacyTransactionInstruction)(instruction);
148
+ return (0, instructions_js_1.createPaymasterFeeInstruction)({
149
+ sessionKey: new web3_js_1.PublicKey(sessionKeyAddress),
150
+ walletPublicKey,
151
+ domain,
152
+ feeMint,
153
+ feeAmount,
154
+ }).then(compat_1.fromLegacyTransactionInstruction);
162
155
  }
163
156
  else {
164
157
  return undefined;
@@ -208,31 +201,13 @@ const getSponsor = async (options, sponsorCache, domain) => {
208
201
  return sponsor;
209
202
  }
210
203
  else {
211
- throw new PaymasterResponseError(response.status, await response.text());
204
+ throw new paymaster_js_1.PaymasterResponseError(response.status, await response.text());
212
205
  }
213
206
  }
214
207
  else {
215
208
  return value;
216
209
  }
217
210
  };
218
- const getFee = async (options, domain, variation, mint) => {
219
- if (variation) {
220
- const url = new URL("/api/fee", options.paymaster ?? DEFAULT_PAYMASTER[options.network]);
221
- url.searchParams.set("domain", domain);
222
- url.searchParams.set("variation", variation);
223
- url.searchParams.set("mint", mint.toBase58());
224
- const response = await fetch(url);
225
- if (response.status === 200) {
226
- return new bn_js_1.default(await response.text());
227
- }
228
- else {
229
- throw new PaymasterResponseError(response.status, await response.text());
230
- }
231
- }
232
- else {
233
- return new bn_js_1.default(0);
234
- }
235
- };
236
211
  const getAddressLookupTable = async (connection, addressLookupTableCache, addressLookupTableAddress) => {
237
212
  const value = addressLookupTableCache.get(addressLookupTableAddress);
238
213
  if (value === undefined) {
@@ -249,9 +224,3 @@ const getAddressLookupTable = async (connection, addressLookupTableCache, addres
249
224
  return value;
250
225
  }
251
226
  };
252
- class PaymasterResponseError extends Error {
253
- constructor(statusCode, message) {
254
- super(`Paymaster sent a ${statusCode.toString()} response: ${message}`);
255
- this.name = "PaymasterResponseError";
256
- }
257
- }
package/cjs/index.d.ts CHANGED
@@ -7,8 +7,9 @@ import type { TransactionOrInstructions, TransactionResult } from "./connection.
7
7
  import type { SendTransactionOptions, SessionContext } from "./context.js";
8
8
  export { type Connection, createSessionConnection, type TransactionOrInstructions, type TransactionResult, TransactionResultType, } from "./connection.js";
9
9
  export { createSessionContext, type SendTransactionOptions, type SessionContext, } from "./context.js";
10
- export { createSessionUnwrapInstruction, createSessionWrapInstructions, createSystemProgramSessionWrapInstruction, } from "./instructions.js";
10
+ export { createPaymasterFeeInstruction, createSessionUnwrapInstruction, createSessionWrapInstructions, createSystemProgramSessionWrapInstruction, } from "./instructions.js";
11
11
  export { Network } from "./network.js";
12
+ export { getPaymasterFee } from "./paymaster.js";
12
13
  type EstablishSessionOptions = {
13
14
  context: SessionContext;
14
15
  walletPublicKey: PublicKey;
@@ -61,7 +62,7 @@ export declare const getSessionAccount: (connection: Connection, sessionPublicKe
61
62
  expiration: Date;
62
63
  extra: unknown;
63
64
  major: number;
64
- minor: 1 | 2 | 3 | 4;
65
+ minor: 4 | 1 | 2 | 3;
65
66
  user: PublicKey;
66
67
  sponsor: PublicKey;
67
68
  } | undefined>;
@@ -2042,7 +2043,7 @@ declare const sessionInfoSchema: z.ZodEffects<z.ZodObject<{
2042
2043
  expiration: Date;
2043
2044
  extra: unknown;
2044
2045
  major: number;
2045
- minor: 1 | 2 | 3 | 4;
2046
+ minor: 4 | 1 | 2 | 3;
2046
2047
  user: PublicKey;
2047
2048
  sponsor: PublicKey;
2048
2049
  } | undefined, {
@@ -2339,7 +2340,7 @@ export declare const verifyLogInToken: (token: string, connection: Connection) =
2339
2340
  expiration: Date;
2340
2341
  extra: unknown;
2341
2342
  major: number;
2342
- minor: 1 | 2 | 3 | 4;
2343
+ minor: 4 | 1 | 2 | 3;
2343
2344
  user: PublicKey;
2344
2345
  sponsor: PublicKey;
2345
2346
  } | undefined>;
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.sendNativeTransfer = 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.Network = exports.createSystemProgramSessionWrapInstruction = exports.createSessionWrapInstructions = exports.createSessionUnwrapInstruction = exports.createSessionContext = exports.TransactionResultType = exports.createSessionConnection = void 0;
6
+ exports.verifyLogInToken = exports.createLogInToken = exports.bridgeIn = exports.bridgeOut = exports.sendNativeTransfer = 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.getPaymasterFee = exports.Network = exports.createSystemProgramSessionWrapInstruction = exports.createSessionWrapInstructions = exports.createSessionUnwrapInstruction = exports.createPaymasterFeeInstruction = exports.createSessionContext = exports.TransactionResultType = exports.createSessionConnection = 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");
@@ -35,11 +35,14 @@ Object.defineProperty(exports, "TransactionResultType", { enumerable: true, get:
35
35
  var context_js_2 = require("./context.js");
36
36
  Object.defineProperty(exports, "createSessionContext", { enumerable: true, get: function () { return context_js_2.createSessionContext; } });
37
37
  var instructions_js_2 = require("./instructions.js");
38
+ Object.defineProperty(exports, "createPaymasterFeeInstruction", { enumerable: true, get: function () { return instructions_js_2.createPaymasterFeeInstruction; } });
38
39
  Object.defineProperty(exports, "createSessionUnwrapInstruction", { enumerable: true, get: function () { return instructions_js_2.createSessionUnwrapInstruction; } });
39
40
  Object.defineProperty(exports, "createSessionWrapInstructions", { enumerable: true, get: function () { return instructions_js_2.createSessionWrapInstructions; } });
40
41
  Object.defineProperty(exports, "createSystemProgramSessionWrapInstruction", { enumerable: true, get: function () { return instructions_js_2.createSystemProgramSessionWrapInstruction; } });
41
42
  var network_js_2 = require("./network.js");
42
43
  Object.defineProperty(exports, "Network", { enumerable: true, get: function () { return network_js_2.Network; } });
44
+ var paymaster_js_1 = require("./paymaster.js");
45
+ Object.defineProperty(exports, "getPaymasterFee", { enumerable: true, get: function () { return paymaster_js_1.getPaymasterFee; } });
43
46
  const MESSAGE_HEADER = `Fogo Sessions:
44
47
  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.
45
48
  `;
@@ -128,10 +131,36 @@ const getSessionAccount = async (connection, sessionPublicKey) => {
128
131
  : sessionInfoSchema.parse(new anchor_1.BorshAccountsCoder(sessions_idls_1.SessionManagerIdl).decode("Session", result.data));
129
132
  };
130
133
  exports.getSessionAccount = getSessionAccount;
134
+ const getDomainRegistryAuthorizedPrograms = async (connection, domain) => {
135
+ const result = await connection.getAccountInfo((0, exports.getDomainRecordAddress)(domain), "confirmed");
136
+ if (result === null) {
137
+ return [];
138
+ }
139
+ else {
140
+ const programs = [];
141
+ for (let i = 0; i < result.data.length; i += 64) {
142
+ programs.push({
143
+ programId: new web3_js_1.PublicKey(result.data.subarray(i, i + 32)),
144
+ signerPda: new web3_js_1.PublicKey(result.data.subarray(i + 32, i + 64)),
145
+ });
146
+ }
147
+ return programs;
148
+ }
149
+ };
150
+ const authorizedProgramsMatchDomainRegistry = (sessionAuthorizedPrograms, domainAuthorizedPrograms) => {
151
+ if (sessionAuthorizedPrograms.type === AuthorizedProgramsType.All) {
152
+ return true;
153
+ }
154
+ return domainAuthorizedPrograms.every(({ programId: programIdFromRegistry }) => sessionAuthorizedPrograms.programs.some(({ programId }) => programId.equals(programIdFromRegistry)));
155
+ };
131
156
  const createSession = async (context, walletPublicKey, sessionKey) => {
132
157
  const sessionPublicKey = new web3_js_1.PublicKey(await (0, kit_1.getAddressFromPublicKey)(sessionKey.publicKey));
133
- const sessionInfo = await (0, exports.getSessionAccount)(context.connection, sessionPublicKey);
134
- return sessionInfo === undefined
158
+ const [sessionInfo, domainRegistryAuthorizedPrograms] = await Promise.all([
159
+ (0, exports.getSessionAccount)(context.connection, sessionPublicKey),
160
+ getDomainRegistryAuthorizedPrograms(context.connection, context.domain),
161
+ ]);
162
+ return sessionInfo === undefined ||
163
+ !authorizedProgramsMatchDomainRegistry(sessionInfo.authorizedPrograms, domainRegistryAuthorizedPrograms)
135
164
  ? undefined
136
165
  : {
137
166
  sessionPublicKey,
@@ -1,5 +1,5 @@
1
- import type { PublicKey } from "@solana/web3.js";
2
- import { TransactionInstruction } from "@solana/web3.js";
1
+ import { PublicKey, TransactionInstruction } from "@solana/web3.js";
2
+ import type BN from "bn.js";
3
3
  /**
4
4
  * Creates the system program instruction `SessionWrap`, only available on Fogo, which allows a session key to transfer native token from its user's wallet to its user's wrapped token associated token account.
5
5
  * This instruction may be combined with the `CreateAssociatedTokenAccountIdempotent` and `SyncNative` instructions for a session to wrap tokens on behalf of its user.
@@ -17,3 +17,15 @@ export declare function createSessionWrapInstructions(sessionKey: PublicKey, wal
17
17
  * Creates the instruction required to unwrap native tokens within a session.
18
18
  */
19
19
  export declare function createSessionUnwrapInstruction(sessionKey: PublicKey, walletPublicKey: PublicKey): TransactionInstruction;
20
+ /**
21
+ * Creates the instruction required to pay the paymaster fee for a transaction.
22
+ * This instruction is only required if the transaction variation has a fee and may be placed anywhere in the instruction list.
23
+ * The fee amount for a variation in a given token can be retrieved using the `getPaymasterFee` function.
24
+ */
25
+ export declare const createPaymasterFeeInstruction: ({ sessionKey, walletPublicKey, domain, feeMint, feeAmount, }: {
26
+ sessionKey: PublicKey;
27
+ walletPublicKey: PublicKey;
28
+ domain: string;
29
+ feeMint: PublicKey;
30
+ feeAmount: BN;
31
+ }) => Promise<TransactionInstruction>;
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createPaymasterFeeInstruction = void 0;
3
4
  exports.createSystemProgramSessionWrapInstruction = createSystemProgramSessionWrapInstruction;
4
5
  exports.createSessionWrapInstructions = createSessionWrapInstructions;
5
6
  exports.createSessionUnwrapInstruction = createSessionUnwrapInstruction;
7
+ const anchor_1 = require("@coral-xyz/anchor");
8
+ const sessions_idls_1 = require("@fogo/sessions-idls");
9
+ const sha2_1 = require("@noble/hashes/sha2");
6
10
  const spl_token_1 = require("@solana/spl-token");
7
11
  const web3_js_1 = require("@solana/web3.js");
8
12
  const SESSION_WRAP_DISCRIMINATOR = 4_000_000;
@@ -54,3 +58,25 @@ function createSessionWrapInstructions(sessionKey, walletPublicKey, amount) {
54
58
  function createSessionUnwrapInstruction(sessionKey, walletPublicKey) {
55
59
  return (0, spl_token_1.createCloseAccountInstruction)(getNativeMintAssociatedTokenAddressSync(walletPublicKey), walletPublicKey, sessionKey);
56
60
  }
61
+ const getDomainTollRecipientAddress = (domain) => {
62
+ const hash = (0, sha2_1.sha256)(domain);
63
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("toll_recipient"), Buffer.from([0]), hash], new web3_js_1.PublicKey(sessions_idls_1.TollboothIdl.address))[0];
64
+ };
65
+ /**
66
+ * Creates the instruction required to pay the paymaster fee for a transaction.
67
+ * This instruction is only required if the transaction variation has a fee and may be placed anywhere in the instruction list.
68
+ * The fee amount for a variation in a given token can be retrieved using the `getPaymasterFee` function.
69
+ */
70
+ const createPaymasterFeeInstruction = ({ sessionKey, walletPublicKey, domain, feeMint, feeAmount, }) => {
71
+ const recipient = getDomainTollRecipientAddress(domain);
72
+ return new sessions_idls_1.TollboothProgram(new anchor_1.AnchorProvider({}, {})).methods
73
+ .payToll(feeAmount, 0)
74
+ .accounts({
75
+ session: sessionKey,
76
+ source: (0, spl_token_1.getAssociatedTokenAddressSync)(feeMint, walletPublicKey),
77
+ destination: (0, spl_token_1.getAssociatedTokenAddressSync)(feeMint, recipient, true),
78
+ mint: feeMint,
79
+ })
80
+ .instruction();
81
+ };
82
+ exports.createPaymasterFeeInstruction = createPaymasterFeeInstruction;
@@ -0,0 +1,9 @@
1
+ import type { PublicKey } from "@solana/web3.js";
2
+ import BN from "bn.js";
3
+ export declare class PaymasterResponseError extends Error {
4
+ constructor(statusCode: number, message: string);
5
+ }
6
+ /**
7
+ * Retrieves the paymaster fee amount from the paymaster server for a given transaction variation when paid in a given mint.
8
+ */
9
+ export declare const getPaymasterFee: (paymaster: string | URL, domain: string, variation: string, mint: PublicKey) => Promise<BN>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getPaymasterFee = exports.PaymasterResponseError = void 0;
7
+ const bn_js_1 = __importDefault(require("bn.js"));
8
+ class PaymasterResponseError extends Error {
9
+ constructor(statusCode, message) {
10
+ super(`Paymaster sent a ${statusCode.toString()} response: ${message}`);
11
+ this.name = "PaymasterResponseError";
12
+ }
13
+ }
14
+ exports.PaymasterResponseError = PaymasterResponseError;
15
+ /**
16
+ * Retrieves the paymaster fee amount from the paymaster server for a given transaction variation when paid in a given mint.
17
+ */
18
+ const getPaymasterFee = async (paymaster, domain, variation, mint) => {
19
+ const url = new URL("/api/fee", paymaster);
20
+ url.searchParams.set("domain", domain);
21
+ url.searchParams.set("variation", variation);
22
+ url.searchParams.set("mint", mint.toBase58());
23
+ const response = await fetch(url);
24
+ if (response.status === 200) {
25
+ return new bn_js_1.default(await response.text());
26
+ }
27
+ else {
28
+ throw new PaymasterResponseError(response.status, await response.text());
29
+ }
30
+ };
31
+ exports.getPaymasterFee = getPaymasterFee;
package/esm/connection.js CHANGED
@@ -1,14 +1,13 @@
1
- import { AnchorProvider } from "@coral-xyz/anchor";
2
- import { TollboothIdl, TollboothProgram } from "@fogo/sessions-idls";
3
- import { sha256 } from "@noble/hashes/sha2";
1
+ import { TollboothIdl } from "@fogo/sessions-idls";
4
2
  import { fromLegacyKeypair, fromLegacyPublicKey, fromLegacyTransactionInstruction, fromVersionedTransaction, } from "@solana/compat";
5
3
  import { addSignersToTransactionMessage, appendTransactionMessageInstructions, compressTransactionMessageUsingAddressLookupTables, createSignerFromKeyPair, createSolanaRpc, createTransactionMessage, getAddressFromPublicKey, getBase64EncodedWireTransaction, partiallySignTransaction, partiallySignTransactionMessageWithSigners, pipe, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, } from "@solana/kit";
6
- import { getAssociatedTokenAddressSync } from "@solana/spl-token";
7
4
  import { Keypair, PublicKey, TransactionInstruction, VersionedTransaction, Connection as Web3Connection, } from "@solana/web3.js";
8
5
  import BN from "bn.js";
9
6
  import { z } from "zod";
7
+ import { createPaymasterFeeInstruction } from "./instructions.js";
10
8
  import { USDC_MINT } from "./mints.js";
11
9
  import { Network } from "./network.js";
10
+ import { getPaymasterFee, PaymasterResponseError } from "./paymaster.js";
12
11
  const DEFAULT_RPC = {
13
12
  [Network.Testnet]: "https://testnet.fogo.io",
14
13
  [Network.Mainnet]: "https://mainnet.fogo.io",
@@ -112,7 +111,9 @@ const buildTransaction = async (connection, domain, sessionKey, signerKeys, inst
112
111
  ? Promise.resolve(undefined)
113
112
  : getAddressLookupTable(connection.connection, connection.addressLookupTableCache, extraConfig.addressLookupTable),
114
113
  Promise.all(signerKeys.map((signer) => createSignerFromKeyPair(signer))),
115
- getFee(connection, domain, extraConfig?.variation, feeMint),
114
+ extraConfig?.variation === undefined
115
+ ? Promise.resolve(new BN(0))
116
+ : getPaymasterFee(connection.paymaster ?? DEFAULT_PAYMASTER[connection.network], domain, extraConfig.variation, feeMint),
116
117
  sessionKey === undefined
117
118
  ? Promise.resolve(undefined)
118
119
  : getAddressFromPublicKey(sessionKey.publicKey),
@@ -126,7 +127,8 @@ const buildTransaction = async (connection, domain, sessionKey, signerKeys, inst
126
127
  });
127
128
  return partiallySignTransactionMessageWithSigners(pipe(createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(fromLegacyPublicKey(sponsor), tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions.map((instruction) => instruction instanceof TransactionInstruction
128
129
  ? fromLegacyTransactionInstruction(instruction)
129
- : instruction), tx), (tx) => tollboothInstruction === undefined
130
+ : instruction), tx), (tx) => tollboothInstruction === undefined ||
131
+ tx.instructions.some((instruction) => instruction.programAddress == TollboothIdl.address)
130
132
  ? tx
131
133
  : appendTransactionMessageInstructions([tollboothInstruction], tx), (tx) => addressLookupTable === undefined
132
134
  ? tx
@@ -134,24 +136,15 @@ const buildTransaction = async (connection, domain, sessionKey, signerKeys, inst
134
136
  [fromLegacyPublicKey(addressLookupTable.key)]: addressLookupTable.state.addresses.map((address) => fromLegacyPublicKey(address)),
135
137
  }), (tx) => addSignersToTransactionMessage(signers, tx)));
136
138
  };
137
- const getDomainTollRecipientAddress = (domain) => {
138
- const hash = sha256(domain);
139
- return PublicKey.findProgramAddressSync([Buffer.from("toll_recipient"), Buffer.from([0]), hash], new PublicKey(TollboothIdl.address))[0];
140
- };
141
- const buildTollboothInstructionIfNeeded = async ({ sessionKeyAddress, walletPublicKey, domain, feeMint, feeAmount, }) => {
139
+ const buildTollboothInstructionIfNeeded = ({ sessionKeyAddress, walletPublicKey, domain, feeMint, feeAmount, }) => {
142
140
  if (feeAmount.gt(new BN(0)) && sessionKeyAddress !== undefined) {
143
- const userTokenAccount = getAssociatedTokenAddressSync(feeMint, walletPublicKey);
144
- const recipient = getDomainTollRecipientAddress(domain);
145
- const instruction = await new TollboothProgram(new AnchorProvider({}, {})).methods
146
- .payToll(feeAmount, 0)
147
- .accounts({
148
- session: new PublicKey(sessionKeyAddress),
149
- source: userTokenAccount,
150
- destination: getAssociatedTokenAddressSync(feeMint, recipient, true),
151
- mint: feeMint,
152
- })
153
- .instruction();
154
- return fromLegacyTransactionInstruction(instruction);
141
+ return createPaymasterFeeInstruction({
142
+ sessionKey: new PublicKey(sessionKeyAddress),
143
+ walletPublicKey,
144
+ domain,
145
+ feeMint,
146
+ feeAmount,
147
+ }).then(fromLegacyTransactionInstruction);
155
148
  }
156
149
  else {
157
150
  return undefined;
@@ -208,24 +201,6 @@ const getSponsor = async (options, sponsorCache, domain) => {
208
201
  return value;
209
202
  }
210
203
  };
211
- const getFee = async (options, domain, variation, mint) => {
212
- if (variation) {
213
- const url = new URL("/api/fee", options.paymaster ?? DEFAULT_PAYMASTER[options.network]);
214
- url.searchParams.set("domain", domain);
215
- url.searchParams.set("variation", variation);
216
- url.searchParams.set("mint", mint.toBase58());
217
- const response = await fetch(url);
218
- if (response.status === 200) {
219
- return new BN(await response.text());
220
- }
221
- else {
222
- throw new PaymasterResponseError(response.status, await response.text());
223
- }
224
- }
225
- else {
226
- return new BN(0);
227
- }
228
- };
229
204
  const getAddressLookupTable = async (connection, addressLookupTableCache, addressLookupTableAddress) => {
230
205
  const value = addressLookupTableCache.get(addressLookupTableAddress);
231
206
  if (value === undefined) {
@@ -242,9 +217,3 @@ const getAddressLookupTable = async (connection, addressLookupTableCache, addres
242
217
  return value;
243
218
  }
244
219
  };
245
- class PaymasterResponseError extends Error {
246
- constructor(statusCode, message) {
247
- super(`Paymaster sent a ${statusCode.toString()} response: ${message}`);
248
- this.name = "PaymasterResponseError";
249
- }
250
- }
package/esm/index.d.ts CHANGED
@@ -7,8 +7,9 @@ import type { TransactionOrInstructions, TransactionResult } from "./connection.
7
7
  import type { SendTransactionOptions, SessionContext } from "./context.js";
8
8
  export { type Connection, createSessionConnection, type TransactionOrInstructions, type TransactionResult, TransactionResultType, } from "./connection.js";
9
9
  export { createSessionContext, type SendTransactionOptions, type SessionContext, } from "./context.js";
10
- export { createSessionUnwrapInstruction, createSessionWrapInstructions, createSystemProgramSessionWrapInstruction, } from "./instructions.js";
10
+ export { createPaymasterFeeInstruction, createSessionUnwrapInstruction, createSessionWrapInstructions, createSystemProgramSessionWrapInstruction, } from "./instructions.js";
11
11
  export { Network } from "./network.js";
12
+ export { getPaymasterFee } from "./paymaster.js";
12
13
  type EstablishSessionOptions = {
13
14
  context: SessionContext;
14
15
  walletPublicKey: PublicKey;
@@ -61,7 +62,7 @@ export declare const getSessionAccount: (connection: Connection, sessionPublicKe
61
62
  expiration: Date;
62
63
  extra: unknown;
63
64
  major: number;
64
- minor: 1 | 2 | 3 | 4;
65
+ minor: 4 | 1 | 2 | 3;
65
66
  user: PublicKey;
66
67
  sponsor: PublicKey;
67
68
  } | undefined>;
@@ -2042,7 +2043,7 @@ declare const sessionInfoSchema: z.ZodEffects<z.ZodObject<{
2042
2043
  expiration: Date;
2043
2044
  extra: unknown;
2044
2045
  major: number;
2045
- minor: 1 | 2 | 3 | 4;
2046
+ minor: 4 | 1 | 2 | 3;
2046
2047
  user: PublicKey;
2047
2048
  sponsor: PublicKey;
2048
2049
  } | undefined, {
@@ -2339,7 +2340,7 @@ export declare const verifyLogInToken: (token: string, connection: Connection) =
2339
2340
  expiration: Date;
2340
2341
  extra: unknown;
2341
2342
  major: number;
2342
- minor: 1 | 2 | 3 | 4;
2343
+ minor: 4 | 1 | 2 | 3;
2343
2344
  user: PublicKey;
2344
2345
  sponsor: PublicKey;
2345
2346
  } | undefined>;
package/esm/index.js CHANGED
@@ -25,8 +25,9 @@ import { USDC_DECIMALS, USDC_MINT } from "./mints.js";
25
25
  import { Network } from "./network.js";
26
26
  export { createSessionConnection, TransactionResultType, } from "./connection.js";
27
27
  export { createSessionContext, } from "./context.js";
28
- export { createSessionUnwrapInstruction, createSessionWrapInstructions, createSystemProgramSessionWrapInstruction, } from "./instructions.js";
28
+ export { createPaymasterFeeInstruction, createSessionUnwrapInstruction, createSessionWrapInstructions, createSystemProgramSessionWrapInstruction, } from "./instructions.js";
29
29
  export { Network } from "./network.js";
30
+ export { getPaymasterFee } from "./paymaster.js";
30
31
  const MESSAGE_HEADER = `Fogo Sessions:
31
32
  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.
32
33
  `;
@@ -110,10 +111,36 @@ export const getSessionAccount = async (connection, sessionPublicKey) => {
110
111
  ? undefined
111
112
  : sessionInfoSchema.parse(new BorshAccountsCoder(SessionManagerIdl).decode("Session", result.data));
112
113
  };
114
+ const getDomainRegistryAuthorizedPrograms = async (connection, domain) => {
115
+ const result = await connection.getAccountInfo(getDomainRecordAddress(domain), "confirmed");
116
+ if (result === null) {
117
+ return [];
118
+ }
119
+ else {
120
+ const programs = [];
121
+ for (let i = 0; i < result.data.length; i += 64) {
122
+ programs.push({
123
+ programId: new PublicKey(result.data.subarray(i, i + 32)),
124
+ signerPda: new PublicKey(result.data.subarray(i + 32, i + 64)),
125
+ });
126
+ }
127
+ return programs;
128
+ }
129
+ };
130
+ const authorizedProgramsMatchDomainRegistry = (sessionAuthorizedPrograms, domainAuthorizedPrograms) => {
131
+ if (sessionAuthorizedPrograms.type === AuthorizedProgramsType.All) {
132
+ return true;
133
+ }
134
+ return domainAuthorizedPrograms.every(({ programId: programIdFromRegistry }) => sessionAuthorizedPrograms.programs.some(({ programId }) => programId.equals(programIdFromRegistry)));
135
+ };
113
136
  const createSession = async (context, walletPublicKey, sessionKey) => {
114
137
  const sessionPublicKey = new PublicKey(await getAddressFromPublicKey(sessionKey.publicKey));
115
- const sessionInfo = await getSessionAccount(context.connection, sessionPublicKey);
116
- return sessionInfo === undefined
138
+ const [sessionInfo, domainRegistryAuthorizedPrograms] = await Promise.all([
139
+ getSessionAccount(context.connection, sessionPublicKey),
140
+ getDomainRegistryAuthorizedPrograms(context.connection, context.domain),
141
+ ]);
142
+ return sessionInfo === undefined ||
143
+ !authorizedProgramsMatchDomainRegistry(sessionInfo.authorizedPrograms, domainRegistryAuthorizedPrograms)
117
144
  ? undefined
118
145
  : {
119
146
  sessionPublicKey,
@@ -1,5 +1,5 @@
1
- import type { PublicKey } from "@solana/web3.js";
2
- import { TransactionInstruction } from "@solana/web3.js";
1
+ import { PublicKey, TransactionInstruction } from "@solana/web3.js";
2
+ import type BN from "bn.js";
3
3
  /**
4
4
  * Creates the system program instruction `SessionWrap`, only available on Fogo, which allows a session key to transfer native token from its user's wallet to its user's wrapped token associated token account.
5
5
  * This instruction may be combined with the `CreateAssociatedTokenAccountIdempotent` and `SyncNative` instructions for a session to wrap tokens on behalf of its user.
@@ -17,3 +17,15 @@ export declare function createSessionWrapInstructions(sessionKey: PublicKey, wal
17
17
  * Creates the instruction required to unwrap native tokens within a session.
18
18
  */
19
19
  export declare function createSessionUnwrapInstruction(sessionKey: PublicKey, walletPublicKey: PublicKey): TransactionInstruction;
20
+ /**
21
+ * Creates the instruction required to pay the paymaster fee for a transaction.
22
+ * This instruction is only required if the transaction variation has a fee and may be placed anywhere in the instruction list.
23
+ * The fee amount for a variation in a given token can be retrieved using the `getPaymasterFee` function.
24
+ */
25
+ export declare const createPaymasterFeeInstruction: ({ sessionKey, walletPublicKey, domain, feeMint, feeAmount, }: {
26
+ sessionKey: PublicKey;
27
+ walletPublicKey: PublicKey;
28
+ domain: string;
29
+ feeMint: PublicKey;
30
+ feeAmount: BN;
31
+ }) => Promise<TransactionInstruction>;
@@ -1,5 +1,8 @@
1
+ import { AnchorProvider } from "@coral-xyz/anchor";
2
+ import { TollboothIdl, TollboothProgram } from "@fogo/sessions-idls";
3
+ import { sha256 } from "@noble/hashes/sha2";
1
4
  import { createAssociatedTokenAccountIdempotentInstruction, createCloseAccountInstruction, createSyncNativeInstruction, getAssociatedTokenAddressSync, NATIVE_MINT, } from "@solana/spl-token";
2
- import { SystemProgram, TransactionInstruction } from "@solana/web3.js";
5
+ import { PublicKey, SystemProgram, TransactionInstruction, } from "@solana/web3.js";
3
6
  const SESSION_WRAP_DISCRIMINATOR = 4_000_000;
4
7
  function getNativeMintAssociatedTokenAddressSync(walletPublicKey) {
5
8
  return getAssociatedTokenAddressSync(NATIVE_MINT, walletPublicKey);
@@ -49,3 +52,24 @@ export function createSessionWrapInstructions(sessionKey, walletPublicKey, amoun
49
52
  export function createSessionUnwrapInstruction(sessionKey, walletPublicKey) {
50
53
  return createCloseAccountInstruction(getNativeMintAssociatedTokenAddressSync(walletPublicKey), walletPublicKey, sessionKey);
51
54
  }
55
+ const getDomainTollRecipientAddress = (domain) => {
56
+ const hash = sha256(domain);
57
+ return PublicKey.findProgramAddressSync([Buffer.from("toll_recipient"), Buffer.from([0]), hash], new PublicKey(TollboothIdl.address))[0];
58
+ };
59
+ /**
60
+ * Creates the instruction required to pay the paymaster fee for a transaction.
61
+ * This instruction is only required if the transaction variation has a fee and may be placed anywhere in the instruction list.
62
+ * The fee amount for a variation in a given token can be retrieved using the `getPaymasterFee` function.
63
+ */
64
+ export const createPaymasterFeeInstruction = ({ sessionKey, walletPublicKey, domain, feeMint, feeAmount, }) => {
65
+ const recipient = getDomainTollRecipientAddress(domain);
66
+ return new TollboothProgram(new AnchorProvider({}, {})).methods
67
+ .payToll(feeAmount, 0)
68
+ .accounts({
69
+ session: sessionKey,
70
+ source: getAssociatedTokenAddressSync(feeMint, walletPublicKey),
71
+ destination: getAssociatedTokenAddressSync(feeMint, recipient, true),
72
+ mint: feeMint,
73
+ })
74
+ .instruction();
75
+ };
@@ -0,0 +1,9 @@
1
+ import type { PublicKey } from "@solana/web3.js";
2
+ import BN from "bn.js";
3
+ export declare class PaymasterResponseError extends Error {
4
+ constructor(statusCode: number, message: string);
5
+ }
6
+ /**
7
+ * Retrieves the paymaster fee amount from the paymaster server for a given transaction variation when paid in a given mint.
8
+ */
9
+ export declare const getPaymasterFee: (paymaster: string | URL, domain: string, variation: string, mint: PublicKey) => Promise<BN>;
@@ -0,0 +1,23 @@
1
+ import BN from "bn.js";
2
+ export class PaymasterResponseError extends Error {
3
+ constructor(statusCode, message) {
4
+ super(`Paymaster sent a ${statusCode.toString()} response: ${message}`);
5
+ this.name = "PaymasterResponseError";
6
+ }
7
+ }
8
+ /**
9
+ * Retrieves the paymaster fee amount from the paymaster server for a given transaction variation when paid in a given mint.
10
+ */
11
+ export const getPaymasterFee = async (paymaster, domain, variation, mint) => {
12
+ const url = new URL("/api/fee", paymaster);
13
+ url.searchParams.set("domain", domain);
14
+ url.searchParams.set("variation", variation);
15
+ url.searchParams.set("mint", mint.toBase58());
16
+ const response = await fetch(url);
17
+ if (response.status === 200) {
18
+ return new BN(await response.text());
19
+ }
20
+ else {
21
+ throw new PaymasterResponseError(response.status, await response.text());
22
+ }
23
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fogo/sessions-sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "A set of utilities for integrating with Fogo sessions",
5
5
  "repository": {
6
6
  "type": "git",