@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.
- package/cjs/connection.d.ts +9 -6
- package/cjs/connection.js +40 -34
- package/cjs/context.d.ts +7 -2
- package/cjs/context.js +10 -5
- package/cjs/index.d.ts +21 -5
- package/cjs/index.js +100 -34
- package/esm/connection.d.ts +9 -6
- package/esm/connection.js +40 -34
- package/esm/context.d.ts +7 -2
- package/esm/context.js +9 -4
- package/esm/index.d.ts +21 -5
- package/esm/index.js +98 -34
- package/package.json +2 -2
package/cjs/connection.d.ts
CHANGED
|
@@ -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:
|
|
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,
|
|
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,
|
|
48
|
-
|
|
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 (
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
103
|
+
return connection.sendToPaymaster(transaction);
|
|
100
104
|
}
|
|
101
105
|
};
|
|
102
|
-
const buildTransaction = async (connection,
|
|
103
|
-
const [
|
|
104
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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,
|
|
21
|
-
|
|
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:
|
|
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
|
|
29
|
-
Object.defineProperty(exports, "createSessionContext", { enumerable: true, get: function () { return
|
|
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 = "
|
|
43
|
+
const CURRENT_INTENT_TRANSFER_MINOR = "2";
|
|
43
44
|
const CURRENT_BRIDGE_OUT_MAJOR = "0";
|
|
44
|
-
const CURRENT_BRIDGE_OUT_MINOR = "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
(
|
|
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
|
-
|
|
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.
|
|
565
|
+
sponsor: options.context.internalPayer,
|
|
566
|
+
metadata:
|
|
527
567
|
// eslint-disable-next-line unicorn/no-null
|
|
528
|
-
|
|
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
|
-
|
|
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
|
-
|
|
579
|
-
|
|
580
|
-
signedQuoteBytes: Buffer.from(quote.signedQuote),
|
|
625
|
+
payDestinationAtaRent: !destinationAtaExists,
|
|
626
|
+
signedQuoteBytes: [...quote.signedQuote],
|
|
581
627
|
})
|
|
582
628
|
.accounts({
|
|
583
|
-
sponsor: options.context.
|
|
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:
|
|
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"));
|
package/esm/connection.d.ts
CHANGED
|
@@ -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:
|
|
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,
|
|
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,
|
|
45
|
-
|
|
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 (
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
99
|
+
return connection.sendToPaymaster(transaction);
|
|
96
100
|
}
|
|
97
101
|
};
|
|
98
|
-
const buildTransaction = async (connection,
|
|
99
|
-
const [
|
|
100
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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,
|
|
18
|
-
|
|
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:
|
|
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 {
|
|
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 = "
|
|
33
|
+
const CURRENT_INTENT_TRANSFER_MINOR = "2";
|
|
33
34
|
const CURRENT_BRIDGE_OUT_MAJOR = "0";
|
|
34
|
-
const CURRENT_BRIDGE_OUT_MINOR = "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
547
|
+
sponsor: options.context.internalPayer,
|
|
548
|
+
metadata:
|
|
511
549
|
// eslint-disable-next-line unicorn/no-null
|
|
512
|
-
|
|
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
|
-
|
|
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
|
-
|
|
562
|
-
|
|
563
|
-
signedQuoteBytes: Buffer.from(quote.signedQuote),
|
|
606
|
+
payDestinationAtaRent: !destinationAtaExists,
|
|
607
|
+
signedQuoteBytes: [...quote.signedQuote],
|
|
564
608
|
})
|
|
565
609
|
.accounts({
|
|
566
|
-
sponsor: options.context.
|
|
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:
|
|
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.
|
|
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.
|
|
47
|
+
"@fogo/sessions-idls": "^0.0.9"
|
|
48
48
|
}
|
|
49
49
|
}
|