@fogo/sessions-sdk 0.0.28 → 0.0.30
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 +2 -2
- package/cjs/index.d.ts +35 -9
- package/cjs/index.js +82 -101
- package/cjs/instructions.d.ts +18 -0
- package/cjs/instructions.js +56 -0
- package/esm/connection.d.ts +2 -2
- package/esm/connection.js +2 -2
- package/esm/context.js +1 -1
- package/esm/index.d.ts +35 -9
- package/esm/index.js +80 -100
- package/esm/instructions.d.ts +18 -0
- package/esm/instructions.js +51 -0
- package/package.json +4 -4
package/cjs/connection.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GetLatestBlockhashApi, Instruction, Rpc, Transaction, TransactionWithLifetime } from "@solana/kit";
|
|
2
2
|
import type { TransactionError } from "@solana/web3.js";
|
|
3
|
-
import { Keypair,
|
|
3
|
+
import { Keypair, PublicKey, TransactionInstruction, VersionedTransaction, Connection as Web3Connection } from "@solana/web3.js";
|
|
4
4
|
export declare enum Network {
|
|
5
5
|
Testnet = 0,
|
|
6
6
|
Mainnet = 1
|
package/cjs/index.d.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { TransactionError } from "@solana/web3.js";
|
|
1
|
+
import type { TransactionError, TransactionInstruction, VersionedTransaction } from "@solana/web3.js";
|
|
3
2
|
import { Connection, PublicKey } from "@solana/web3.js";
|
|
4
3
|
import type { Chain } from "@wormhole-foundation/sdk";
|
|
5
4
|
import BN from "bn.js";
|
|
6
5
|
import { z } from "zod";
|
|
7
6
|
import type { TransactionOrInstructions, TransactionResult } from "./connection.js";
|
|
8
7
|
import type { SendTransactionOptions, SessionContext } from "./context.js";
|
|
9
|
-
export { type
|
|
10
|
-
export {
|
|
8
|
+
export { type Connection, createSessionConnection, Network, type TransactionOrInstructions, type TransactionResult, TransactionResultType, } from "./connection.js";
|
|
9
|
+
export { createSessionContext, type SendTransactionOptions, type SessionContext, } from "./context.js";
|
|
11
10
|
type EstablishSessionOptions = {
|
|
12
11
|
context: SessionContext;
|
|
13
12
|
walletPublicKey: PublicKey;
|
|
14
|
-
signMessage: (message: Uint8Array) => Promise<
|
|
13
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
14
|
+
signedMessage: Uint8Array;
|
|
15
|
+
signature: Uint8Array;
|
|
16
|
+
}>;
|
|
15
17
|
expires: Date;
|
|
16
18
|
extra?: Record<string, string> | undefined;
|
|
17
19
|
createUnsafeExtractableSessionKey?: boolean | undefined;
|
|
@@ -26,7 +28,10 @@ export declare const establishSession: (options: EstablishSessionOptions) => Pro
|
|
|
26
28
|
export declare const replaceSession: (options: {
|
|
27
29
|
context: SessionContext;
|
|
28
30
|
session: Session;
|
|
29
|
-
signMessage: (message: Uint8Array) => Promise<
|
|
31
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
32
|
+
signedMessage: Uint8Array;
|
|
33
|
+
signature: Uint8Array;
|
|
34
|
+
}>;
|
|
30
35
|
expires: Date;
|
|
31
36
|
extra?: Record<string, string> | undefined;
|
|
32
37
|
} & ({
|
|
@@ -2223,6 +2228,9 @@ export type Session = {
|
|
|
2223
2228
|
sessionKey: CryptoKeyPair;
|
|
2224
2229
|
walletPublicKey: PublicKey;
|
|
2225
2230
|
payer: PublicKey;
|
|
2231
|
+
getSystemProgramSessionWrapInstruction: (amount: bigint) => TransactionInstruction;
|
|
2232
|
+
getSessionWrapInstructions: (amount: bigint) => TransactionInstruction[];
|
|
2233
|
+
getSessionUnwrapInstructions: () => TransactionInstruction[];
|
|
2226
2234
|
sendTransaction: (instructions: TransactionOrInstructions, extraConfig?: SendTransactionOptions) => Promise<TransactionResult>;
|
|
2227
2235
|
sessionInfo: NonNullable<z.infer<typeof sessionInfoSchema>>;
|
|
2228
2236
|
};
|
|
@@ -2243,19 +2251,37 @@ export declare const getBridgeOutFee: (context: SessionContext) => Promise<{
|
|
|
2243
2251
|
type SendTransferOptions = {
|
|
2244
2252
|
context: SessionContext;
|
|
2245
2253
|
walletPublicKey: PublicKey;
|
|
2246
|
-
signMessage: (message: Uint8Array) => Promise<
|
|
2254
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
2255
|
+
signedMessage: Uint8Array;
|
|
2256
|
+
signature: Uint8Array;
|
|
2257
|
+
}>;
|
|
2247
2258
|
mint: PublicKey;
|
|
2248
2259
|
amount: bigint;
|
|
2249
2260
|
recipient: PublicKey;
|
|
2250
2261
|
feeConfig: Awaited<ReturnType<typeof getTransferFee>>;
|
|
2251
2262
|
};
|
|
2252
2263
|
export declare const sendTransfer: (options: SendTransferOptions) => Promise<TransactionResult>;
|
|
2264
|
+
type SendNativeTransferOptions = {
|
|
2265
|
+
context: SessionContext;
|
|
2266
|
+
walletPublicKey: PublicKey;
|
|
2267
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
2268
|
+
signedMessage: Uint8Array;
|
|
2269
|
+
signature: Uint8Array;
|
|
2270
|
+
}>;
|
|
2271
|
+
amount: bigint;
|
|
2272
|
+
recipient: PublicKey;
|
|
2273
|
+
feeConfig: Awaited<ReturnType<typeof getTransferFee>>;
|
|
2274
|
+
};
|
|
2275
|
+
export declare const sendNativeTransfer: (options: SendNativeTransferOptions) => Promise<TransactionResult>;
|
|
2253
2276
|
type SendBridgeOutOptions = {
|
|
2254
2277
|
context: SessionContext;
|
|
2255
2278
|
sessionKey: CryptoKeyPair;
|
|
2256
2279
|
sessionPublicKey: PublicKey;
|
|
2257
2280
|
walletPublicKey: PublicKey;
|
|
2258
|
-
|
|
2281
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
2282
|
+
signedMessage: Uint8Array;
|
|
2283
|
+
signature: Uint8Array;
|
|
2284
|
+
}>;
|
|
2259
2285
|
amount: bigint;
|
|
2260
2286
|
fromToken: WormholeToken & {
|
|
2261
2287
|
chain: "Fogo";
|
|
@@ -2275,7 +2301,7 @@ export declare const bridgeOut: (options: SendBridgeOutOptions) => Promise<Trans
|
|
|
2275
2301
|
type SendBridgeInOptions = {
|
|
2276
2302
|
context: SessionContext;
|
|
2277
2303
|
walletPublicKey: PublicKey;
|
|
2278
|
-
|
|
2304
|
+
signTransaction: (transaction: VersionedTransaction) => Promise<VersionedTransaction>;
|
|
2279
2305
|
amount: bigint;
|
|
2280
2306
|
fromToken: WormholeToken & {
|
|
2281
2307
|
chain: "Solana";
|
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.getBridgeOutFee = exports.getTransferFee = exports.SessionResultType = exports.getDomainRecordAddress = exports.AuthorizedTokens = exports.AuthorizedProgramsType = exports.getSessionAccount = exports.reestablishSession = exports.revokeSession = exports.replaceSession = exports.establishSession = exports.
|
|
6
|
+
exports.verifyLogInToken = exports.createLogInToken = exports.bridgeIn = exports.bridgeOut = exports.sendNativeTransfer = exports.sendTransfer = exports.getBridgeOutFee = exports.getTransferFee = exports.SessionResultType = exports.getDomainRecordAddress = exports.AuthorizedTokens = exports.AuthorizedProgramsType = exports.getSessionAccount = exports.reestablishSession = exports.revokeSession = exports.replaceSession = exports.establishSession = exports.createSessionContext = exports.TransactionResultType = exports.Network = exports.createSessionConnection = void 0;
|
|
7
7
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
8
8
|
const sessions_idls_1 = require("@fogo/sessions-idls");
|
|
9
9
|
const mpl_token_metadata_1 = require("@metaplex-foundation/mpl-token-metadata");
|
|
@@ -26,12 +26,13 @@ const zod_1 = require("zod");
|
|
|
26
26
|
const connection_js_1 = require("./connection.js");
|
|
27
27
|
const context_js_1 = require("./context.js");
|
|
28
28
|
const crypto_js_1 = require("./crypto.js");
|
|
29
|
-
|
|
30
|
-
Object.defineProperty(exports, "createSessionContext", { enumerable: true, get: function () { return context_js_2.createSessionContext; } });
|
|
29
|
+
const instructions_js_1 = require("./instructions.js");
|
|
31
30
|
var connection_js_2 = require("./connection.js");
|
|
31
|
+
Object.defineProperty(exports, "createSessionConnection", { enumerable: true, get: function () { return connection_js_2.createSessionConnection; } });
|
|
32
32
|
Object.defineProperty(exports, "Network", { enumerable: true, get: function () { return connection_js_2.Network; } });
|
|
33
33
|
Object.defineProperty(exports, "TransactionResultType", { enumerable: true, get: function () { return connection_js_2.TransactionResultType; } });
|
|
34
|
-
|
|
34
|
+
var context_js_2 = require("./context.js");
|
|
35
|
+
Object.defineProperty(exports, "createSessionContext", { enumerable: true, get: function () { return context_js_2.createSessionContext; } });
|
|
35
36
|
const MESSAGE_HEADER = `Fogo Sessions:
|
|
36
37
|
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.
|
|
37
38
|
`;
|
|
@@ -53,7 +54,7 @@ const establishSession = async (options) => {
|
|
|
53
54
|
: await (0, kit_1.generateKeyPair)();
|
|
54
55
|
if (options.unlimited) {
|
|
55
56
|
return sendSessionEstablishTransaction(options, sessionKey, await Promise.all([
|
|
56
|
-
|
|
57
|
+
buildStartSessionIntentInstruction(options, sessionKey),
|
|
57
58
|
buildStartSessionInstruction(options, sessionKey),
|
|
58
59
|
]), options.sessionEstablishmentLookupTable);
|
|
59
60
|
}
|
|
@@ -63,7 +64,7 @@ const establishSession = async (options) => {
|
|
|
63
64
|
? await getTokenInfo(options.context, filteredLimits)
|
|
64
65
|
: [];
|
|
65
66
|
const [intentInstruction, startSessionInstruction] = await Promise.all([
|
|
66
|
-
|
|
67
|
+
buildStartSessionIntentInstruction(options, sessionKey, tokenInfo),
|
|
67
68
|
buildStartSessionInstruction(options, sessionKey, tokenInfo),
|
|
68
69
|
]);
|
|
69
70
|
return sendSessionEstablishTransaction(options, sessionKey, [intentInstruction, startSessionInstruction], options.sessionEstablishmentLookupTable);
|
|
@@ -130,6 +131,11 @@ const createSession = async (context, walletPublicKey, sessionKey) => {
|
|
|
130
131
|
walletPublicKey,
|
|
131
132
|
sessionKey,
|
|
132
133
|
payer: context.payer,
|
|
134
|
+
getSystemProgramSessionWrapInstruction: (amount) => (0, instructions_js_1.createSystemProgramSessionWrapInstruction)(sessionPublicKey, walletPublicKey, amount),
|
|
135
|
+
getSessionWrapInstructions: (amount) => (0, instructions_js_1.createSessionWrapInstructions)(sessionPublicKey, walletPublicKey, amount),
|
|
136
|
+
getSessionUnwrapInstructions: () => [
|
|
137
|
+
(0, instructions_js_1.createSessionUnwrapInstruction)(sessionPublicKey, walletPublicKey),
|
|
138
|
+
],
|
|
133
139
|
sendTransaction: (instructions, extraConfig) => context.sendTransaction(sessionKey, instructions, extraConfig),
|
|
134
140
|
sessionInfo,
|
|
135
141
|
};
|
|
@@ -354,7 +360,7 @@ const SymbolOrMint = {
|
|
|
354
360
|
mint,
|
|
355
361
|
}),
|
|
356
362
|
};
|
|
357
|
-
const getTokenInfo =
|
|
363
|
+
const getTokenInfo = (context, limits) => {
|
|
358
364
|
const umi = (0, umi_bundle_defaults_1.createUmi)(context.connection.rpcEndpoint);
|
|
359
365
|
return Promise.all(limits.entries().map(async ([mint, amount]) => {
|
|
360
366
|
const metaplexMint = (0, umi_1.publicKey)(mint.toBase58());
|
|
@@ -374,64 +380,23 @@ const getTokenInfo = async (context, limits) => {
|
|
|
374
380
|
};
|
|
375
381
|
}));
|
|
376
382
|
};
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
// Source: https://github.com/anza-xyz/solana-sdk/blob/master/offchain-message/src/lib.rs#L162
|
|
390
|
-
const messageWithOffchainMessagePrefix = Uint8Array.from([
|
|
391
|
-
// eslint-disable-next-line unicorn/number-literal-case
|
|
392
|
-
0xff,
|
|
393
|
-
...new TextEncoder().encode("solana offchain"),
|
|
394
|
-
0,
|
|
395
|
-
1,
|
|
396
|
-
...serializeU16LE(message.length),
|
|
397
|
-
...message,
|
|
398
|
-
]);
|
|
399
|
-
if (await (0, kit_1.verifySignature)(publicKey, signature, messageWithOffchainMessagePrefix)) {
|
|
400
|
-
return messageWithOffchainMessagePrefix;
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
throw new Error("The signature provided by the browser wallet is not valid");
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
const buildIntentInstruction = async (options, sessionKey, tokens) => {
|
|
408
|
-
const message = await buildMessage({
|
|
409
|
-
chainId: options.context.chainId,
|
|
410
|
-
domain: options.context.domain,
|
|
411
|
-
sessionKey,
|
|
412
|
-
expires: options.expires,
|
|
413
|
-
tokens,
|
|
414
|
-
extra: options.extra,
|
|
415
|
-
});
|
|
416
|
-
const intentSignature = (0, kit_1.signatureBytes)(await options.signMessage(message));
|
|
383
|
+
const buildStartSessionIntentInstruction = async (options, sessionKey, tokens) => buildIntentInstruction(options, MESSAGE_HEADER, {
|
|
384
|
+
version: `${CURRENT_MAJOR}.${CURRENT_MINOR}`,
|
|
385
|
+
chain_id: options.context.chainId,
|
|
386
|
+
domain: options.context.domain,
|
|
387
|
+
expires: options.expires.toISOString(),
|
|
388
|
+
session_key: await (0, kit_1.getAddressFromPublicKey)(sessionKey.publicKey),
|
|
389
|
+
tokens: serializeTokenList(tokens),
|
|
390
|
+
});
|
|
391
|
+
const buildIntentInstruction = async (options, header, body, extra) => {
|
|
392
|
+
const message = new TextEncoder().encode([header, serializeKV(body), extra && serializeExtra(extra)].join("\n"));
|
|
393
|
+
const { signature, signedMessage } = await options.signMessage(message);
|
|
417
394
|
return web3_js_1.Ed25519Program.createInstructionWithPublicKey({
|
|
418
395
|
publicKey: options.walletPublicKey.toBytes(),
|
|
419
|
-
signature
|
|
420
|
-
message:
|
|
396
|
+
signature,
|
|
397
|
+
message: signedMessage,
|
|
421
398
|
});
|
|
422
399
|
};
|
|
423
|
-
const buildMessage = async (body) => new TextEncoder().encode([
|
|
424
|
-
MESSAGE_HEADER,
|
|
425
|
-
serializeKV({
|
|
426
|
-
version: `${CURRENT_MAJOR}.${CURRENT_MINOR}`,
|
|
427
|
-
chain_id: body.chainId,
|
|
428
|
-
domain: body.domain,
|
|
429
|
-
expires: body.expires.toISOString(),
|
|
430
|
-
session_key: await (0, kit_1.getAddressFromPublicKey)(body.sessionKey.publicKey),
|
|
431
|
-
tokens: serializeTokenList(body.tokens),
|
|
432
|
-
}),
|
|
433
|
-
body.extra && serializeExtra(body.extra),
|
|
434
|
-
].join("\n"));
|
|
435
400
|
const serializeExtra = (extra) => {
|
|
436
401
|
for (const [key, value] of Object.entries(extra)) {
|
|
437
402
|
if (!/^[a-z]+(_[a-z0-9]+)*$/.test(key)) {
|
|
@@ -618,24 +583,50 @@ const buildTransferIntentInstruction = async (program, options, symbol, feeToken
|
|
|
618
583
|
getNonce(program, options.walletPublicKey, NonceType.Transfer),
|
|
619
584
|
(0, spl_token_1.getMint)(options.context.connection, options.mint),
|
|
620
585
|
]);
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
586
|
+
return buildIntentInstruction(options, TRANSFER_MESSAGE_HEADER, {
|
|
587
|
+
version: `${CURRENT_INTENT_TRANSFER_MAJOR}.${CURRENT_INTENT_TRANSFER_MINOR}`,
|
|
588
|
+
chain_id: options.context.chainId,
|
|
589
|
+
token: symbol ?? options.mint.toBase58(),
|
|
590
|
+
amount: amountToString(options.amount, decimals),
|
|
591
|
+
recipient: options.recipient.toBase58(),
|
|
592
|
+
fee_token: feeToken,
|
|
593
|
+
fee_amount: feeAmount,
|
|
594
|
+
nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
|
|
595
|
+
});
|
|
596
|
+
};
|
|
597
|
+
const sendNativeTransfer = async (options) => {
|
|
598
|
+
const program = new sessions_idls_1.IntentTransferProgram(new anchor_1.AnchorProvider(options.context.connection, {}, {}));
|
|
599
|
+
return options.context.sendTransaction(undefined, [
|
|
600
|
+
await buildNativeTransferIntentInstruction(program, options, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
|
|
601
|
+
await program.methods
|
|
602
|
+
.sendNative()
|
|
603
|
+
.accounts({
|
|
604
|
+
feeMetadata: options.feeConfig.metadata,
|
|
605
|
+
feeMint: options.feeConfig.mint,
|
|
606
|
+
feeSource: (0, spl_token_1.getAssociatedTokenAddressSync)(options.feeConfig.mint, options.walletPublicKey),
|
|
607
|
+
source: options.walletPublicKey,
|
|
608
|
+
destination: options.recipient,
|
|
609
|
+
sponsor: options.context.internalPayer,
|
|
610
|
+
})
|
|
611
|
+
.instruction(),
|
|
612
|
+
], {
|
|
613
|
+
variation: "Intent Transfer",
|
|
614
|
+
paymasterDomain: context_js_1.SESSIONS_INTERNAL_PAYMASTER_DOMAIN,
|
|
615
|
+
});
|
|
616
|
+
};
|
|
617
|
+
exports.sendNativeTransfer = sendNativeTransfer;
|
|
618
|
+
const FOGO_DECIMALS = 9;
|
|
619
|
+
const buildNativeTransferIntentInstruction = async (program, options, feeToken, feeAmount) => {
|
|
620
|
+
const nonce = await getNonce(program, options.walletPublicKey, NonceType.Transfer);
|
|
621
|
+
return buildIntentInstruction(options, TRANSFER_MESSAGE_HEADER, {
|
|
622
|
+
version: `${CURRENT_INTENT_TRANSFER_MAJOR}.${CURRENT_INTENT_TRANSFER_MINOR}`,
|
|
623
|
+
chain_id: options.context.chainId,
|
|
624
|
+
token: "FOGO",
|
|
625
|
+
amount: amountToString(options.amount, FOGO_DECIMALS),
|
|
626
|
+
recipient: options.recipient.toBase58(),
|
|
627
|
+
fee_token: feeToken,
|
|
628
|
+
fee_amount: feeAmount,
|
|
629
|
+
nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
|
|
639
630
|
});
|
|
640
631
|
};
|
|
641
632
|
const BRIDGE_OUT_MESSAGE_HEADER = `Fogo Bridge Transfer:
|
|
@@ -738,25 +729,16 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
|
|
|
738
729
|
};
|
|
739
730
|
const buildBridgeOutIntent = async (program, options, decimals, symbol, feeToken, feeAmount) => {
|
|
740
731
|
const nonce = await getNonce(program, options.walletPublicKey, NonceType.Bridge);
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
fee_amount: feeAmount,
|
|
752
|
-
nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
|
|
753
|
-
}),
|
|
754
|
-
].join("\n"));
|
|
755
|
-
const intentSignature = (0, kit_1.signatureBytes)(await options.solanaWallet.signMessage(message));
|
|
756
|
-
return web3_js_1.Ed25519Program.createInstructionWithPublicKey({
|
|
757
|
-
publicKey: options.walletPublicKey.toBytes(),
|
|
758
|
-
signature: intentSignature,
|
|
759
|
-
message: await addOffchainMessagePrefixToMessageIfNeeded(options.walletPublicKey, intentSignature, message),
|
|
732
|
+
return buildIntentInstruction(options, BRIDGE_OUT_MESSAGE_HEADER, {
|
|
733
|
+
version: `${CURRENT_BRIDGE_OUT_MAJOR}.${CURRENT_BRIDGE_OUT_MINOR}`,
|
|
734
|
+
from_chain_id: options.context.chainId,
|
|
735
|
+
to_chain_id: "solana",
|
|
736
|
+
token: symbol ?? options.fromToken.mint.toBase58(),
|
|
737
|
+
amount: amountToString(options.amount, decimals),
|
|
738
|
+
recipient_address: options.walletPublicKey.toBase58(),
|
|
739
|
+
fee_token: feeToken,
|
|
740
|
+
fee_amount: feeAmount,
|
|
741
|
+
nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
|
|
760
742
|
});
|
|
761
743
|
};
|
|
762
744
|
const bridgeIn = async (options) => {
|
|
@@ -770,10 +752,9 @@ const bridgeIn = async (options) => {
|
|
|
770
752
|
address: () => options.walletPublicKey.toBase58(),
|
|
771
753
|
chain: () => "Solana",
|
|
772
754
|
sign: (transactions) => Promise.all(transactions.map(async ({ transaction }) => {
|
|
773
|
-
|
|
774
|
-
const signedTx = await options.solanaWallet.signTransaction(
|
|
755
|
+
const signedTx = await options.signTransaction(
|
|
775
756
|
// Hooray for Wormhole's incomplete typing eh?
|
|
776
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
757
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
777
758
|
transaction.transaction);
|
|
778
759
|
// Hooray for Wormhole's incomplete typing eh?
|
|
779
760
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type PublicKey, TransactionInstruction } from "@solana/web3.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates the system program instruction `SessionWrap`, only available on Fogo, which allows a session key to transfer native token from its user's wallet to its user's wrapped token associated token account.
|
|
4
|
+
* This instruction may be combined with the `CreateAssociatedTokenAccountIdempotent` and `SyncNative` instructions for a session to wrap tokens on behalf of its user.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createSystemProgramSessionWrapInstruction(sessionKey: PublicKey, walletPublicKey: PublicKey, amount: bigint): TransactionInstruction;
|
|
7
|
+
/**
|
|
8
|
+
* Creates the sequence of instructions required to wrap native tokens within a session.
|
|
9
|
+
*
|
|
10
|
+
* Note: This function sets the session key as the payer for the `CreateAssociatedTokenAccountIdempotent` instruction, which is unconventional since the session key can't spend funds.
|
|
11
|
+
* It works because at the time `CreateAssociatedTokenAccountIdempotent` is called, the `userTokenAccount` has already been funded by the `SessionWrap` instruction.
|
|
12
|
+
* The paymaster will reject the transaction if the payer of the `CreateAssociatedTokenAccountIdempotent` is set to the paymaster payer to avoid the paymaster's funds getting drained.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createSessionWrapInstructions(sessionKey: PublicKey, walletPublicKey: PublicKey, amount: bigint): TransactionInstruction[];
|
|
15
|
+
/**
|
|
16
|
+
* Creates the instruction required to unwrap native tokens within a session.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createSessionUnwrapInstruction(sessionKey: PublicKey, walletPublicKey: PublicKey): TransactionInstruction;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSystemProgramSessionWrapInstruction = createSystemProgramSessionWrapInstruction;
|
|
4
|
+
exports.createSessionWrapInstructions = createSessionWrapInstructions;
|
|
5
|
+
exports.createSessionUnwrapInstruction = createSessionUnwrapInstruction;
|
|
6
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
7
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
+
const SESSION_WRAP_DISCRIMINATOR = 4_000_000;
|
|
9
|
+
function getNativeMintAssociatedTokenAddressSync(walletPublicKey) {
|
|
10
|
+
return (0, spl_token_1.getAssociatedTokenAddressSync)(spl_token_1.NATIVE_MINT, walletPublicKey);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates the system program instruction `SessionWrap`, only available on Fogo, which allows a session key to transfer native token from its user's wallet to its user's wrapped token associated token account.
|
|
14
|
+
* This instruction may be combined with the `CreateAssociatedTokenAccountIdempotent` and `SyncNative` instructions for a session to wrap tokens on behalf of its user.
|
|
15
|
+
*/
|
|
16
|
+
function createSystemProgramSessionWrapInstruction(sessionKey, walletPublicKey, amount) {
|
|
17
|
+
const data = new Uint8Array(12);
|
|
18
|
+
const view = new DataView(data.buffer);
|
|
19
|
+
view.setUint32(0, SESSION_WRAP_DISCRIMINATOR, true);
|
|
20
|
+
view.setBigUint64(4, amount, true);
|
|
21
|
+
return new web3_js_1.TransactionInstruction({
|
|
22
|
+
programId: web3_js_1.SystemProgram.programId,
|
|
23
|
+
keys: [
|
|
24
|
+
{ pubkey: walletPublicKey, isSigner: false, isWritable: true },
|
|
25
|
+
{
|
|
26
|
+
pubkey: getNativeMintAssociatedTokenAddressSync(walletPublicKey),
|
|
27
|
+
isSigner: false,
|
|
28
|
+
isWritable: true,
|
|
29
|
+
},
|
|
30
|
+
{ pubkey: sessionKey, isSigner: true, isWritable: false },
|
|
31
|
+
],
|
|
32
|
+
data: Buffer.from(data),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates the sequence of instructions required to wrap native tokens within a session.
|
|
37
|
+
*
|
|
38
|
+
* Note: This function sets the session key as the payer for the `CreateAssociatedTokenAccountIdempotent` instruction, which is unconventional since the session key can't spend funds.
|
|
39
|
+
* It works because at the time `CreateAssociatedTokenAccountIdempotent` is called, the `userTokenAccount` has already been funded by the `SessionWrap` instruction.
|
|
40
|
+
* The paymaster will reject the transaction if the payer of the `CreateAssociatedTokenAccountIdempotent` is set to the paymaster payer to avoid the paymaster's funds getting drained.
|
|
41
|
+
*/
|
|
42
|
+
function createSessionWrapInstructions(sessionKey, walletPublicKey, amount) {
|
|
43
|
+
const userTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(spl_token_1.NATIVE_MINT, walletPublicKey);
|
|
44
|
+
return [
|
|
45
|
+
createSystemProgramSessionWrapInstruction(sessionKey, walletPublicKey, amount),
|
|
46
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(sessionKey, // This is unconventional! Read the note in the function's docs.
|
|
47
|
+
userTokenAccount, walletPublicKey, spl_token_1.NATIVE_MINT),
|
|
48
|
+
(0, spl_token_1.createSyncNativeInstruction)(userTokenAccount),
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Creates the instruction required to unwrap native tokens within a session.
|
|
53
|
+
*/
|
|
54
|
+
function createSessionUnwrapInstruction(sessionKey, walletPublicKey) {
|
|
55
|
+
return (0, spl_token_1.createCloseAccountInstruction)(getNativeMintAssociatedTokenAddressSync(walletPublicKey), walletPublicKey, sessionKey);
|
|
56
|
+
}
|
package/esm/connection.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GetLatestBlockhashApi, Instruction, Rpc, Transaction, TransactionWithLifetime } from "@solana/kit";
|
|
2
2
|
import type { TransactionError } from "@solana/web3.js";
|
|
3
|
-
import { Keypair,
|
|
3
|
+
import { Keypair, PublicKey, TransactionInstruction, VersionedTransaction, Connection as Web3Connection } from "@solana/web3.js";
|
|
4
4
|
export declare enum Network {
|
|
5
5
|
Testnet = 0,
|
|
6
6
|
Mainnet = 1
|
package/esm/connection.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fromLegacyKeypair, fromLegacyPublicKey, fromLegacyTransactionInstruction, fromVersionedTransaction, } from "@solana/compat";
|
|
2
|
-
import {
|
|
3
|
-
import { Keypair,
|
|
2
|
+
import { addSignersToTransactionMessage, appendTransactionMessageInstructions, compressTransactionMessageUsingAddressLookupTables, createSignerFromKeyPair, createSolanaRpc, createTransactionMessage, getBase64EncodedWireTransaction, partiallySignTransaction, partiallySignTransactionMessageWithSigners, pipe, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, } from "@solana/kit";
|
|
3
|
+
import { Keypair, PublicKey, TransactionInstruction, VersionedTransaction, Connection as Web3Connection, } from "@solana/web3.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
export var Network;
|
|
6
6
|
(function (Network) {
|
package/esm/context.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AnchorProvider } from "@coral-xyz/anchor";
|
|
2
2
|
import { ChainIdProgram } from "@fogo/sessions-idls";
|
|
3
|
-
import { Connection as Web3Connection
|
|
3
|
+
import { Keypair, Connection as Web3Connection } from "@solana/web3.js";
|
|
4
4
|
// eslint-disable-next-line unicorn/no-typeof-undefined
|
|
5
5
|
const IS_BROWSER = typeof globalThis.window !== "undefined";
|
|
6
6
|
export const SESSIONS_INTERNAL_PAYMASTER_DOMAIN = "sessions";
|
package/esm/index.d.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { TransactionError } from "@solana/web3.js";
|
|
1
|
+
import type { TransactionError, TransactionInstruction, VersionedTransaction } from "@solana/web3.js";
|
|
3
2
|
import { Connection, PublicKey } from "@solana/web3.js";
|
|
4
3
|
import type { Chain } from "@wormhole-foundation/sdk";
|
|
5
4
|
import BN from "bn.js";
|
|
6
5
|
import { z } from "zod";
|
|
7
6
|
import type { TransactionOrInstructions, TransactionResult } from "./connection.js";
|
|
8
7
|
import type { SendTransactionOptions, SessionContext } from "./context.js";
|
|
9
|
-
export { type
|
|
10
|
-
export {
|
|
8
|
+
export { type Connection, createSessionConnection, Network, type TransactionOrInstructions, type TransactionResult, TransactionResultType, } from "./connection.js";
|
|
9
|
+
export { createSessionContext, type SendTransactionOptions, type SessionContext, } from "./context.js";
|
|
11
10
|
type EstablishSessionOptions = {
|
|
12
11
|
context: SessionContext;
|
|
13
12
|
walletPublicKey: PublicKey;
|
|
14
|
-
signMessage: (message: Uint8Array) => Promise<
|
|
13
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
14
|
+
signedMessage: Uint8Array;
|
|
15
|
+
signature: Uint8Array;
|
|
16
|
+
}>;
|
|
15
17
|
expires: Date;
|
|
16
18
|
extra?: Record<string, string> | undefined;
|
|
17
19
|
createUnsafeExtractableSessionKey?: boolean | undefined;
|
|
@@ -26,7 +28,10 @@ export declare const establishSession: (options: EstablishSessionOptions) => Pro
|
|
|
26
28
|
export declare const replaceSession: (options: {
|
|
27
29
|
context: SessionContext;
|
|
28
30
|
session: Session;
|
|
29
|
-
signMessage: (message: Uint8Array) => Promise<
|
|
31
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
32
|
+
signedMessage: Uint8Array;
|
|
33
|
+
signature: Uint8Array;
|
|
34
|
+
}>;
|
|
30
35
|
expires: Date;
|
|
31
36
|
extra?: Record<string, string> | undefined;
|
|
32
37
|
} & ({
|
|
@@ -2223,6 +2228,9 @@ export type Session = {
|
|
|
2223
2228
|
sessionKey: CryptoKeyPair;
|
|
2224
2229
|
walletPublicKey: PublicKey;
|
|
2225
2230
|
payer: PublicKey;
|
|
2231
|
+
getSystemProgramSessionWrapInstruction: (amount: bigint) => TransactionInstruction;
|
|
2232
|
+
getSessionWrapInstructions: (amount: bigint) => TransactionInstruction[];
|
|
2233
|
+
getSessionUnwrapInstructions: () => TransactionInstruction[];
|
|
2226
2234
|
sendTransaction: (instructions: TransactionOrInstructions, extraConfig?: SendTransactionOptions) => Promise<TransactionResult>;
|
|
2227
2235
|
sessionInfo: NonNullable<z.infer<typeof sessionInfoSchema>>;
|
|
2228
2236
|
};
|
|
@@ -2243,19 +2251,37 @@ export declare const getBridgeOutFee: (context: SessionContext) => Promise<{
|
|
|
2243
2251
|
type SendTransferOptions = {
|
|
2244
2252
|
context: SessionContext;
|
|
2245
2253
|
walletPublicKey: PublicKey;
|
|
2246
|
-
signMessage: (message: Uint8Array) => Promise<
|
|
2254
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
2255
|
+
signedMessage: Uint8Array;
|
|
2256
|
+
signature: Uint8Array;
|
|
2257
|
+
}>;
|
|
2247
2258
|
mint: PublicKey;
|
|
2248
2259
|
amount: bigint;
|
|
2249
2260
|
recipient: PublicKey;
|
|
2250
2261
|
feeConfig: Awaited<ReturnType<typeof getTransferFee>>;
|
|
2251
2262
|
};
|
|
2252
2263
|
export declare const sendTransfer: (options: SendTransferOptions) => Promise<TransactionResult>;
|
|
2264
|
+
type SendNativeTransferOptions = {
|
|
2265
|
+
context: SessionContext;
|
|
2266
|
+
walletPublicKey: PublicKey;
|
|
2267
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
2268
|
+
signedMessage: Uint8Array;
|
|
2269
|
+
signature: Uint8Array;
|
|
2270
|
+
}>;
|
|
2271
|
+
amount: bigint;
|
|
2272
|
+
recipient: PublicKey;
|
|
2273
|
+
feeConfig: Awaited<ReturnType<typeof getTransferFee>>;
|
|
2274
|
+
};
|
|
2275
|
+
export declare const sendNativeTransfer: (options: SendNativeTransferOptions) => Promise<TransactionResult>;
|
|
2253
2276
|
type SendBridgeOutOptions = {
|
|
2254
2277
|
context: SessionContext;
|
|
2255
2278
|
sessionKey: CryptoKeyPair;
|
|
2256
2279
|
sessionPublicKey: PublicKey;
|
|
2257
2280
|
walletPublicKey: PublicKey;
|
|
2258
|
-
|
|
2281
|
+
signMessage: (message: Uint8Array) => Promise<{
|
|
2282
|
+
signedMessage: Uint8Array;
|
|
2283
|
+
signature: Uint8Array;
|
|
2284
|
+
}>;
|
|
2259
2285
|
amount: bigint;
|
|
2260
2286
|
fromToken: WormholeToken & {
|
|
2261
2287
|
chain: "Fogo";
|
|
@@ -2275,7 +2301,7 @@ export declare const bridgeOut: (options: SendBridgeOutOptions) => Promise<Trans
|
|
|
2275
2301
|
type SendBridgeInOptions = {
|
|
2276
2302
|
context: SessionContext;
|
|
2277
2303
|
walletPublicKey: PublicKey;
|
|
2278
|
-
|
|
2304
|
+
signTransaction: (transaction: VersionedTransaction) => Promise<VersionedTransaction>;
|
|
2279
2305
|
amount: bigint;
|
|
2280
2306
|
fromToken: WormholeToken & {
|
|
2281
2307
|
chain: "Solana";
|
package/esm/index.js
CHANGED
|
@@ -5,10 +5,10 @@ import { publicKey as metaplexPublicKey } from "@metaplex-foundation/umi";
|
|
|
5
5
|
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
|
|
6
6
|
import { sha256 } from "@noble/hashes/sha2";
|
|
7
7
|
import { fromLegacyPublicKey } from "@solana/compat";
|
|
8
|
-
import { generateKeyPair, getAddressFromPublicKey, getProgramDerivedAddress,
|
|
8
|
+
import { generateKeyPair, getAddressFromPublicKey, getProgramDerivedAddress, } from "@solana/kit";
|
|
9
9
|
import { getAssociatedTokenAddressSync, getMint } from "@solana/spl-token";
|
|
10
10
|
import { ComputeBudgetProgram, Connection, Ed25519Program, Keypair, PublicKey, } from "@solana/web3.js";
|
|
11
|
-
import { Wormhole, wormhole
|
|
11
|
+
import { routes, Wormhole, wormhole } from "@wormhole-foundation/sdk";
|
|
12
12
|
import solanaSdk from "@wormhole-foundation/sdk/solana";
|
|
13
13
|
import { contracts } from "@wormhole-foundation/sdk-base";
|
|
14
14
|
import { nttExecutorRoute } from "@wormhole-foundation/sdk-route-ntt";
|
|
@@ -20,8 +20,9 @@ import { z } from "zod";
|
|
|
20
20
|
import { Network, TransactionResultType } from "./connection.js";
|
|
21
21
|
import { SESSIONS_INTERNAL_PAYMASTER_DOMAIN } from "./context.js";
|
|
22
22
|
import { importKey, signMessageWithKey, verifyMessageWithKey, } from "./crypto.js";
|
|
23
|
+
import { createSessionUnwrapInstruction, createSessionWrapInstructions, createSystemProgramSessionWrapInstruction, } from "./instructions.js";
|
|
24
|
+
export { createSessionConnection, Network, TransactionResultType, } from "./connection.js";
|
|
23
25
|
export { createSessionContext, } from "./context.js";
|
|
24
|
-
export { Network, TransactionResultType, createSessionConnection, } from "./connection.js";
|
|
25
26
|
const MESSAGE_HEADER = `Fogo Sessions:
|
|
26
27
|
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.
|
|
27
28
|
`;
|
|
@@ -43,7 +44,7 @@ export const establishSession = async (options) => {
|
|
|
43
44
|
: await generateKeyPair();
|
|
44
45
|
if (options.unlimited) {
|
|
45
46
|
return sendSessionEstablishTransaction(options, sessionKey, await Promise.all([
|
|
46
|
-
|
|
47
|
+
buildStartSessionIntentInstruction(options, sessionKey),
|
|
47
48
|
buildStartSessionInstruction(options, sessionKey),
|
|
48
49
|
]), options.sessionEstablishmentLookupTable);
|
|
49
50
|
}
|
|
@@ -53,7 +54,7 @@ export const establishSession = async (options) => {
|
|
|
53
54
|
? await getTokenInfo(options.context, filteredLimits)
|
|
54
55
|
: [];
|
|
55
56
|
const [intentInstruction, startSessionInstruction] = await Promise.all([
|
|
56
|
-
|
|
57
|
+
buildStartSessionIntentInstruction(options, sessionKey, tokenInfo),
|
|
57
58
|
buildStartSessionInstruction(options, sessionKey, tokenInfo),
|
|
58
59
|
]);
|
|
59
60
|
return sendSessionEstablishTransaction(options, sessionKey, [intentInstruction, startSessionInstruction], options.sessionEstablishmentLookupTable);
|
|
@@ -115,6 +116,11 @@ const createSession = async (context, walletPublicKey, sessionKey) => {
|
|
|
115
116
|
walletPublicKey,
|
|
116
117
|
sessionKey,
|
|
117
118
|
payer: context.payer,
|
|
119
|
+
getSystemProgramSessionWrapInstruction: (amount) => createSystemProgramSessionWrapInstruction(sessionPublicKey, walletPublicKey, amount),
|
|
120
|
+
getSessionWrapInstructions: (amount) => createSessionWrapInstructions(sessionPublicKey, walletPublicKey, amount),
|
|
121
|
+
getSessionUnwrapInstructions: () => [
|
|
122
|
+
createSessionUnwrapInstruction(sessionPublicKey, walletPublicKey),
|
|
123
|
+
],
|
|
118
124
|
sendTransaction: (instructions, extraConfig) => context.sendTransaction(sessionKey, instructions, extraConfig),
|
|
119
125
|
sessionInfo,
|
|
120
126
|
};
|
|
@@ -339,7 +345,7 @@ const SymbolOrMint = {
|
|
|
339
345
|
mint,
|
|
340
346
|
}),
|
|
341
347
|
};
|
|
342
|
-
const getTokenInfo =
|
|
348
|
+
const getTokenInfo = (context, limits) => {
|
|
343
349
|
const umi = createUmi(context.connection.rpcEndpoint);
|
|
344
350
|
return Promise.all(limits.entries().map(async ([mint, amount]) => {
|
|
345
351
|
const metaplexMint = metaplexPublicKey(mint.toBase58());
|
|
@@ -359,64 +365,23 @@ const getTokenInfo = async (context, limits) => {
|
|
|
359
365
|
};
|
|
360
366
|
}));
|
|
361
367
|
};
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
else {
|
|
374
|
-
// Source: https://github.com/anza-xyz/solana-sdk/blob/master/offchain-message/src/lib.rs#L162
|
|
375
|
-
const messageWithOffchainMessagePrefix = Uint8Array.from([
|
|
376
|
-
// eslint-disable-next-line unicorn/number-literal-case
|
|
377
|
-
0xff,
|
|
378
|
-
...new TextEncoder().encode("solana offchain"),
|
|
379
|
-
0,
|
|
380
|
-
1,
|
|
381
|
-
...serializeU16LE(message.length),
|
|
382
|
-
...message,
|
|
383
|
-
]);
|
|
384
|
-
if (await verifySignature(publicKey, signature, messageWithOffchainMessagePrefix)) {
|
|
385
|
-
return messageWithOffchainMessagePrefix;
|
|
386
|
-
}
|
|
387
|
-
else {
|
|
388
|
-
throw new Error("The signature provided by the browser wallet is not valid");
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
};
|
|
392
|
-
const buildIntentInstruction = async (options, sessionKey, tokens) => {
|
|
393
|
-
const message = await buildMessage({
|
|
394
|
-
chainId: options.context.chainId,
|
|
395
|
-
domain: options.context.domain,
|
|
396
|
-
sessionKey,
|
|
397
|
-
expires: options.expires,
|
|
398
|
-
tokens,
|
|
399
|
-
extra: options.extra,
|
|
400
|
-
});
|
|
401
|
-
const intentSignature = signatureBytes(await options.signMessage(message));
|
|
368
|
+
const buildStartSessionIntentInstruction = async (options, sessionKey, tokens) => buildIntentInstruction(options, MESSAGE_HEADER, {
|
|
369
|
+
version: `${CURRENT_MAJOR}.${CURRENT_MINOR}`,
|
|
370
|
+
chain_id: options.context.chainId,
|
|
371
|
+
domain: options.context.domain,
|
|
372
|
+
expires: options.expires.toISOString(),
|
|
373
|
+
session_key: await getAddressFromPublicKey(sessionKey.publicKey),
|
|
374
|
+
tokens: serializeTokenList(tokens),
|
|
375
|
+
});
|
|
376
|
+
const buildIntentInstruction = async (options, header, body, extra) => {
|
|
377
|
+
const message = new TextEncoder().encode([header, serializeKV(body), extra && serializeExtra(extra)].join("\n"));
|
|
378
|
+
const { signature, signedMessage } = await options.signMessage(message);
|
|
402
379
|
return Ed25519Program.createInstructionWithPublicKey({
|
|
403
380
|
publicKey: options.walletPublicKey.toBytes(),
|
|
404
|
-
signature
|
|
405
|
-
message:
|
|
381
|
+
signature,
|
|
382
|
+
message: signedMessage,
|
|
406
383
|
});
|
|
407
384
|
};
|
|
408
|
-
const buildMessage = async (body) => new TextEncoder().encode([
|
|
409
|
-
MESSAGE_HEADER,
|
|
410
|
-
serializeKV({
|
|
411
|
-
version: `${CURRENT_MAJOR}.${CURRENT_MINOR}`,
|
|
412
|
-
chain_id: body.chainId,
|
|
413
|
-
domain: body.domain,
|
|
414
|
-
expires: body.expires.toISOString(),
|
|
415
|
-
session_key: await getAddressFromPublicKey(body.sessionKey.publicKey),
|
|
416
|
-
tokens: serializeTokenList(body.tokens),
|
|
417
|
-
}),
|
|
418
|
-
body.extra && serializeExtra(body.extra),
|
|
419
|
-
].join("\n"));
|
|
420
385
|
const serializeExtra = (extra) => {
|
|
421
386
|
for (const [key, value] of Object.entries(extra)) {
|
|
422
387
|
if (!/^[a-z]+(_[a-z0-9]+)*$/.test(key)) {
|
|
@@ -599,24 +564,49 @@ const buildTransferIntentInstruction = async (program, options, symbol, feeToken
|
|
|
599
564
|
getNonce(program, options.walletPublicKey, NonceType.Transfer),
|
|
600
565
|
getMint(options.context.connection, options.mint),
|
|
601
566
|
]);
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
567
|
+
return buildIntentInstruction(options, TRANSFER_MESSAGE_HEADER, {
|
|
568
|
+
version: `${CURRENT_INTENT_TRANSFER_MAJOR}.${CURRENT_INTENT_TRANSFER_MINOR}`,
|
|
569
|
+
chain_id: options.context.chainId,
|
|
570
|
+
token: symbol ?? options.mint.toBase58(),
|
|
571
|
+
amount: amountToString(options.amount, decimals),
|
|
572
|
+
recipient: options.recipient.toBase58(),
|
|
573
|
+
fee_token: feeToken,
|
|
574
|
+
fee_amount: feeAmount,
|
|
575
|
+
nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
|
|
576
|
+
});
|
|
577
|
+
};
|
|
578
|
+
export const sendNativeTransfer = async (options) => {
|
|
579
|
+
const program = new IntentTransferProgram(new AnchorProvider(options.context.connection, {}, {}));
|
|
580
|
+
return options.context.sendTransaction(undefined, [
|
|
581
|
+
await buildNativeTransferIntentInstruction(program, options, options.feeConfig.symbolOrMint, amountToString(options.feeConfig.fee, options.feeConfig.decimals)),
|
|
582
|
+
await program.methods
|
|
583
|
+
.sendNative()
|
|
584
|
+
.accounts({
|
|
585
|
+
feeMetadata: options.feeConfig.metadata,
|
|
586
|
+
feeMint: options.feeConfig.mint,
|
|
587
|
+
feeSource: getAssociatedTokenAddressSync(options.feeConfig.mint, options.walletPublicKey),
|
|
588
|
+
source: options.walletPublicKey,
|
|
589
|
+
destination: options.recipient,
|
|
590
|
+
sponsor: options.context.internalPayer,
|
|
591
|
+
})
|
|
592
|
+
.instruction(),
|
|
593
|
+
], {
|
|
594
|
+
variation: "Intent Transfer",
|
|
595
|
+
paymasterDomain: SESSIONS_INTERNAL_PAYMASTER_DOMAIN,
|
|
596
|
+
});
|
|
597
|
+
};
|
|
598
|
+
const FOGO_DECIMALS = 9;
|
|
599
|
+
const buildNativeTransferIntentInstruction = async (program, options, feeToken, feeAmount) => {
|
|
600
|
+
const nonce = await getNonce(program, options.walletPublicKey, NonceType.Transfer);
|
|
601
|
+
return buildIntentInstruction(options, TRANSFER_MESSAGE_HEADER, {
|
|
602
|
+
version: `${CURRENT_INTENT_TRANSFER_MAJOR}.${CURRENT_INTENT_TRANSFER_MINOR}`,
|
|
603
|
+
chain_id: options.context.chainId,
|
|
604
|
+
token: "FOGO",
|
|
605
|
+
amount: amountToString(options.amount, FOGO_DECIMALS),
|
|
606
|
+
recipient: options.recipient.toBase58(),
|
|
607
|
+
fee_token: feeToken,
|
|
608
|
+
fee_amount: feeAmount,
|
|
609
|
+
nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
|
|
620
610
|
});
|
|
621
611
|
};
|
|
622
612
|
const BRIDGE_OUT_MESSAGE_HEADER = `Fogo Bridge Transfer:
|
|
@@ -718,25 +708,16 @@ const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeA
|
|
|
718
708
|
};
|
|
719
709
|
const buildBridgeOutIntent = async (program, options, decimals, symbol, feeToken, feeAmount) => {
|
|
720
710
|
const nonce = await getNonce(program, options.walletPublicKey, NonceType.Bridge);
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
fee_amount: feeAmount,
|
|
732
|
-
nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
|
|
733
|
-
}),
|
|
734
|
-
].join("\n"));
|
|
735
|
-
const intentSignature = signatureBytes(await options.solanaWallet.signMessage(message));
|
|
736
|
-
return Ed25519Program.createInstructionWithPublicKey({
|
|
737
|
-
publicKey: options.walletPublicKey.toBytes(),
|
|
738
|
-
signature: intentSignature,
|
|
739
|
-
message: await addOffchainMessagePrefixToMessageIfNeeded(options.walletPublicKey, intentSignature, message),
|
|
711
|
+
return buildIntentInstruction(options, BRIDGE_OUT_MESSAGE_HEADER, {
|
|
712
|
+
version: `${CURRENT_BRIDGE_OUT_MAJOR}.${CURRENT_BRIDGE_OUT_MINOR}`,
|
|
713
|
+
from_chain_id: options.context.chainId,
|
|
714
|
+
to_chain_id: "solana",
|
|
715
|
+
token: symbol ?? options.fromToken.mint.toBase58(),
|
|
716
|
+
amount: amountToString(options.amount, decimals),
|
|
717
|
+
recipient_address: options.walletPublicKey.toBase58(),
|
|
718
|
+
fee_token: feeToken,
|
|
719
|
+
fee_amount: feeAmount,
|
|
720
|
+
nonce: nonce === null ? "1" : nonce.nonce.add(new BN(1)).toString(),
|
|
740
721
|
});
|
|
741
722
|
};
|
|
742
723
|
export const bridgeIn = async (options) => {
|
|
@@ -750,10 +731,9 @@ export const bridgeIn = async (options) => {
|
|
|
750
731
|
address: () => options.walletPublicKey.toBase58(),
|
|
751
732
|
chain: () => "Solana",
|
|
752
733
|
sign: (transactions) => Promise.all(transactions.map(async ({ transaction }) => {
|
|
753
|
-
|
|
754
|
-
const signedTx = await options.solanaWallet.signTransaction(
|
|
734
|
+
const signedTx = await options.signTransaction(
|
|
755
735
|
// Hooray for Wormhole's incomplete typing eh?
|
|
756
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
736
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
757
737
|
transaction.transaction);
|
|
758
738
|
// Hooray for Wormhole's incomplete typing eh?
|
|
759
739
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type PublicKey, TransactionInstruction } from "@solana/web3.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates the system program instruction `SessionWrap`, only available on Fogo, which allows a session key to transfer native token from its user's wallet to its user's wrapped token associated token account.
|
|
4
|
+
* This instruction may be combined with the `CreateAssociatedTokenAccountIdempotent` and `SyncNative` instructions for a session to wrap tokens on behalf of its user.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createSystemProgramSessionWrapInstruction(sessionKey: PublicKey, walletPublicKey: PublicKey, amount: bigint): TransactionInstruction;
|
|
7
|
+
/**
|
|
8
|
+
* Creates the sequence of instructions required to wrap native tokens within a session.
|
|
9
|
+
*
|
|
10
|
+
* Note: This function sets the session key as the payer for the `CreateAssociatedTokenAccountIdempotent` instruction, which is unconventional since the session key can't spend funds.
|
|
11
|
+
* It works because at the time `CreateAssociatedTokenAccountIdempotent` is called, the `userTokenAccount` has already been funded by the `SessionWrap` instruction.
|
|
12
|
+
* The paymaster will reject the transaction if the payer of the `CreateAssociatedTokenAccountIdempotent` is set to the paymaster payer to avoid the paymaster's funds getting drained.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createSessionWrapInstructions(sessionKey: PublicKey, walletPublicKey: PublicKey, amount: bigint): TransactionInstruction[];
|
|
15
|
+
/**
|
|
16
|
+
* Creates the instruction required to unwrap native tokens within a session.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createSessionUnwrapInstruction(sessionKey: PublicKey, walletPublicKey: PublicKey): TransactionInstruction;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createAssociatedTokenAccountIdempotentInstruction, createCloseAccountInstruction, createSyncNativeInstruction, getAssociatedTokenAddressSync, NATIVE_MINT, } from "@solana/spl-token";
|
|
2
|
+
import { SystemProgram, TransactionInstruction, } from "@solana/web3.js";
|
|
3
|
+
const SESSION_WRAP_DISCRIMINATOR = 4_000_000;
|
|
4
|
+
function getNativeMintAssociatedTokenAddressSync(walletPublicKey) {
|
|
5
|
+
return getAssociatedTokenAddressSync(NATIVE_MINT, walletPublicKey);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates the system program instruction `SessionWrap`, only available on Fogo, which allows a session key to transfer native token from its user's wallet to its user's wrapped token associated token account.
|
|
9
|
+
* This instruction may be combined with the `CreateAssociatedTokenAccountIdempotent` and `SyncNative` instructions for a session to wrap tokens on behalf of its user.
|
|
10
|
+
*/
|
|
11
|
+
export function createSystemProgramSessionWrapInstruction(sessionKey, walletPublicKey, amount) {
|
|
12
|
+
const data = new Uint8Array(12);
|
|
13
|
+
const view = new DataView(data.buffer);
|
|
14
|
+
view.setUint32(0, SESSION_WRAP_DISCRIMINATOR, true);
|
|
15
|
+
view.setBigUint64(4, amount, true);
|
|
16
|
+
return new TransactionInstruction({
|
|
17
|
+
programId: SystemProgram.programId,
|
|
18
|
+
keys: [
|
|
19
|
+
{ pubkey: walletPublicKey, isSigner: false, isWritable: true },
|
|
20
|
+
{
|
|
21
|
+
pubkey: getNativeMintAssociatedTokenAddressSync(walletPublicKey),
|
|
22
|
+
isSigner: false,
|
|
23
|
+
isWritable: true,
|
|
24
|
+
},
|
|
25
|
+
{ pubkey: sessionKey, isSigner: true, isWritable: false },
|
|
26
|
+
],
|
|
27
|
+
data: Buffer.from(data),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates the sequence of instructions required to wrap native tokens within a session.
|
|
32
|
+
*
|
|
33
|
+
* Note: This function sets the session key as the payer for the `CreateAssociatedTokenAccountIdempotent` instruction, which is unconventional since the session key can't spend funds.
|
|
34
|
+
* It works because at the time `CreateAssociatedTokenAccountIdempotent` is called, the `userTokenAccount` has already been funded by the `SessionWrap` instruction.
|
|
35
|
+
* The paymaster will reject the transaction if the payer of the `CreateAssociatedTokenAccountIdempotent` is set to the paymaster payer to avoid the paymaster's funds getting drained.
|
|
36
|
+
*/
|
|
37
|
+
export function createSessionWrapInstructions(sessionKey, walletPublicKey, amount) {
|
|
38
|
+
const userTokenAccount = getAssociatedTokenAddressSync(NATIVE_MINT, walletPublicKey);
|
|
39
|
+
return [
|
|
40
|
+
createSystemProgramSessionWrapInstruction(sessionKey, walletPublicKey, amount),
|
|
41
|
+
createAssociatedTokenAccountIdempotentInstruction(sessionKey, // This is unconventional! Read the note in the function's docs.
|
|
42
|
+
userTokenAccount, walletPublicKey, NATIVE_MINT),
|
|
43
|
+
createSyncNativeInstruction(userTokenAccount),
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates the instruction required to unwrap native tokens within a session.
|
|
48
|
+
*/
|
|
49
|
+
export function createSessionUnwrapInstruction(sessionKey, walletPublicKey) {
|
|
50
|
+
return createCloseAccountInstruction(getNativeMintAssociatedTokenAddressSync(walletPublicKey), walletPublicKey, sessionKey);
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fogo/sessions-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.30",
|
|
4
4
|
"description": "A set of utilities for integrating with Fogo sessions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"main": "./cjs/index.js",
|
|
18
18
|
"types": "./cjs/index.d.ts",
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": ">=22"
|
|
21
21
|
},
|
|
22
22
|
"exports": {
|
|
23
23
|
".": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@wormhole-foundation/sdk-solana-ntt": "^4.0.1",
|
|
49
49
|
"bn.js": "^5.1.2",
|
|
50
50
|
"bs58": "^6.0.0",
|
|
51
|
-
"zod": "
|
|
52
|
-
"@fogo/sessions-idls": "^0.0.
|
|
51
|
+
"zod": "3.25.67",
|
|
52
|
+
"@fogo/sessions-idls": "^0.0.13"
|
|
53
53
|
}
|
|
54
54
|
}
|