@fogo/sessions-sdk 0.0.18 → 0.0.19
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 +10 -20
- package/cjs/connection.js +67 -41
- package/cjs/context.d.ts +4 -2
- package/cjs/context.js +7 -2
- package/cjs/index.d.ts +39 -2
- package/cjs/index.js +225 -8
- package/esm/connection.d.ts +10 -20
- package/esm/connection.js +65 -39
- package/esm/context.d.ts +4 -2
- package/esm/context.js +7 -2
- package/esm/index.d.ts +39 -2
- package/esm/index.js +224 -9
- package/package.json +7 -2
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.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.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");
|
|
@@ -14,6 +14,12 @@ const compat_1 = require("@solana/compat");
|
|
|
14
14
|
const kit_1 = require("@solana/kit");
|
|
15
15
|
const spl_token_1 = require("@solana/spl-token");
|
|
16
16
|
const web3_js_1 = require("@solana/web3.js");
|
|
17
|
+
const sdk_1 = require("@wormhole-foundation/sdk");
|
|
18
|
+
const solana_1 = __importDefault(require("@wormhole-foundation/sdk/solana"));
|
|
19
|
+
const sdk_base_1 = require("@wormhole-foundation/sdk-base");
|
|
20
|
+
const sdk_route_ntt_1 = require("@wormhole-foundation/sdk-route-ntt");
|
|
21
|
+
const sdk_solana_core_1 = require("@wormhole-foundation/sdk-solana-core");
|
|
22
|
+
const sdk_solana_ntt_1 = require("@wormhole-foundation/sdk-solana-ntt");
|
|
17
23
|
const bn_js_1 = __importDefault(require("bn.js"));
|
|
18
24
|
const bs58_1 = __importDefault(require("bs58"));
|
|
19
25
|
const zod_1 = require("zod");
|
|
@@ -34,6 +40,12 @@ const CURRENT_MAJOR = "0";
|
|
|
34
40
|
const CURRENT_MINOR = "3";
|
|
35
41
|
const CURRENT_INTENT_TRANSFER_MAJOR = "0";
|
|
36
42
|
const CURRENT_INTENT_TRANSFER_MINOR = "1";
|
|
43
|
+
const CURRENT_BRIDGE_OUT_MAJOR = "0";
|
|
44
|
+
const CURRENT_BRIDGE_OUT_MINOR = "1";
|
|
45
|
+
const SESSION_ESTABLISHMENT_LOOKUP_TABLE_ADDRESS = {
|
|
46
|
+
[connection_js_1.Network.Testnet]: "B8cUjJMqaWWTNNSTXBmeptjWswwCH1gTSCRYv4nu7kJW",
|
|
47
|
+
[connection_js_1.Network.Mainnet]: undefined,
|
|
48
|
+
};
|
|
37
49
|
const establishSession = async (options) => {
|
|
38
50
|
const sessionKey = options.createUnsafeExtractableSessionKey
|
|
39
51
|
? await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"])
|
|
@@ -42,7 +54,7 @@ const establishSession = async (options) => {
|
|
|
42
54
|
return sendSessionEstablishTransaction(options, sessionKey, await Promise.all([
|
|
43
55
|
buildIntentInstruction(options, sessionKey),
|
|
44
56
|
buildStartSessionInstruction(options, sessionKey),
|
|
45
|
-
]));
|
|
57
|
+
]), options.sessionEstablishmentLookupTable);
|
|
46
58
|
}
|
|
47
59
|
else {
|
|
48
60
|
const filteredLimits = new Map(options.limits?.entries().filter(([, amount]) => amount > 0n));
|
|
@@ -57,12 +69,15 @@ const establishSession = async (options) => {
|
|
|
57
69
|
...buildCreateAssociatedTokenAccountInstructions(options, tokenInfo),
|
|
58
70
|
intentInstruction,
|
|
59
71
|
startSessionInstruction,
|
|
60
|
-
]);
|
|
72
|
+
], options.sessionEstablishmentLookupTable);
|
|
61
73
|
}
|
|
62
74
|
};
|
|
63
75
|
exports.establishSession = establishSession;
|
|
64
|
-
const sendSessionEstablishTransaction = async (options, sessionKey, instructions) => {
|
|
65
|
-
const result = await options.context.sendTransaction(sessionKey, instructions
|
|
76
|
+
const sendSessionEstablishTransaction = async (options, sessionKey, instructions, sessionEstablishmentLookupTable) => {
|
|
77
|
+
const result = await options.context.sendTransaction(sessionKey, instructions, {
|
|
78
|
+
addressLookupTable: sessionEstablishmentLookupTable ??
|
|
79
|
+
SESSION_ESTABLISHMENT_LOOKUP_TABLE_ADDRESS[options.context.network],
|
|
80
|
+
});
|
|
66
81
|
switch (result.type) {
|
|
67
82
|
case connection_js_1.TransactionResultType.Success: {
|
|
68
83
|
const session = await createSession(options.context, options.walletPublicKey, sessionKey);
|
|
@@ -426,6 +441,13 @@ const getDomainRecordAddress = (domain) => {
|
|
|
426
441
|
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("domain-record"), hash], new web3_js_1.PublicKey(sessions_idls_1.DomainRegistryIdl.address))[0];
|
|
427
442
|
};
|
|
428
443
|
exports.getDomainRecordAddress = getDomainRecordAddress;
|
|
444
|
+
const BRIDGING_ADDRESS_LOOKUP_TABLE = {
|
|
445
|
+
[connection_js_1.Network.Testnet]: {
|
|
446
|
+
// USDC
|
|
447
|
+
ELNbJ1RtERV2fjtuZjbTscDekWhVzkQ1LjmiPsxp5uND: "4FCi6LptexBdZtaePsoCMeb1XpCijxnWu96g5LsSb6WP",
|
|
448
|
+
},
|
|
449
|
+
[connection_js_1.Network.Mainnet]: undefined,
|
|
450
|
+
};
|
|
429
451
|
const buildStartSessionInstruction = async (options, sessionKey, tokens) => {
|
|
430
452
|
const instruction = new sessions_idls_1.SessionManagerProgram(new anchor_1.AnchorProvider(options.context.connection, {}, {})).methods
|
|
431
453
|
.startSession()
|
|
@@ -508,7 +530,7 @@ const sendTransfer = async (options) => {
|
|
|
508
530
|
exports.sendTransfer = sendTransfer;
|
|
509
531
|
const buildTransferIntentInstruction = async (program, options, symbol) => {
|
|
510
532
|
const [nonce, { decimals }] = await Promise.all([
|
|
511
|
-
getNonce(program, options.walletPublicKey),
|
|
533
|
+
getNonce(program, options.walletPublicKey, NonceType.Transfer),
|
|
512
534
|
(0, spl_token_1.getMint)(options.context.connection, options.mint),
|
|
513
535
|
]);
|
|
514
536
|
const message = new TextEncoder().encode([
|
|
@@ -529,13 +551,208 @@ const buildTransferIntentInstruction = async (program, options, symbol) => {
|
|
|
529
551
|
message: await addOffchainMessagePrefixToMessageIfNeeded(options.walletPublicKey, intentSignature, message),
|
|
530
552
|
});
|
|
531
553
|
};
|
|
532
|
-
const
|
|
554
|
+
const BRIDGE_OUT_MESSAGE_HEADER = `Fogo Bridge Transfer:
|
|
555
|
+
Signing this intent will bridge out the tokens as described below.
|
|
556
|
+
`;
|
|
557
|
+
const bridgeOut = async (options) => {
|
|
558
|
+
const { wh, route, transferRequest, transferParams, decimals } = await buildWormholeTransfer(options, options.context.connection);
|
|
559
|
+
// @ts-expect-error the wormhole client types are incorrect and do not
|
|
560
|
+
// properly represent the runtime representation.
|
|
561
|
+
const quote = await route.fetchExecutorQuote(transferRequest, transferParams);
|
|
562
|
+
const program = new sessions_idls_1.IntentTransferProgram(new anchor_1.AnchorProvider(options.context.connection, {}, {}));
|
|
563
|
+
const umi = (0, umi_bundle_defaults_1.createUmi)(options.context.connection.rpcEndpoint);
|
|
564
|
+
const metaplexMint = (0, umi_1.publicKey)(options.fromToken.mint.toBase58());
|
|
565
|
+
const metadataAddress = (0, mpl_token_metadata_1.findMetadataPda)(umi, { mint: metaplexMint })[0];
|
|
566
|
+
const outboxItem = web3_js_1.Keypair.generate();
|
|
567
|
+
const [metadata, nttPdas] = await Promise.all([
|
|
568
|
+
(0, mpl_token_metadata_1.safeFetchMetadata)(umi, metadataAddress),
|
|
569
|
+
getNttPdas(options, wh, program, outboxItem.publicKey, new web3_js_1.PublicKey(quote.payeeAddress)),
|
|
570
|
+
]);
|
|
571
|
+
const bridgeInstruction = await program.methods
|
|
572
|
+
.bridgeNttTokens({
|
|
573
|
+
execAmount: new bn_js_1.default(quote.estimatedCost.toString()),
|
|
574
|
+
relayInstructions: Buffer.from(quote.relayInstructions),
|
|
575
|
+
signedQuoteBytes: Buffer.from(quote.signedQuote),
|
|
576
|
+
})
|
|
577
|
+
.accounts({
|
|
578
|
+
sponsor: options.context.payer,
|
|
579
|
+
mint: options.fromToken.mint,
|
|
580
|
+
metadata:
|
|
581
|
+
// eslint-disable-next-line unicorn/no-null
|
|
582
|
+
metadata?.symbol === undefined ? null : new web3_js_1.PublicKey(metadataAddress),
|
|
583
|
+
source: (0, spl_token_1.getAssociatedTokenAddressSync)(options.fromToken.mint, options.walletPublicKey),
|
|
584
|
+
ntt: nttPdas,
|
|
585
|
+
})
|
|
586
|
+
.instruction();
|
|
587
|
+
return options.context.sendTransaction(options.sessionKey, [
|
|
588
|
+
await buildBridgeOutIntent(program, options, decimals, metadata?.symbol),
|
|
589
|
+
bridgeInstruction,
|
|
590
|
+
], {
|
|
591
|
+
extraSigners: [outboxItem],
|
|
592
|
+
addressLookupTable: BRIDGING_ADDRESS_LOOKUP_TABLE[options.context.network]?.[options.fromToken.mint.toBase58()],
|
|
593
|
+
});
|
|
594
|
+
};
|
|
595
|
+
exports.bridgeOut = bridgeOut;
|
|
596
|
+
// Here we use the Wormhole SDKs to produce the wormhole pdas that are needed
|
|
597
|
+
// for the bridge out transaction. Currently this is using wormhole SDK apis
|
|
598
|
+
// that are _technically_ public but it seems likely these are not considered to
|
|
599
|
+
// be truly public. It might be better to extract the pdas by using the sdk to
|
|
600
|
+
// generate (but not send) a transaction, and taking the pdas from that. That
|
|
601
|
+
// may be something to revisit in the future if we find that the wormhole sdk
|
|
602
|
+
// upgrades in ways that break these calls.
|
|
603
|
+
const getNttPdas = async (options, wh, program, outboxItemPublicKey, quotePayeeAddress) => {
|
|
604
|
+
const pdas = sdk_solana_ntt_1.NTT.pdas(options.fromToken.manager);
|
|
605
|
+
const solana = wh.getChain("Solana");
|
|
606
|
+
const coreBridgeContract = sdk_base_1.contracts.coreBridge.get(wh.network, "Fogo");
|
|
607
|
+
if (coreBridgeContract === undefined) {
|
|
608
|
+
throw new Error("Core bridge contract address not returned by wormhole!");
|
|
609
|
+
}
|
|
610
|
+
const transceiverPdas = sdk_solana_ntt_1.NTT.transceiverPdas(options.fromToken.manager);
|
|
611
|
+
const [intentTransferSetterPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("intent_transfer")], program.programId);
|
|
612
|
+
const wormholePdas = sdk_solana_core_1.utils.getWormholeDerivedAccounts(options.fromToken.manager, coreBridgeContract);
|
|
613
|
+
return {
|
|
614
|
+
emitter: transceiverPdas.emitterAccount(),
|
|
615
|
+
nttConfig: pdas.configAccount(),
|
|
616
|
+
nttCustody: await sdk_solana_ntt_1.NTT.custodyAccountAddress(pdas, options.fromToken.mint),
|
|
617
|
+
nttInboxRateLimit: pdas.inboxRateLimitAccount(solana.chain),
|
|
618
|
+
nttManager: options.fromToken.manager,
|
|
619
|
+
nttOutboxItem: outboxItemPublicKey,
|
|
620
|
+
nttOutboxRateLimit: pdas.outboxRateLimitAccount(),
|
|
621
|
+
nttPeer: pdas.peerAccount(solana.chain),
|
|
622
|
+
nttSessionAuthority: pdas.sessionAuthority(intentTransferSetterPda, sdk_solana_ntt_1.NTT.transferArgs(options.amount, sdk_1.Wormhole.chainAddress("Solana", options.walletPublicKey.toBase58()), false)),
|
|
623
|
+
nttTokenAuthority: pdas.tokenAuthority(),
|
|
624
|
+
payeeNttWithExecutor: quotePayeeAddress,
|
|
625
|
+
transceiver: options.fromToken.transceiver,
|
|
626
|
+
wormholeBridge: wormholePdas.wormholeBridge,
|
|
627
|
+
wormholeFeeCollector: wormholePdas.wormholeFeeCollector,
|
|
628
|
+
wormholeMessage: transceiverPdas.wormholeMessageAccount(outboxItemPublicKey),
|
|
629
|
+
wormholeSequence: wormholePdas.wormholeSequence,
|
|
630
|
+
};
|
|
631
|
+
};
|
|
632
|
+
const buildBridgeOutIntent = async (program, options, decimals, symbol) => {
|
|
633
|
+
const nonce = await getNonce(program, options.walletPublicKey, NonceType.Bridge);
|
|
634
|
+
const message = new TextEncoder().encode([
|
|
635
|
+
BRIDGE_OUT_MESSAGE_HEADER,
|
|
636
|
+
serializeKV({
|
|
637
|
+
version: `${CURRENT_BRIDGE_OUT_MAJOR}.${CURRENT_BRIDGE_OUT_MINOR}`,
|
|
638
|
+
from_chain_id: options.context.chainId,
|
|
639
|
+
to_chain_id: "solana",
|
|
640
|
+
token: symbol ?? options.fromToken.mint.toBase58(),
|
|
641
|
+
amount: amountToString(options.amount, decimals),
|
|
642
|
+
recipient_address: options.walletPublicKey.toBase58(),
|
|
643
|
+
nonce: nonce === null ? "1" : nonce.nonce.add(new bn_js_1.default(1)).toString(),
|
|
644
|
+
}),
|
|
645
|
+
].join("\n"));
|
|
646
|
+
const intentSignature = (0, kit_1.signatureBytes)(await options.solanaWallet.signMessage(message));
|
|
647
|
+
return web3_js_1.Ed25519Program.createInstructionWithPublicKey({
|
|
648
|
+
publicKey: options.walletPublicKey.toBytes(),
|
|
649
|
+
signature: intentSignature,
|
|
650
|
+
message: await addOffchainMessagePrefixToMessageIfNeeded(options.walletPublicKey, intentSignature, message),
|
|
651
|
+
});
|
|
652
|
+
};
|
|
653
|
+
const bridgeIn = async (options) => {
|
|
654
|
+
const solanaConnection = await options.context.getSolanaConnection();
|
|
655
|
+
const { route, transferRequest, transferParams } = await buildWormholeTransfer(options, solanaConnection);
|
|
656
|
+
// @ts-expect-error the wormhole client types are incorrect and do not
|
|
657
|
+
// properly represent the runtime representation.
|
|
658
|
+
const quote = await route.quote(transferRequest, transferParams);
|
|
659
|
+
if (quote.success) {
|
|
660
|
+
return await sdk_1.routes.checkAndCompleteTransfer(route, await route.initiate(transferRequest, {
|
|
661
|
+
address: () => options.walletPublicKey.toBase58(),
|
|
662
|
+
chain: () => "Solana",
|
|
663
|
+
signAndSend: (transactions) => Promise.all(transactions.map(({ transaction }) => options.solanaWallet.sendTransaction(
|
|
664
|
+
// Hooray for Wormhole's incomplete typing eh?
|
|
665
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
666
|
+
transaction.transaction, solanaConnection,
|
|
667
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
668
|
+
{ signers: transaction.signers, skipPreflight: true }))),
|
|
669
|
+
}, quote, sdk_1.Wormhole.chainAddress("Fogo", options.walletPublicKey.toBase58())));
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
throw quote.error;
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
exports.bridgeIn = bridgeIn;
|
|
676
|
+
const buildWormholeTransfer = async (options, connection) => {
|
|
677
|
+
const [wh, { decimals }] = await Promise.all([
|
|
678
|
+
(0, sdk_1.wormhole)(NETWORK_TO_WORMHOLE_NETWORK[options.context.network], [solana_1.default]),
|
|
679
|
+
(0, spl_token_1.getMint)(connection, options.fromToken.mint),
|
|
680
|
+
]);
|
|
681
|
+
const Route = (0, sdk_route_ntt_1.nttExecutorRoute)({
|
|
682
|
+
ntt: {
|
|
683
|
+
tokens: {
|
|
684
|
+
USDC: [
|
|
685
|
+
{
|
|
686
|
+
chain: options.fromToken.chain,
|
|
687
|
+
manager: options.fromToken.manager.toBase58(),
|
|
688
|
+
token: options.fromToken.mint.toBase58(),
|
|
689
|
+
transceiver: [
|
|
690
|
+
{
|
|
691
|
+
address: options.fromToken.transceiver.toBase58(),
|
|
692
|
+
type: "wormhole",
|
|
693
|
+
},
|
|
694
|
+
],
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
chain: options.toToken.chain,
|
|
698
|
+
manager: options.toToken.manager.toBase58(),
|
|
699
|
+
token: options.toToken.mint.toBase58(),
|
|
700
|
+
transceiver: [
|
|
701
|
+
{
|
|
702
|
+
address: options.toToken.transceiver.toBase58(),
|
|
703
|
+
type: "wormhole",
|
|
704
|
+
},
|
|
705
|
+
],
|
|
706
|
+
},
|
|
707
|
+
],
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
});
|
|
711
|
+
const route = new Route(wh);
|
|
712
|
+
const transferRequest = await sdk_1.routes.RouteTransferRequest.create(wh, {
|
|
713
|
+
source: sdk_1.Wormhole.tokenId(options.fromToken.chain, options.fromToken.mint.toBase58()),
|
|
714
|
+
destination: sdk_1.Wormhole.tokenId(options.toToken.chain, options.toToken.mint.toBase58()),
|
|
715
|
+
});
|
|
716
|
+
const validated = await route.validate(transferRequest, {
|
|
717
|
+
amount: amountToString(options.amount, decimals),
|
|
718
|
+
options: route.getDefaultOptions(),
|
|
719
|
+
});
|
|
720
|
+
if (validated.valid) {
|
|
721
|
+
return {
|
|
722
|
+
wh,
|
|
723
|
+
route,
|
|
724
|
+
transferRequest,
|
|
725
|
+
transferParams: validated.params,
|
|
726
|
+
decimals,
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
throw validated.error;
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
const NETWORK_TO_WORMHOLE_NETWORK = {
|
|
734
|
+
[connection_js_1.Network.Mainnet]: "Mainnet",
|
|
735
|
+
[connection_js_1.Network.Testnet]: "Testnet",
|
|
736
|
+
};
|
|
737
|
+
const getNonce = async (program, walletPublicKey, nonceType) => {
|
|
533
738
|
const [noncePda] = await (0, kit_1.getProgramDerivedAddress)({
|
|
534
739
|
programAddress: (0, compat_1.fromLegacyPublicKey)(program.programId),
|
|
535
|
-
seeds: [
|
|
740
|
+
seeds: [
|
|
741
|
+
Buffer.from(NONCE_TYPE_TO_SEED[nonceType]),
|
|
742
|
+
walletPublicKey.toBuffer(),
|
|
743
|
+
],
|
|
536
744
|
});
|
|
537
745
|
return program.account.nonce.fetchNullable(noncePda);
|
|
538
746
|
};
|
|
747
|
+
var NonceType;
|
|
748
|
+
(function (NonceType) {
|
|
749
|
+
NonceType[NonceType["Transfer"] = 0] = "Transfer";
|
|
750
|
+
NonceType[NonceType["Bridge"] = 1] = "Bridge";
|
|
751
|
+
})(NonceType || (NonceType = {}));
|
|
752
|
+
const NONCE_TYPE_TO_SEED = {
|
|
753
|
+
[NonceType.Transfer]: "nonce",
|
|
754
|
+
[NonceType.Bridge]: "bridge_ntt_nonce",
|
|
755
|
+
};
|
|
539
756
|
const loginTokenPayloadSchema = zod_1.z.object({
|
|
540
757
|
iat: zod_1.z.number(),
|
|
541
758
|
sessionPublicKey: zod_1.z.string(),
|
package/esm/connection.d.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import type { Transaction, Instruction, TransactionWithLifetime } from "@solana/kit";
|
|
2
|
-
import type {
|
|
3
|
-
import { Connection as Web3Connection, TransactionInstruction, VersionedTransaction, PublicKey } from "@solana/web3.js";
|
|
2
|
+
import type { TransactionError } from "@solana/web3.js";
|
|
3
|
+
import { Keypair, Connection as Web3Connection, TransactionInstruction, VersionedTransaction, PublicKey } from "@solana/web3.js";
|
|
4
4
|
export declare enum Network {
|
|
5
5
|
Testnet = 0,
|
|
6
6
|
Mainnet = 1
|
|
7
7
|
}
|
|
8
|
-
export declare const DEFAULT_RPC: {
|
|
9
|
-
0: string;
|
|
10
|
-
1: string;
|
|
11
|
-
};
|
|
12
|
-
export declare const DEFAULT_PAYMASTER: {
|
|
13
|
-
0: string;
|
|
14
|
-
1: string;
|
|
15
|
-
};
|
|
16
8
|
export declare enum TransactionResultType {
|
|
17
9
|
Success = 0,
|
|
18
10
|
Failed = 1
|
|
@@ -32,26 +24,24 @@ export type TransactionResult = ReturnType<(typeof TransactionResult)[keyof type
|
|
|
32
24
|
export declare const createSessionConnection: (options: {
|
|
33
25
|
network: Network;
|
|
34
26
|
rpc?: string | URL | undefined;
|
|
35
|
-
paymaster?: undefined;
|
|
36
|
-
sendToPaymaster?: undefined;
|
|
37
|
-
sponsor?: undefined;
|
|
38
|
-
} | ({
|
|
39
|
-
network?: Network | undefined;
|
|
40
|
-
rpc: string | URL;
|
|
41
27
|
} & ({
|
|
42
|
-
paymaster
|
|
28
|
+
paymaster?: string | URL | undefined;
|
|
43
29
|
sendToPaymaster?: undefined;
|
|
44
30
|
sponsor?: undefined;
|
|
45
31
|
} | {
|
|
46
32
|
paymaster?: undefined;
|
|
47
33
|
sendToPaymaster: (transaction: Transaction) => Promise<TransactionResult>;
|
|
48
34
|
sponsor: PublicKey;
|
|
49
|
-
}))
|
|
35
|
+
})) => {
|
|
50
36
|
rpc: import("@solana/kit").Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & import("@solana/kit").GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
|
|
51
37
|
connection: Web3Connection;
|
|
52
|
-
|
|
38
|
+
network: Network;
|
|
39
|
+
getSolanaConnection: () => Promise<Web3Connection>;
|
|
40
|
+
sendToPaymaster: (domain: string, sponsor: PublicKey, sessionKey: CryptoKeyPair | undefined, instructions: (TransactionInstruction | Instruction)[] | VersionedTransaction | (Transaction & TransactionWithLifetime), extraConfig?: {
|
|
41
|
+
addressLookupTable?: string | undefined;
|
|
42
|
+
extraSigners?: (CryptoKeyPair | Keypair)[] | undefined;
|
|
43
|
+
}) => Promise<TransactionResult>;
|
|
53
44
|
getSponsor: (domain: string) => Promise<PublicKey>;
|
|
54
|
-
getAddressLookupTables: (addressLookupTableAddress?: string) => Promise<AddressLookupTableAccount[] | undefined>;
|
|
55
45
|
};
|
|
56
46
|
export type Connection = ReturnType<typeof createSessionConnection>;
|
|
57
47
|
export {};
|
package/esm/connection.js
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
import { fromLegacyPublicKey, fromLegacyTransactionInstruction, fromVersionedTransaction, } from "@solana/compat";
|
|
1
|
+
import { fromLegacyKeypair, fromLegacyPublicKey, fromLegacyTransactionInstruction, fromVersionedTransaction, } from "@solana/compat";
|
|
2
2
|
import { createSolanaRpc, getBase64EncodedWireTransaction, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, partiallySignTransactionMessageWithSigners, pipe, addSignersToTransactionMessage, compressTransactionMessageUsingAddressLookupTables, createSignerFromKeyPair, partiallySignTransaction, } from "@solana/kit";
|
|
3
|
-
import { Connection as Web3Connection, TransactionInstruction, VersionedTransaction, PublicKey, } from "@solana/web3.js";
|
|
3
|
+
import { Keypair, Connection as Web3Connection, TransactionInstruction, VersionedTransaction, PublicKey, } from "@solana/web3.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
export var Network;
|
|
6
6
|
(function (Network) {
|
|
7
7
|
Network[Network["Testnet"] = 0] = "Testnet";
|
|
8
8
|
Network[Network["Mainnet"] = 1] = "Mainnet";
|
|
9
9
|
})(Network || (Network = {}));
|
|
10
|
-
|
|
10
|
+
const DEFAULT_RPC = {
|
|
11
11
|
[Network.Testnet]: "https://testnet.fogo.io",
|
|
12
12
|
[Network.Mainnet]: "https://mainnet.fogo.io",
|
|
13
13
|
};
|
|
14
|
-
|
|
14
|
+
const DEFAULT_PAYMASTER = {
|
|
15
15
|
[Network.Testnet]: "https://paymaster.fogo.io",
|
|
16
16
|
[Network.Mainnet]: "https://paymaster.dourolabs.app",
|
|
17
17
|
};
|
|
18
|
-
const DEFAULT_ADDRESS_LOOKUP_TABLE_ADDRESS = {
|
|
19
|
-
[Network.Testnet]: "B8cUjJMqaWWTNNSTXBmeptjWswwCH1gTSCRYv4nu7kJW",
|
|
20
|
-
[Network.Mainnet]: undefined,
|
|
21
|
-
};
|
|
22
18
|
export var TransactionResultType;
|
|
23
19
|
(function (TransactionResultType) {
|
|
24
20
|
TransactionResultType[TransactionResultType["Success"] = 0] = "Success";
|
|
@@ -36,26 +32,45 @@ const TransactionResult = {
|
|
|
36
32
|
}),
|
|
37
33
|
};
|
|
38
34
|
export const createSessionConnection = (options) => {
|
|
39
|
-
// For some reason, typescript is unable to narrow this type even though it's
|
|
40
|
-
// obvious that `rpc` can only be `undefined` if `network` is defined. I
|
|
41
|
-
// don't like the non-null assertion, but here we can guarantee it's safe (and
|
|
42
|
-
// typescript really should be able to narrow this...)
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
44
35
|
const rpcUrl = (options.rpc ?? DEFAULT_RPC[options.network]).toString();
|
|
45
36
|
const rpc = createSolanaRpc(rpcUrl);
|
|
46
37
|
const connection = new Web3Connection(rpcUrl, "confirmed");
|
|
38
|
+
const addressLookupTableCache = new Map();
|
|
47
39
|
return {
|
|
48
40
|
rpc,
|
|
49
41
|
connection,
|
|
50
|
-
|
|
42
|
+
network: options.network,
|
|
43
|
+
getSolanaConnection: createSolanaConnectionGetter(options.network),
|
|
44
|
+
sendToPaymaster: async (domain, sponsor, sessionKey, instructions, extraConfig) => {
|
|
51
45
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
52
|
-
const transaction = await buildTransaction(latestBlockhash, sessionKey, sponsor, instructions,
|
|
46
|
+
const transaction = await buildTransaction(connection, latestBlockhash, sessionKey, sponsor, instructions, addressLookupTableCache, extraConfig);
|
|
53
47
|
return sendToPaymaster(options, domain, transaction);
|
|
54
48
|
},
|
|
55
49
|
getSponsor: (domain) => getSponsor(options, domain),
|
|
56
|
-
getAddressLookupTables: (addressLookupTableAddress) => getAddressLookupTables(options, connection, addressLookupTableAddress),
|
|
57
50
|
};
|
|
58
51
|
};
|
|
52
|
+
const createSolanaConnectionGetter = (network) => {
|
|
53
|
+
let connection;
|
|
54
|
+
return async () => {
|
|
55
|
+
if (connection === undefined) {
|
|
56
|
+
const url = new URL("https://api.fogo.io/api/solana-rpc");
|
|
57
|
+
url.searchParams.set("network", NETWORK_TO_QUERY_PARAM[network]);
|
|
58
|
+
const rpcUrlRes = await fetch(url);
|
|
59
|
+
if (rpcUrlRes.status === 200) {
|
|
60
|
+
const rpcUrl = await rpcUrlRes.text();
|
|
61
|
+
connection = new Web3Connection(rpcUrl);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
throw new Error("Failed to resolve Solana RPC url");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return connection;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
const NETWORK_TO_QUERY_PARAM = {
|
|
71
|
+
[Network.Mainnet]: "mainnet",
|
|
72
|
+
[Network.Testnet]: "testnet",
|
|
73
|
+
};
|
|
59
74
|
const sendToPaymaster = async (options, domain, transaction) => {
|
|
60
75
|
if (options.sendToPaymaster === undefined) {
|
|
61
76
|
const url = new URL("/api/sponsor_and_send", options.paymaster ?? DEFAULT_PAYMASTER[options.network]);
|
|
@@ -80,29 +95,39 @@ const sendToPaymaster = async (options, domain, transaction) => {
|
|
|
80
95
|
return options.sendToPaymaster(transaction);
|
|
81
96
|
}
|
|
82
97
|
};
|
|
83
|
-
const buildTransaction = async (latestBlockhash, sessionKey, sponsor, instructions,
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
98
|
+
const buildTransaction = async (connection, latestBlockhash, sessionKey, sponsor, instructions, addressLookupTableCache, extraConfig) => {
|
|
99
|
+
const [signerKeys, addressLookupTable] = await Promise.all([
|
|
100
|
+
getSignerKeys(sessionKey, extraConfig?.extraSigners),
|
|
101
|
+
extraConfig?.addressLookupTable === undefined
|
|
102
|
+
? Promise.resolve(undefined)
|
|
103
|
+
: getAddressLookupTable(connection, addressLookupTableCache, extraConfig.addressLookupTable),
|
|
104
|
+
]);
|
|
87
105
|
if (Array.isArray(instructions)) {
|
|
106
|
+
const signers = await Promise.all(signerKeys.map((signer) => createSignerFromKeyPair(signer)));
|
|
88
107
|
return partiallySignTransactionMessageWithSigners(pipe(createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(fromLegacyPublicKey(sponsor), tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions.map((instruction) => instruction instanceof TransactionInstruction
|
|
89
108
|
? fromLegacyTransactionInstruction(instruction)
|
|
90
|
-
: instruction), tx), (tx) =>
|
|
91
|
-
fromLegacyPublicKey(table.key),
|
|
92
|
-
table.state.addresses.map((address) => fromLegacyPublicKey(address)),
|
|
93
|
-
]) ?? [])), (tx) => sessionKeySigner === undefined
|
|
109
|
+
: instruction), tx), (tx) => addressLookupTable === undefined
|
|
94
110
|
? tx
|
|
95
|
-
:
|
|
111
|
+
: compressTransactionMessageUsingAddressLookupTables(tx, {
|
|
112
|
+
[fromLegacyPublicKey(addressLookupTable.key)]: addressLookupTable.state.addresses.map((address) => fromLegacyPublicKey(address)),
|
|
113
|
+
}), (tx) => addSignersToTransactionMessage(signers, tx)));
|
|
96
114
|
}
|
|
97
115
|
else {
|
|
98
116
|
const tx = instructions instanceof VersionedTransaction
|
|
99
117
|
? fromVersionedTransaction(instructions) // VersionedTransaction has a lifetime so it's fine to cast it so we can call partiallySignTransaction
|
|
100
118
|
: instructions;
|
|
101
|
-
return
|
|
102
|
-
? tx
|
|
103
|
-
: partiallySignTransaction([sessionKey], tx);
|
|
119
|
+
return partiallySignTransaction(signerKeys, tx);
|
|
104
120
|
}
|
|
105
121
|
};
|
|
122
|
+
const getSignerKeys = async (sessionKey, extraSigners) => {
|
|
123
|
+
const extraSignerKeys = extraSigners === undefined
|
|
124
|
+
? []
|
|
125
|
+
: await Promise.all(extraSigners.map((signer) => signer instanceof Keypair ? fromLegacyKeypair(signer) : signer));
|
|
126
|
+
return [
|
|
127
|
+
...extraSignerKeys,
|
|
128
|
+
...(sessionKey === undefined ? [] : [sessionKey]),
|
|
129
|
+
];
|
|
130
|
+
};
|
|
106
131
|
const sponsorAndSendResponseSchema = z
|
|
107
132
|
.discriminatedUnion("type", [
|
|
108
133
|
z.object({
|
|
@@ -138,19 +163,20 @@ const getSponsor = async (options, domain) => {
|
|
|
138
163
|
return options.sponsor;
|
|
139
164
|
}
|
|
140
165
|
};
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
166
|
+
const getAddressLookupTable = async (connection, addressLookupTableCache, addressLookupTableAddress) => {
|
|
167
|
+
const value = addressLookupTableCache.get(addressLookupTableAddress);
|
|
168
|
+
if (value === undefined) {
|
|
169
|
+
const result = await connection.getAddressLookupTable(new PublicKey(addressLookupTableAddress));
|
|
170
|
+
if (result.value === null) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
addressLookupTableCache.set(addressLookupTableAddress, result.value);
|
|
175
|
+
return result.value;
|
|
176
|
+
}
|
|
151
177
|
}
|
|
152
178
|
else {
|
|
153
|
-
return;
|
|
179
|
+
return value;
|
|
154
180
|
}
|
|
155
181
|
};
|
|
156
182
|
class PaymasterResponseError extends Error {
|
package/esm/context.d.ts
CHANGED
|
@@ -2,14 +2,16 @@ import { Connection as Web3Connection } from "@solana/web3.js";
|
|
|
2
2
|
import type { Connection } from "./connection.js";
|
|
3
3
|
export declare const createSessionContext: (options: {
|
|
4
4
|
connection: Connection;
|
|
5
|
-
|
|
5
|
+
defaultAddressLookupTableAddress?: string | undefined;
|
|
6
6
|
domain?: string | undefined;
|
|
7
7
|
}) => Promise<{
|
|
8
8
|
chainId: string;
|
|
9
9
|
domain: string;
|
|
10
10
|
payer: import("@solana/web3.js").PublicKey;
|
|
11
|
+
getSolanaConnection: () => Promise<Web3Connection>;
|
|
11
12
|
connection: Web3Connection;
|
|
12
13
|
rpc: import("@solana/kit").Rpc<import("@solana/kit").RequestAirdropApi & import("@solana/kit").GetAccountInfoApi & import("@solana/kit").GetBalanceApi & import("@solana/kit").GetBlockApi & import("@solana/kit").GetBlockCommitmentApi & import("@solana/kit").GetBlockHeightApi & import("@solana/kit").GetBlockProductionApi & import("@solana/kit").GetBlocksApi & import("@solana/kit").GetBlocksWithLimitApi & import("@solana/kit").GetBlockTimeApi & import("@solana/kit").GetClusterNodesApi & import("@solana/kit").GetEpochInfoApi & import("@solana/kit").GetEpochScheduleApi & import("@solana/kit").GetFeeForMessageApi & import("@solana/kit").GetFirstAvailableBlockApi & import("@solana/kit").GetGenesisHashApi & import("@solana/kit").GetHealthApi & import("@solana/kit").GetHighestSnapshotSlotApi & import("@solana/kit").GetIdentityApi & import("@solana/kit").GetInflationGovernorApi & import("@solana/kit").GetInflationRateApi & import("@solana/kit").GetInflationRewardApi & import("@solana/kit").GetLargestAccountsApi & import("@solana/kit").GetLatestBlockhashApi & import("@solana/kit").GetLeaderScheduleApi & import("@solana/kit").GetMaxRetransmitSlotApi & import("@solana/kit").GetMaxShredInsertSlotApi & import("@solana/kit").GetMinimumBalanceForRentExemptionApi & import("@solana/kit").GetMultipleAccountsApi & import("@solana/kit").GetProgramAccountsApi & import("@solana/kit").GetRecentPerformanceSamplesApi & import("@solana/kit").GetRecentPrioritizationFeesApi & import("@solana/kit").GetSignaturesForAddressApi & import("@solana/kit").GetSignatureStatusesApi & import("@solana/kit").GetSlotApi & import("@solana/kit").GetSlotLeaderApi & import("@solana/kit").GetSlotLeadersApi & import("@solana/kit").GetStakeMinimumDelegationApi & import("@solana/kit").GetSupplyApi & import("@solana/kit").GetTokenAccountBalanceApi & import("@solana/kit").GetTokenAccountsByDelegateApi & import("@solana/kit").GetTokenAccountsByOwnerApi & import("@solana/kit").GetTokenLargestAccountsApi & import("@solana/kit").GetTokenSupplyApi & import("@solana/kit").GetTransactionApi & import("@solana/kit").GetTransactionCountApi & import("@solana/kit").GetVersionApi & import("@solana/kit").GetVoteAccountsApi & import("@solana/kit").IsBlockhashValidApi & import("@solana/kit").MinimumLedgerSlotApi & import("@solana/kit").SendTransactionApi & import("@solana/kit").SimulateTransactionApi>;
|
|
13
|
-
|
|
14
|
+
network: import("./connection.js").Network;
|
|
15
|
+
sendTransaction: (sessionKey: CryptoKeyPair | undefined, instructions: Parameters<typeof options.connection.sendToPaymaster>[3], extraConfig?: Parameters<typeof options.connection.sendToPaymaster>[4]) => Promise<import("./connection.js").TransactionResult>;
|
|
14
16
|
}>;
|
|
15
17
|
export type SessionContext = Awaited<ReturnType<typeof createSessionContext>>;
|
package/esm/context.js
CHANGED
|
@@ -4,16 +4,21 @@ 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
6
|
export const createSessionContext = async (options) => {
|
|
7
|
-
const addressLookupTables = await options.connection.getAddressLookupTables(options.addressLookupTableAddress);
|
|
8
7
|
const domain = getDomain(options.domain);
|
|
9
8
|
const sponsor = await options.connection.getSponsor(domain);
|
|
10
9
|
return {
|
|
11
10
|
chainId: await fetchChainId(options.connection.connection),
|
|
12
11
|
domain: getDomain(options.domain),
|
|
13
12
|
payer: sponsor,
|
|
13
|
+
getSolanaConnection: options.connection.getSolanaConnection,
|
|
14
14
|
connection: options.connection.connection,
|
|
15
15
|
rpc: options.connection.rpc,
|
|
16
|
-
|
|
16
|
+
network: options.connection.network,
|
|
17
|
+
sendTransaction: (sessionKey, instructions, extraConfig) => options.connection.sendToPaymaster(domain, sponsor, sessionKey, instructions, {
|
|
18
|
+
addressLookupTable: extraConfig?.addressLookupTable ??
|
|
19
|
+
options.defaultAddressLookupTableAddress,
|
|
20
|
+
extraSigners: extraConfig?.extraSigners,
|
|
21
|
+
}),
|
|
17
22
|
};
|
|
18
23
|
};
|
|
19
24
|
const fetchChainId = async (connection) => {
|