@fogo/sessions-sdk 0.0.16 → 0.0.18
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.d.ts +57 -0
- package/cjs/{adapter.js → connection.js} +77 -89
- package/cjs/context.d.ts +15 -0
- package/cjs/context.js +59 -0
- package/cjs/index.d.ts +10 -8
- package/cjs/index.js +36 -33
- package/esm/connection.d.ts +57 -0
- package/esm/{adapter.js → connection.js} +77 -89
- package/esm/context.d.ts +15 -0
- package/esm/context.js +55 -0
- package/esm/index.d.ts +10 -8
- package/esm/index.js +29 -28
- package/package.json +2 -2
- package/cjs/adapter.d.ts +0 -37
- package/esm/adapter.d.ts +0 -37
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Transaction, Instruction, TransactionWithLifetime } from "@solana/kit";
|
|
2
|
+
import type { AddressLookupTableAccount, TransactionError } from "@solana/web3.js";
|
|
3
|
+
import { Connection as Web3Connection, TransactionInstruction, VersionedTransaction, PublicKey } from "@solana/web3.js";
|
|
4
|
+
export declare enum Network {
|
|
5
|
+
Testnet = 0,
|
|
6
|
+
Mainnet = 1
|
|
7
|
+
}
|
|
8
|
+
export declare const DEFAULT_RPC: {
|
|
9
|
+
0: string;
|
|
10
|
+
1: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const DEFAULT_PAYMASTER: {
|
|
13
|
+
0: string;
|
|
14
|
+
1: string;
|
|
15
|
+
};
|
|
16
|
+
export declare enum TransactionResultType {
|
|
17
|
+
Success = 0,
|
|
18
|
+
Failed = 1
|
|
19
|
+
}
|
|
20
|
+
declare const TransactionResult: {
|
|
21
|
+
Success: (signature: string) => {
|
|
22
|
+
type: TransactionResultType.Success;
|
|
23
|
+
signature: string;
|
|
24
|
+
};
|
|
25
|
+
Failed: (signature: string, error: TransactionError) => {
|
|
26
|
+
type: TransactionResultType.Failed;
|
|
27
|
+
signature: string;
|
|
28
|
+
error: TransactionError;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export type TransactionResult = ReturnType<(typeof TransactionResult)[keyof typeof TransactionResult]>;
|
|
32
|
+
export declare const createSessionConnection: (options: {
|
|
33
|
+
network: Network;
|
|
34
|
+
rpc?: string | URL | undefined;
|
|
35
|
+
paymaster?: undefined;
|
|
36
|
+
sendToPaymaster?: undefined;
|
|
37
|
+
sponsor?: undefined;
|
|
38
|
+
} | ({
|
|
39
|
+
network?: Network | undefined;
|
|
40
|
+
rpc: string | URL;
|
|
41
|
+
} & ({
|
|
42
|
+
paymaster: string | URL;
|
|
43
|
+
sendToPaymaster?: undefined;
|
|
44
|
+
sponsor?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
paymaster?: undefined;
|
|
47
|
+
sendToPaymaster: (transaction: Transaction) => Promise<TransactionResult>;
|
|
48
|
+
sponsor: PublicKey;
|
|
49
|
+
}))) => {
|
|
50
|
+
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>;
|
|
51
|
+
connection: Web3Connection;
|
|
52
|
+
sendToPaymaster: (domain: string, sponsor: PublicKey, addressLookupTables: AddressLookupTableAccount[] | undefined, sessionKey: CryptoKeyPair | undefined, instructions: (TransactionInstruction | Instruction)[] | VersionedTransaction | (Transaction & TransactionWithLifetime)) => Promise<TransactionResult>;
|
|
53
|
+
getSponsor: (domain: string) => Promise<PublicKey>;
|
|
54
|
+
getAddressLookupTables: (addressLookupTableAddress?: string) => Promise<AddressLookupTableAccount[] | undefined>;
|
|
55
|
+
};
|
|
56
|
+
export type Connection = ReturnType<typeof createSessionConnection>;
|
|
57
|
+
export {};
|
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
import { AnchorProvider } from "@coral-xyz/anchor";
|
|
2
|
-
import { ChainIdProgram } from "@fogo/sessions-idls";
|
|
3
1
|
import { fromLegacyPublicKey, fromLegacyTransactionInstruction, fromVersionedTransaction, } from "@solana/compat";
|
|
4
|
-
import { createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions,
|
|
5
|
-
import {
|
|
2
|
+
import { createSolanaRpc, getBase64EncodedWireTransaction, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, partiallySignTransactionMessageWithSigners, pipe, addSignersToTransactionMessage, compressTransactionMessageUsingAddressLookupTables, createSignerFromKeyPair, partiallySignTransaction, } from "@solana/kit";
|
|
3
|
+
import { Connection as Web3Connection, TransactionInstruction, VersionedTransaction, PublicKey, } from "@solana/web3.js";
|
|
6
4
|
import { z } from "zod";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
export var Network;
|
|
6
|
+
(function (Network) {
|
|
7
|
+
Network[Network["Testnet"] = 0] = "Testnet";
|
|
8
|
+
Network[Network["Mainnet"] = 1] = "Mainnet";
|
|
9
|
+
})(Network || (Network = {}));
|
|
10
|
+
export const DEFAULT_RPC = {
|
|
11
|
+
[Network.Testnet]: "https://testnet.fogo.io",
|
|
12
|
+
[Network.Mainnet]: "https://mainnet.fogo.io",
|
|
13
|
+
};
|
|
14
|
+
export const DEFAULT_PAYMASTER = {
|
|
15
|
+
[Network.Testnet]: "https://paymaster.fogo.io",
|
|
16
|
+
[Network.Mainnet]: "https://paymaster.dourolabs.app",
|
|
17
|
+
};
|
|
18
|
+
const DEFAULT_ADDRESS_LOOKUP_TABLE_ADDRESS = {
|
|
19
|
+
[Network.Testnet]: "B8cUjJMqaWWTNNSTXBmeptjWswwCH1gTSCRYv4nu7kJW",
|
|
20
|
+
[Network.Mainnet]: undefined,
|
|
21
|
+
};
|
|
11
22
|
export var TransactionResultType;
|
|
12
23
|
(function (TransactionResultType) {
|
|
13
24
|
TransactionResultType[TransactionResultType["Success"] = 0] = "Success";
|
|
@@ -24,22 +35,51 @@ const TransactionResult = {
|
|
|
24
35
|
error,
|
|
25
36
|
}),
|
|
26
37
|
};
|
|
27
|
-
export const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
export const createSessionConnection = (options) => {
|
|
39
|
+
// For some reason, typescript is unable to narrow this type even though it's
|
|
40
|
+
// obvious that `rpc` can only be `undefined` if `network` is defined. I
|
|
41
|
+
// don't like the non-null assertion, but here we can guarantee it's safe (and
|
|
42
|
+
// typescript really should be able to narrow this...)
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
44
|
+
const rpcUrl = (options.rpc ?? DEFAULT_RPC[options.network]).toString();
|
|
45
|
+
const rpc = createSolanaRpc(rpcUrl);
|
|
46
|
+
const connection = new Web3Connection(rpcUrl, "confirmed");
|
|
31
47
|
return {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
domain: getDomain(options.domain),
|
|
36
|
-
sendTransaction: async (sessionKey, instructions) => {
|
|
37
|
-
const rpc = createSolanaRpc(options.connection.rpcEndpoint);
|
|
48
|
+
rpc,
|
|
49
|
+
connection,
|
|
50
|
+
sendToPaymaster: async (domain, sponsor, addressLookupTables, sessionKey, instructions) => {
|
|
38
51
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
39
|
-
|
|
52
|
+
const transaction = await buildTransaction(latestBlockhash, sessionKey, sponsor, instructions, addressLookupTables);
|
|
53
|
+
return sendToPaymaster(options, domain, transaction);
|
|
40
54
|
},
|
|
55
|
+
getSponsor: (domain) => getSponsor(options, domain),
|
|
56
|
+
getAddressLookupTables: (addressLookupTableAddress) => getAddressLookupTables(options, connection, addressLookupTableAddress),
|
|
41
57
|
};
|
|
42
58
|
};
|
|
59
|
+
const sendToPaymaster = async (options, domain, transaction) => {
|
|
60
|
+
if (options.sendToPaymaster === undefined) {
|
|
61
|
+
const url = new URL("/api/sponsor_and_send", options.paymaster ?? DEFAULT_PAYMASTER[options.network]);
|
|
62
|
+
url.searchParams.set("domain", domain);
|
|
63
|
+
const response = await fetch(url, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: {
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
transaction: getBase64EncodedWireTransaction(transaction),
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
if (response.status === 200) {
|
|
73
|
+
return sponsorAndSendResponseSchema.parse(await response.json());
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
throw new PaymasterResponseError(response.status, await response.text());
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
return options.sendToPaymaster(transaction);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
43
83
|
const buildTransaction = async (latestBlockhash, sessionKey, sponsor, instructions, addressLookupTables) => {
|
|
44
84
|
const sessionKeySigner = sessionKey
|
|
45
85
|
? await createSignerFromKeyPair(sessionKey)
|
|
@@ -63,22 +103,6 @@ const buildTransaction = async (latestBlockhash, sessionKey, sponsor, instructio
|
|
|
63
103
|
: partiallySignTransaction([sessionKey], tx);
|
|
64
104
|
}
|
|
65
105
|
};
|
|
66
|
-
const getSponsor = async (options, domain) => {
|
|
67
|
-
if ("sponsor" in options) {
|
|
68
|
-
return options.sponsor;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
const url = new URL("/api/sponsor_pubkey", options.paymaster ?? DEFAULT_PAYMASTER);
|
|
72
|
-
url.searchParams.set("domain", domain);
|
|
73
|
-
const response = await fetch(url);
|
|
74
|
-
if (response.status === 200) {
|
|
75
|
-
return new PublicKey(z.string().parse(await response.text()));
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
throw new PaymasterResponseError(response.status, await response.text());
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
106
|
const sponsorAndSendResponseSchema = z
|
|
83
107
|
.discriminatedUnion("type", [
|
|
84
108
|
z.object({
|
|
@@ -98,59 +122,35 @@ const sponsorAndSendResponseSchema = z
|
|
|
98
122
|
? TransactionResult.Success(data.signature)
|
|
99
123
|
: TransactionResult.Failed(data.signature, data.error);
|
|
100
124
|
});
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
const url = new URL("/api/sponsor_and_send", options.paymaster ?? DEFAULT_PAYMASTER);
|
|
125
|
+
const getSponsor = async (options, domain) => {
|
|
126
|
+
if (options.sponsor === undefined) {
|
|
127
|
+
const url = new URL("/api/sponsor_pubkey", options.paymaster ?? DEFAULT_PAYMASTER[options.network]);
|
|
107
128
|
url.searchParams.set("domain", domain);
|
|
108
|
-
const response = await fetch(url
|
|
109
|
-
method: "POST",
|
|
110
|
-
headers: {
|
|
111
|
-
"Content-Type": "application/json",
|
|
112
|
-
},
|
|
113
|
-
body: JSON.stringify({
|
|
114
|
-
transaction: getBase64EncodedWireTransaction(transaction),
|
|
115
|
-
}),
|
|
116
|
-
});
|
|
129
|
+
const response = await fetch(url);
|
|
117
130
|
if (response.status === 200) {
|
|
118
|
-
return
|
|
131
|
+
return new PublicKey(z.string().parse(await response.text()));
|
|
119
132
|
}
|
|
120
133
|
else {
|
|
121
134
|
throw new PaymasterResponseError(response.status, await response.text());
|
|
122
135
|
}
|
|
123
136
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const addressLookupTableResult = await connection.getAddressLookupTable(new PublicKey(addressLookupTableAddress));
|
|
127
|
-
return addressLookupTableResult.value
|
|
128
|
-
? [addressLookupTableResult.value]
|
|
129
|
-
: undefined;
|
|
130
|
-
};
|
|
131
|
-
const fetchChainId = async (connection) => {
|
|
132
|
-
const chainIdProgram = new ChainIdProgram(new AnchorProvider(connection, { publicKey: new Keypair().publicKey }, {})); // We mock the wallet because we don't need to sign anything
|
|
133
|
-
const { chainIdAccount: chainIdAddress } = await chainIdProgram.methods
|
|
134
|
-
.set("")
|
|
135
|
-
.pubkeys(); // We use Anchor to derive the chain ID address, not caring about the actual argument of `set`
|
|
136
|
-
if (chainIdAddress === undefined) {
|
|
137
|
-
throw new NoChainIdAddressError();
|
|
137
|
+
else {
|
|
138
|
+
return options.sponsor;
|
|
138
139
|
}
|
|
139
|
-
const chainId = await chainIdProgram.account.chainId.fetch(chainIdAddress);
|
|
140
|
-
return chainId.chainId;
|
|
141
140
|
};
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
141
|
+
const getAddressLookupTables = async (options, connection, addressLookupTableAddress) => {
|
|
142
|
+
const altAddress = addressLookupTableAddress ??
|
|
143
|
+
(options.network === undefined
|
|
144
|
+
? undefined
|
|
145
|
+
: DEFAULT_ADDRESS_LOOKUP_TABLE_ADDRESS[options.network]);
|
|
146
|
+
if (altAddress) {
|
|
147
|
+
const addressLookupTableResult = await connection.getAddressLookupTable(new PublicKey(altAddress));
|
|
148
|
+
return addressLookupTableResult.value
|
|
149
|
+
? [addressLookupTableResult.value]
|
|
150
|
+
: undefined;
|
|
151
151
|
}
|
|
152
152
|
else {
|
|
153
|
-
return
|
|
153
|
+
return;
|
|
154
154
|
}
|
|
155
155
|
};
|
|
156
156
|
class PaymasterResponseError extends Error {
|
|
@@ -159,15 +159,3 @@ class PaymasterResponseError extends Error {
|
|
|
159
159
|
this.name = "PaymasterResponseError";
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
-
class NoChainIdAddressError extends Error {
|
|
163
|
-
constructor() {
|
|
164
|
-
super("Failed to derive chain ID address");
|
|
165
|
-
this.name = "NoChainIdAddressError";
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
class DomainRequiredError extends Error {
|
|
169
|
-
constructor() {
|
|
170
|
-
super("On platforms where the origin cannot be determined, you must pass a domain to create a session.");
|
|
171
|
-
this.name = "DomainRequiredError";
|
|
172
|
-
}
|
|
173
|
-
}
|
package/esm/context.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Connection as Web3Connection } from "@solana/web3.js";
|
|
2
|
+
import type { Connection } from "./connection.js";
|
|
3
|
+
export declare const createSessionContext: (options: {
|
|
4
|
+
connection: Connection;
|
|
5
|
+
addressLookupTableAddress?: string | undefined;
|
|
6
|
+
domain?: string | undefined;
|
|
7
|
+
}) => Promise<{
|
|
8
|
+
chainId: string;
|
|
9
|
+
domain: string;
|
|
10
|
+
payer: import("@solana/web3.js").PublicKey;
|
|
11
|
+
connection: Web3Connection;
|
|
12
|
+
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>;
|
|
13
|
+
sendTransaction: (sessionKey: CryptoKeyPair | undefined, instructions: Parameters<typeof options.connection.sendToPaymaster>[4]) => Promise<import("./connection.js").TransactionResult>;
|
|
14
|
+
}>;
|
|
15
|
+
export type SessionContext = Awaited<ReturnType<typeof createSessionContext>>;
|
package/esm/context.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AnchorProvider } from "@coral-xyz/anchor";
|
|
2
|
+
import { ChainIdProgram } from "@fogo/sessions-idls";
|
|
3
|
+
import { Connection as Web3Connection, Keypair } from "@solana/web3.js";
|
|
4
|
+
// eslint-disable-next-line unicorn/no-typeof-undefined
|
|
5
|
+
const IS_BROWSER = typeof globalThis.window !== "undefined";
|
|
6
|
+
export const createSessionContext = async (options) => {
|
|
7
|
+
const addressLookupTables = await options.connection.getAddressLookupTables(options.addressLookupTableAddress);
|
|
8
|
+
const domain = getDomain(options.domain);
|
|
9
|
+
const sponsor = await options.connection.getSponsor(domain);
|
|
10
|
+
return {
|
|
11
|
+
chainId: await fetchChainId(options.connection.connection),
|
|
12
|
+
domain: getDomain(options.domain),
|
|
13
|
+
payer: sponsor,
|
|
14
|
+
connection: options.connection.connection,
|
|
15
|
+
rpc: options.connection.rpc,
|
|
16
|
+
sendTransaction: (sessionKey, instructions) => options.connection.sendToPaymaster(domain, sponsor, addressLookupTables, sessionKey, instructions),
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
const fetchChainId = async (connection) => {
|
|
20
|
+
const chainIdProgram = new ChainIdProgram(new AnchorProvider(connection, { publicKey: new Keypair().publicKey }, {})); // We mock the wallet because we don't need to sign anything
|
|
21
|
+
const { chainIdAccount: chainIdAddress } = await chainIdProgram.methods
|
|
22
|
+
.set("")
|
|
23
|
+
.pubkeys(); // We use Anchor to derive the chain ID address, not caring about the actual argument of `set`
|
|
24
|
+
if (chainIdAddress === undefined) {
|
|
25
|
+
throw new NoChainIdAddressError();
|
|
26
|
+
}
|
|
27
|
+
const chainId = await chainIdProgram.account.chainId.fetch(chainIdAddress);
|
|
28
|
+
return chainId.chainId;
|
|
29
|
+
};
|
|
30
|
+
const getDomain = (requestedDomain) => {
|
|
31
|
+
const detectedDomain = IS_BROWSER ? globalThis.location.origin : undefined;
|
|
32
|
+
if (requestedDomain === undefined) {
|
|
33
|
+
if (detectedDomain === undefined) {
|
|
34
|
+
throw new DomainRequiredError();
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return detectedDomain;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return requestedDomain;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
class NoChainIdAddressError extends Error {
|
|
45
|
+
constructor() {
|
|
46
|
+
super("Failed to derive chain ID address");
|
|
47
|
+
this.name = "NoChainIdAddressError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
class DomainRequiredError extends Error {
|
|
51
|
+
constructor() {
|
|
52
|
+
super("On platforms where the origin cannot be determined, you must pass a domain to create a session.");
|
|
53
|
+
this.name = "DomainRequiredError";
|
|
54
|
+
}
|
|
55
|
+
}
|
package/esm/index.d.ts
CHANGED
|
@@ -2,10 +2,12 @@ import type { Connection, TransactionError } from "@solana/web3.js";
|
|
|
2
2
|
import { PublicKey } from "@solana/web3.js";
|
|
3
3
|
import BN from "bn.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import type {
|
|
6
|
-
|
|
5
|
+
import type { TransactionResult } from "./connection.js";
|
|
6
|
+
import type { SessionContext } from "./context.js";
|
|
7
|
+
export { type SessionContext, createSessionContext } from "./context.js";
|
|
8
|
+
export { type TransactionResult, type Connection, Network, TransactionResultType, createSessionConnection, } from "./connection.js";
|
|
7
9
|
type EstablishSessionOptions = {
|
|
8
|
-
|
|
10
|
+
context: SessionContext;
|
|
9
11
|
walletPublicKey: PublicKey;
|
|
10
12
|
signMessage: (message: Uint8Array) => Promise<Uint8Array>;
|
|
11
13
|
expires: Date;
|
|
@@ -19,7 +21,7 @@ type EstablishSessionOptions = {
|
|
|
19
21
|
});
|
|
20
22
|
export declare const establishSession: (options: EstablishSessionOptions) => Promise<EstablishSessionResult>;
|
|
21
23
|
export declare const replaceSession: (options: {
|
|
22
|
-
|
|
24
|
+
context: SessionContext;
|
|
23
25
|
session: Session;
|
|
24
26
|
signMessage: (message: Uint8Array) => Promise<Uint8Array>;
|
|
25
27
|
expires: Date;
|
|
@@ -31,10 +33,10 @@ export declare const replaceSession: (options: {
|
|
|
31
33
|
unlimited: true;
|
|
32
34
|
})) => Promise<EstablishSessionResult>;
|
|
33
35
|
export declare const revokeSession: (options: {
|
|
34
|
-
|
|
36
|
+
context: SessionContext;
|
|
35
37
|
session: Session;
|
|
36
38
|
}) => Promise<TransactionResult | undefined>;
|
|
37
|
-
export declare const reestablishSession: (
|
|
39
|
+
export declare const reestablishSession: (context: SessionContext, walletPublicKey: PublicKey, sessionKey: CryptoKeyPair) => Promise<Session | undefined>;
|
|
38
40
|
export declare const getSessionAccount: (connection: Connection, sessionPublicKey: PublicKey) => Promise<{
|
|
39
41
|
authorizedPrograms: {
|
|
40
42
|
type: AuthorizedProgramsType.All;
|
|
@@ -1302,11 +1304,11 @@ export type Session = {
|
|
|
1302
1304
|
sessionKey: CryptoKeyPair;
|
|
1303
1305
|
walletPublicKey: PublicKey;
|
|
1304
1306
|
payer: PublicKey;
|
|
1305
|
-
sendTransaction: (instructions: Parameters<
|
|
1307
|
+
sendTransaction: (instructions: Parameters<SessionContext["sendTransaction"]>[1]) => Promise<TransactionResult>;
|
|
1306
1308
|
sessionInfo: NonNullable<z.infer<typeof sessionInfoSchema>>;
|
|
1307
1309
|
};
|
|
1308
1310
|
type SendTransferOptions = {
|
|
1309
|
-
|
|
1311
|
+
context: SessionContext;
|
|
1310
1312
|
walletPublicKey: PublicKey;
|
|
1311
1313
|
signMessage: (message: Uint8Array) => Promise<Uint8Array>;
|
|
1312
1314
|
mint: PublicKey;
|
package/esm/index.js
CHANGED
|
@@ -11,9 +11,10 @@ import { Ed25519Program, PublicKey } from "@solana/web3.js";
|
|
|
11
11
|
import BN from "bn.js";
|
|
12
12
|
import bs58 from "bs58";
|
|
13
13
|
import { z } from "zod";
|
|
14
|
-
import { TransactionResultType } from "./
|
|
14
|
+
import { TransactionResultType } from "./connection.js";
|
|
15
15
|
import { importKey, signMessageWithKey, verifyMessageWithKey, } from "./crypto.js";
|
|
16
|
-
export {
|
|
16
|
+
export { createSessionContext } from "./context.js";
|
|
17
|
+
export { Network, TransactionResultType, createSessionConnection, } from "./connection.js";
|
|
17
18
|
const MESSAGE_HEADER = `Fogo Sessions:
|
|
18
19
|
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.
|
|
19
20
|
`;
|
|
@@ -36,7 +37,7 @@ export const establishSession = async (options) => {
|
|
|
36
37
|
else {
|
|
37
38
|
const filteredLimits = new Map(options.limits?.entries().filter(([, amount]) => amount > 0n));
|
|
38
39
|
const tokenInfo = filteredLimits.size > 0
|
|
39
|
-
? await getTokenInfo(options.
|
|
40
|
+
? await getTokenInfo(options.context, filteredLimits)
|
|
40
41
|
: [];
|
|
41
42
|
const [intentInstruction, startSessionInstruction] = await Promise.all([
|
|
42
43
|
buildIntentInstruction(options, sessionKey, tokenInfo),
|
|
@@ -50,10 +51,10 @@ export const establishSession = async (options) => {
|
|
|
50
51
|
}
|
|
51
52
|
};
|
|
52
53
|
const sendSessionEstablishTransaction = async (options, sessionKey, instructions) => {
|
|
53
|
-
const result = await options.
|
|
54
|
+
const result = await options.context.sendTransaction(sessionKey, instructions);
|
|
54
55
|
switch (result.type) {
|
|
55
56
|
case TransactionResultType.Success: {
|
|
56
|
-
const session = await createSession(options.
|
|
57
|
+
const session = await createSession(options.context, options.walletPublicKey, sessionKey);
|
|
57
58
|
return session
|
|
58
59
|
? EstablishSessionResult.Success(result.signature, session)
|
|
59
60
|
: EstablishSessionResult.Failed(result.signature, new Error("Transaction succeeded, but the session was not created"));
|
|
@@ -69,14 +70,14 @@ export const replaceSession = async (options) => establishSession({
|
|
|
69
70
|
});
|
|
70
71
|
export const revokeSession = async (options) => {
|
|
71
72
|
if (options.session.sessionInfo.minor >= 2) {
|
|
72
|
-
const instruction = await new SessionManagerProgram(new AnchorProvider(options.
|
|
73
|
+
const instruction = await new SessionManagerProgram(new AnchorProvider(options.context.connection, {}, {})).methods
|
|
73
74
|
.revokeSession()
|
|
74
75
|
.accounts({
|
|
75
76
|
sponsor: options.session.sessionInfo.sponsor,
|
|
76
77
|
session: options.session.sessionPublicKey,
|
|
77
78
|
})
|
|
78
79
|
.instruction();
|
|
79
|
-
return options.
|
|
80
|
+
return options.context.sendTransaction(options.session.sessionKey, [
|
|
80
81
|
instruction,
|
|
81
82
|
]);
|
|
82
83
|
}
|
|
@@ -84,24 +85,24 @@ export const revokeSession = async (options) => {
|
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
86
87
|
};
|
|
87
|
-
export const reestablishSession = async (
|
|
88
|
+
export const reestablishSession = async (context, walletPublicKey, sessionKey) => createSession(context, walletPublicKey, sessionKey);
|
|
88
89
|
export const getSessionAccount = async (connection, sessionPublicKey) => {
|
|
89
90
|
const result = await connection.getAccountInfo(sessionPublicKey, "confirmed");
|
|
90
91
|
return result === null
|
|
91
92
|
? undefined
|
|
92
93
|
: sessionInfoSchema.parse(new BorshAccountsCoder(SessionManagerIdl).decode("Session", result.data));
|
|
93
94
|
};
|
|
94
|
-
const createSession = async (
|
|
95
|
+
const createSession = async (context, walletPublicKey, sessionKey) => {
|
|
95
96
|
const sessionPublicKey = new PublicKey(await getAddressFromPublicKey(sessionKey.publicKey));
|
|
96
|
-
const sessionInfo = await getSessionAccount(
|
|
97
|
+
const sessionInfo = await getSessionAccount(context.connection, sessionPublicKey);
|
|
97
98
|
return sessionInfo === undefined
|
|
98
99
|
? undefined
|
|
99
100
|
: {
|
|
100
101
|
sessionPublicKey,
|
|
101
102
|
walletPublicKey,
|
|
102
103
|
sessionKey,
|
|
103
|
-
payer:
|
|
104
|
-
sendTransaction: (instructions) =>
|
|
104
|
+
payer: context.payer,
|
|
105
|
+
sendTransaction: (instructions) => context.sendTransaction(sessionKey, instructions),
|
|
105
106
|
sessionInfo,
|
|
106
107
|
};
|
|
107
108
|
};
|
|
@@ -286,13 +287,13 @@ const SymbolOrMint = {
|
|
|
286
287
|
mint,
|
|
287
288
|
}),
|
|
288
289
|
};
|
|
289
|
-
const getTokenInfo = async (
|
|
290
|
-
const umi = createUmi(
|
|
290
|
+
const getTokenInfo = async (context, limits) => {
|
|
291
|
+
const umi = createUmi(context.connection.rpcEndpoint);
|
|
291
292
|
return Promise.all(limits.entries().map(async ([mint, amount]) => {
|
|
292
293
|
const metaplexMint = metaplexPublicKey(mint.toBase58());
|
|
293
294
|
const metadataAddress = findMetadataPda(umi, { mint: metaplexMint })[0];
|
|
294
295
|
const [mintInfo, metadata] = await Promise.all([
|
|
295
|
-
getMint(
|
|
296
|
+
getMint(context.connection, mint),
|
|
296
297
|
safeFetchMetadata(umi, metadataAddress),
|
|
297
298
|
]);
|
|
298
299
|
return {
|
|
@@ -338,8 +339,8 @@ const addOffchainMessagePrefixToMessageIfNeeded = async (walletPublicKey, signat
|
|
|
338
339
|
};
|
|
339
340
|
const buildIntentInstruction = async (options, sessionKey, tokens) => {
|
|
340
341
|
const message = await buildMessage({
|
|
341
|
-
chainId: options.
|
|
342
|
-
domain: options.
|
|
342
|
+
chainId: options.context.chainId,
|
|
343
|
+
domain: options.context.domain,
|
|
343
344
|
sessionKey,
|
|
344
345
|
expires: options.expires,
|
|
345
346
|
tokens,
|
|
@@ -404,18 +405,18 @@ const amountToString = (amount, decimals) => {
|
|
|
404
405
|
...(decimalTruncated === "" ? [] : [".", decimalTruncated]),
|
|
405
406
|
].join("");
|
|
406
407
|
};
|
|
407
|
-
const buildCreateAssociatedTokenAccountInstructions = (options, tokens) => tokens.map(({ mint }) => createAssociatedTokenAccountIdempotentInstruction(options.
|
|
408
|
+
const buildCreateAssociatedTokenAccountInstructions = (options, tokens) => tokens.map(({ mint }) => createAssociatedTokenAccountIdempotentInstruction(options.context.payer, getAssociatedTokenAddressSync(mint, options.walletPublicKey), options.walletPublicKey, mint));
|
|
408
409
|
export const getDomainRecordAddress = (domain) => {
|
|
409
410
|
const hash = sha256(domain);
|
|
410
411
|
return PublicKey.findProgramAddressSync([Buffer.from("domain-record"), hash], new PublicKey(DomainRegistryIdl.address))[0];
|
|
411
412
|
};
|
|
412
413
|
const buildStartSessionInstruction = async (options, sessionKey, tokens) => {
|
|
413
|
-
const instruction = new SessionManagerProgram(new AnchorProvider(options.
|
|
414
|
+
const instruction = new SessionManagerProgram(new AnchorProvider(options.context.connection, {}, {})).methods
|
|
414
415
|
.startSession()
|
|
415
416
|
.accounts({
|
|
416
|
-
sponsor: options.
|
|
417
|
+
sponsor: options.context.payer,
|
|
417
418
|
session: await getAddressFromPublicKey(sessionKey.publicKey),
|
|
418
|
-
domainRegistry: getDomainRecordAddress(options.
|
|
419
|
+
domainRegistry: getDomainRecordAddress(options.context.domain),
|
|
419
420
|
});
|
|
420
421
|
return tokens === undefined
|
|
421
422
|
? instruction.instruction()
|
|
@@ -466,14 +467,14 @@ Signing this intent will transfer the tokens as described below.
|
|
|
466
467
|
export const sendTransfer = async (options) => {
|
|
467
468
|
const sourceAta = getAssociatedTokenAddressSync(options.mint, options.walletPublicKey);
|
|
468
469
|
const destinationAta = getAssociatedTokenAddressSync(options.mint, options.recipient);
|
|
469
|
-
const program = new IntentTransferProgram(new AnchorProvider(options.
|
|
470
|
-
const umi = createUmi(options.
|
|
470
|
+
const program = new IntentTransferProgram(new AnchorProvider(options.context.connection, {}, {}));
|
|
471
|
+
const umi = createUmi(options.context.connection.rpcEndpoint);
|
|
471
472
|
const metaplexMint = metaplexPublicKey(options.mint.toBase58());
|
|
472
473
|
const metadataAddress = findMetadataPda(umi, { mint: metaplexMint })[0];
|
|
473
474
|
const metadata = await safeFetchMetadata(umi, metadataAddress);
|
|
474
475
|
const symbol = metadata?.symbol ?? undefined;
|
|
475
|
-
return options.
|
|
476
|
-
createAssociatedTokenAccountIdempotentInstruction(options.
|
|
476
|
+
return options.context.sendTransaction(undefined, [
|
|
477
|
+
createAssociatedTokenAccountIdempotentInstruction(options.context.payer, destinationAta, options.recipient, options.mint),
|
|
477
478
|
await buildTransferIntentInstruction(program, options, symbol),
|
|
478
479
|
await program.methods
|
|
479
480
|
.sendTokens()
|
|
@@ -481,7 +482,7 @@ export const sendTransfer = async (options) => {
|
|
|
481
482
|
destination: destinationAta,
|
|
482
483
|
mint: options.mint,
|
|
483
484
|
source: sourceAta,
|
|
484
|
-
sponsor: options.
|
|
485
|
+
sponsor: options.context.payer,
|
|
485
486
|
// eslint-disable-next-line unicorn/no-null
|
|
486
487
|
metadata: symbol === undefined ? null : new PublicKey(metadataAddress),
|
|
487
488
|
})
|
|
@@ -491,13 +492,13 @@ export const sendTransfer = async (options) => {
|
|
|
491
492
|
const buildTransferIntentInstruction = async (program, options, symbol) => {
|
|
492
493
|
const [nonce, { decimals }] = await Promise.all([
|
|
493
494
|
getNonce(program, options.walletPublicKey),
|
|
494
|
-
getMint(options.
|
|
495
|
+
getMint(options.context.connection, options.mint),
|
|
495
496
|
]);
|
|
496
497
|
const message = new TextEncoder().encode([
|
|
497
498
|
TRANSFER_MESSAGE_HEADER,
|
|
498
499
|
serializeKV({
|
|
499
500
|
version: `${CURRENT_INTENT_TRANSFER_MAJOR}.${CURRENT_INTENT_TRANSFER_MINOR}`,
|
|
500
|
-
chain_id: options.
|
|
501
|
+
chain_id: options.context.chainId,
|
|
501
502
|
token: symbol ?? options.mint.toBase58(),
|
|
502
503
|
amount: amountToString(options.amount, decimals),
|
|
503
504
|
recipient: options.recipient.toBase58(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fogo/sessions-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "A set of utilities for integrating with Fogo sessions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fogo",
|
|
@@ -39,6 +39,6 @@
|
|
|
39
39
|
"bn.js": "^5.1.2",
|
|
40
40
|
"bs58": "^6.0.0",
|
|
41
41
|
"zod": "^3.25.62",
|
|
42
|
-
"@fogo/sessions-idls": "^0.0.
|
|
42
|
+
"@fogo/sessions-idls": "^0.0.7"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/cjs/adapter.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { Transaction, Instruction, TransactionWithLifetime } from "@solana/kit";
|
|
2
|
-
import type { Connection, TransactionError } from "@solana/web3.js";
|
|
3
|
-
import { PublicKey, TransactionInstruction, VersionedTransaction } from "@solana/web3.js";
|
|
4
|
-
export type SessionAdapter = {
|
|
5
|
-
chainId: string;
|
|
6
|
-
connection: Connection;
|
|
7
|
-
payer: PublicKey;
|
|
8
|
-
domain: string;
|
|
9
|
-
sendTransaction: (sessionKey: CryptoKeyPair | undefined, instructions: (TransactionInstruction | Instruction)[] | VersionedTransaction | (Transaction & TransactionWithLifetime)) => Promise<TransactionResult>;
|
|
10
|
-
};
|
|
11
|
-
export declare enum TransactionResultType {
|
|
12
|
-
Success = 0,
|
|
13
|
-
Failed = 1
|
|
14
|
-
}
|
|
15
|
-
declare const TransactionResult: {
|
|
16
|
-
Success: (signature: string) => {
|
|
17
|
-
type: TransactionResultType.Success;
|
|
18
|
-
signature: string;
|
|
19
|
-
};
|
|
20
|
-
Failed: (signature: string, error: TransactionError) => {
|
|
21
|
-
type: TransactionResultType.Failed;
|
|
22
|
-
signature: string;
|
|
23
|
-
error: TransactionError;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
export type TransactionResult = ReturnType<(typeof TransactionResult)[keyof typeof TransactionResult]>;
|
|
27
|
-
export declare const createSolanaWalletAdapter: (options: {
|
|
28
|
-
connection: Connection;
|
|
29
|
-
addressLookupTableAddress?: string | undefined;
|
|
30
|
-
domain?: string | undefined;
|
|
31
|
-
} & ({
|
|
32
|
-
paymaster?: string | URL | undefined;
|
|
33
|
-
} | {
|
|
34
|
-
sendToPaymaster: (transaction: Transaction) => Promise<TransactionResult>;
|
|
35
|
-
sponsor: PublicKey;
|
|
36
|
-
})) => Promise<SessionAdapter>;
|
|
37
|
-
export {};
|