@faremeter/payment-solana 0.20.0 → 0.21.0

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.
Files changed (62) hide show
  1. package/dist/src/charge/client.d.ts +10 -6
  2. package/dist/src/charge/client.d.ts.map +1 -1
  3. package/dist/src/charge/client.js +117 -102
  4. package/dist/src/charge/common.d.ts +3 -3
  5. package/dist/src/charge/server.d.ts +18 -7
  6. package/dist/src/charge/server.d.ts.map +1 -1
  7. package/dist/src/charge/server.js +22 -24
  8. package/dist/src/compat.d.ts +38 -0
  9. package/dist/src/compat.d.ts.map +1 -0
  10. package/dist/src/compat.js +86 -0
  11. package/dist/src/compat.test.d.ts +3 -0
  12. package/dist/src/compat.test.d.ts.map +1 -0
  13. package/dist/src/compat.test.js +70 -0
  14. package/dist/src/exact/client.d.ts +18 -15
  15. package/dist/src/exact/client.d.ts.map +1 -1
  16. package/dist/src/exact/client.js +124 -96
  17. package/dist/src/exact/common.d.ts +1 -1
  18. package/dist/src/exact/facilitator.d.ts +19 -12
  19. package/dist/src/exact/facilitator.d.ts.map +1 -1
  20. package/dist/src/exact/facilitator.js +19 -18
  21. package/dist/src/exact/memo.d.ts +0 -2
  22. package/dist/src/exact/memo.d.ts.map +1 -1
  23. package/dist/src/exact/memo.js +0 -9
  24. package/dist/src/exact/verify.d.ts +5 -1
  25. package/dist/src/exact/verify.d.ts.map +1 -1
  26. package/dist/src/exact/verify.js +8 -2
  27. package/dist/src/exact/verify.test.js +80 -3
  28. package/dist/src/flex/client/handler.d.ts +31 -0
  29. package/dist/src/flex/client/handler.d.ts.map +1 -0
  30. package/dist/src/flex/client/handler.js +104 -0
  31. package/dist/src/flex/client/index.d.ts +3 -0
  32. package/dist/src/flex/client/index.d.ts.map +1 -0
  33. package/dist/src/flex/client/index.js +1 -0
  34. package/dist/src/flex/common.d.ts +15 -0
  35. package/dist/src/flex/common.d.ts.map +1 -0
  36. package/dist/src/flex/common.js +7 -0
  37. package/dist/src/flex/facilitator/handler.d.ts +48 -0
  38. package/dist/src/flex/facilitator/handler.d.ts.map +1 -0
  39. package/dist/src/flex/facilitator/handler.js +705 -0
  40. package/dist/src/flex/facilitator/index.d.ts +5 -0
  41. package/dist/src/flex/facilitator/index.d.ts.map +1 -0
  42. package/dist/src/flex/facilitator/index.js +2 -0
  43. package/dist/src/flex/hono/index.d.ts +3 -0
  44. package/dist/src/flex/hono/index.d.ts.map +1 -0
  45. package/dist/src/flex/hono/index.js +1 -0
  46. package/dist/src/flex/hono/upto-handler.d.ts +20 -0
  47. package/dist/src/flex/hono/upto-handler.d.ts.map +1 -0
  48. package/dist/src/flex/hono/upto-handler.js +72 -0
  49. package/dist/src/flex/hono/upto-handler.test.d.ts +3 -0
  50. package/dist/src/flex/hono/upto-handler.test.d.ts.map +1 -0
  51. package/dist/src/flex/hono/upto-handler.test.js +381 -0
  52. package/dist/src/flex/index.d.ts +4 -0
  53. package/dist/src/flex/index.d.ts.map +1 -0
  54. package/dist/src/flex/index.js +3 -0
  55. package/dist/src/flex/logger.d.ts +2 -0
  56. package/dist/src/flex/logger.d.ts.map +1 -0
  57. package/dist/src/flex/logger.js +2 -0
  58. package/dist/src/index.d.ts +1 -0
  59. package/dist/src/index.d.ts.map +1 -1
  60. package/dist/src/index.js +1 -0
  61. package/dist/tsconfig.tsbuildinfo +1 -1
  62. package/package.json +23 -7
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env pnpm tsx
2
+ import t from "tap";
3
+ import { address, createKeyPairSignerFromBytes, createSolanaRpc, } from "@solana/kit";
4
+ import { toAddress, toKeyPairSigner, toRpc } from "./compat.js";
5
+ // A deterministic 64-byte secret key for testing (32-byte seed +
6
+ // 32-byte public key, as expected by createKeyPairSignerFromBytes).
7
+ // Generated once from a known seed; the address is deterministic.
8
+ const TEST_SECRET_KEY = new Uint8Array([
9
+ 174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56,
10
+ 222, 53, 138, 189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, 15,
11
+ 185, 186, 82, 177, 240, 148, 69, 241, 227, 167, 80, 141, 89, 240, 121, 121,
12
+ 35, 172, 247, 68, 251, 226, 218, 48, 63, 176, 109, 168, 89, 238, 135,
13
+ ]);
14
+ // ---------- toAddress ----------
15
+ await t.test("toAddress: passes through a kit Address string", async (t) => {
16
+ const addr = address("11111111111111111111111111111111");
17
+ const result = toAddress(addr);
18
+ t.equal(result, addr);
19
+ });
20
+ await t.test("toAddress: converts a PublicKey-like object", async (t) => {
21
+ const fakePublicKey = {
22
+ toBase58: () => "11111111111111111111111111111111",
23
+ };
24
+ const result = toAddress(fakePublicKey);
25
+ t.equal(result, "11111111111111111111111111111111");
26
+ });
27
+ await t.test("toAddress: throws on invalid input", async (t) => {
28
+ t.throws(() => toAddress(42), {
29
+ message: /expected an Address string or PublicKey/,
30
+ });
31
+ });
32
+ // ---------- toKeyPairSigner ----------
33
+ await t.test("toKeyPairSigner: passes through a kit KeyPairSigner", async (t) => {
34
+ const signer = await createKeyPairSignerFromBytes(TEST_SECRET_KEY);
35
+ const result = await toKeyPairSigner(signer);
36
+ t.equal(result.address, signer.address);
37
+ t.ok("signMessages" in result);
38
+ });
39
+ await t.test("toKeyPairSigner: converts a Uint8Array secret key", async (t) => {
40
+ const expected = await createKeyPairSignerFromBytes(TEST_SECRET_KEY);
41
+ const result = await toKeyPairSigner(TEST_SECRET_KEY);
42
+ t.equal(result.address, expected.address);
43
+ t.ok("signMessages" in result);
44
+ });
45
+ await t.test("toKeyPairSigner: converts a Keypair-like object", async (t) => {
46
+ const expected = await createKeyPairSignerFromBytes(TEST_SECRET_KEY);
47
+ // Duck-type that matches @solana/web3.js v1 Keypair shape
48
+ const fakeKeypair = {
49
+ secretKey: TEST_SECRET_KEY,
50
+ publicKey: { toBase58: () => expected.address },
51
+ };
52
+ const result = await toKeyPairSigner(fakeKeypair);
53
+ t.equal(result.address, expected.address);
54
+ t.ok("signMessages" in result);
55
+ });
56
+ await t.test("toKeyPairSigner: throws on invalid input", async (t) => {
57
+ await t.rejects(() => toKeyPairSigner("not-a-signer"), {
58
+ message: /expected a Uint8Array, KeyPairSigner, or Keypair/,
59
+ });
60
+ });
61
+ // ---------- toRpc ----------
62
+ await t.test("toRpc: passes through an Rpc object", async (t) => {
63
+ const rpc = createSolanaRpc("https://api.devnet.solana.com");
64
+ const result = toRpc(rpc);
65
+ t.equal(result, rpc);
66
+ });
67
+ await t.test("toRpc: creates an Rpc from a URL string", async (t) => {
68
+ const result = toRpc("https://api.devnet.solana.com");
69
+ t.ok(typeof result.getLatestBlockhash === "function");
70
+ });
@@ -1,26 +1,27 @@
1
1
  import type { PaymentHandler } from "@faremeter/types/client";
2
2
  import type { SolanaCAIP2Network } from "@faremeter/info/solana";
3
- import { Connection, PublicKey, TransactionInstruction, VersionedTransaction } from "@solana/web3.js";
3
+ import { type Address, type Blockhash, type Instruction, type Rpc, type SolanaRpcApi, type Transaction } from "@solana/kit";
4
+ export type WalletLifetimeConstraint = {
5
+ blockhash: Blockhash;
6
+ lastValidBlockHeight: bigint;
7
+ };
4
8
  export type Wallet = {
5
9
  network: string | SolanaCAIP2Network;
6
- publicKey: PublicKey;
7
- buildTransaction?: (instructions: TransactionInstruction[], recentBlockHash: string) => Promise<VersionedTransaction>;
8
- partiallySignTransaction?: (tx: VersionedTransaction) => Promise<VersionedTransaction>;
9
- updateTransaction?: (tx: VersionedTransaction) => Promise<VersionedTransaction>;
10
- sendTransaction?: (tx: VersionedTransaction) => Promise<string>;
10
+ publicKey: Address;
11
+ buildTransaction?: (instructions: readonly Instruction[], lifetimeConstraint: WalletLifetimeConstraint) => Promise<Transaction>;
12
+ partiallySignTransaction?: (tx: Transaction) => Promise<Transaction>;
13
+ sendTransaction?: (tx: Transaction) => Promise<string>;
11
14
  };
12
- interface GetAssociatedTokenAddressSyncOptions {
13
- allowOwnerOffCurve?: boolean;
14
- programId?: PublicKey;
15
- associatedTokenProgramId?: PublicKey;
16
- }
17
15
  interface CreatePaymentHandlerOptions {
18
- token?: GetAssociatedTokenAddressSyncOptions;
16
+ token?: {
17
+ programId?: Address;
18
+ };
19
19
  settlementRentDestination?: string;
20
20
  features?: {
21
21
  enableSettlementAccounts?: boolean;
22
22
  };
23
23
  }
24
+ export declare function buildAndSignClientTransaction(wallet: Wallet, instructions: readonly Instruction[], payerKey: Address, lifetimeConstraint: WalletLifetimeConstraint): Promise<Transaction>;
24
25
  /**
25
26
  * Creates a payment handler for the Solana exact payment scheme.
26
27
  *
@@ -28,11 +29,13 @@ interface CreatePaymentHandlerOptions {
28
29
  * and submitted by the client to fulfill x402 payment requirements.
29
30
  *
30
31
  * @param wallet - Wallet providing signing capabilities
31
- * @param mint - SPL token mint public key
32
- * @param connection - Optional Solana connection for fetching blockhash and mint info
32
+ * @param mintInput - SPL token mint address
33
+ * @param rpcInput - Optional Solana RPC client for fetching blockhash and mint info
33
34
  * @param options - Optional configuration for token address and features
34
35
  * @returns A PaymentHandler function for use with the x402 client
35
36
  */
36
- export declare function createPaymentHandler(wallet: Wallet, mint: PublicKey, connection?: Connection, options?: CreatePaymentHandlerOptions): PaymentHandler;
37
+ export declare function createPaymentHandler(wallet: Wallet, mintInput: Address | {
38
+ toBase58(): string;
39
+ }, rpcInput?: Rpc<SolanaRpcApi> | string, options?: CreatePaymentHandlerOptions): PaymentHandler;
37
40
  export {};
38
41
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/exact/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,cAAc,EAEf,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAajE,OAAO,EAEL,UAAU,EACV,SAAS,EACT,sBAAsB,EAEtB,oBAAoB,EAErB,MAAM,iBAAiB,CAAC;AAMzB,MAAM,MAAM,MAAM,GAAG;IACnB,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAAC;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,gBAAgB,CAAC,EAAE,CACjB,YAAY,EAAE,sBAAsB,EAAE,EACtC,eAAe,EAAE,MAAM,KACpB,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,wBAAwB,CAAC,EAAE,CACzB,EAAE,EAAE,oBAAoB,KACrB,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,iBAAiB,CAAC,EAAE,CAClB,EAAE,EAAE,oBAAoB,KACrB,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,oBAAoB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACjE,CAAC;AAEF,UAAU,oCAAoC;IAC5C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,wBAAwB,CAAC,EAAE,SAAS,CAAC;CACtC;AAyFD,UAAU,2BAA2B;IACnC,KAAK,CAAC,EAAE,oCAAoC,CAAC;IAC7C,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,EAAE;QACT,wBAAwB,CAAC,EAAE,OAAO,CAAC;KACpC,CAAC;CACH;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,UAAU,CAAC,EAAE,UAAU,EACvB,OAAO,CAAC,EAAE,2BAA2B,GACpC,cAAc,CAyMhB"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/exact/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,cAAc,EAEf,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAYjE,OAAO,EAYL,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,GAAG,EACR,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAOrB,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,CACjB,YAAY,EAAE,SAAS,WAAW,EAAE,EACpC,kBAAkB,EAAE,wBAAwB,KACzC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1B,wBAAwB,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACrE,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACxD,CAAC;AAoFF,UAAU,2BAA2B;IACnC,KAAK,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,EAAE;QACT,wBAAwB,CAAC,EAAE,OAAO,CAAC;KACpC,CAAC;CACH;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,SAAS,WAAW,EAAE,EACpC,QAAQ,EAAE,OAAO,EACjB,kBAAkB,EAAE,wBAAwB,GAC3C,OAAO,CAAC,WAAW,CAAC,CAsBtB;AAwBD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO,GAAG;IAAE,QAAQ,IAAI,MAAM,CAAA;CAAE,EAC3C,QAAQ,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,MAAM,EACrC,OAAO,CAAC,EAAE,2BAA2B,GACpC,cAAc,CAwJhB"}
@@ -1,35 +1,38 @@
1
1
  import { isValidationError, throwValidationError } from "@faremeter/types";
2
- import { createAssociatedTokenAccountIdempotentInstruction, createTransferCheckedInstruction, getAssociatedTokenAddressSync, getMint, TOKEN_PROGRAM_ID, } from "@solana/spl-token";
3
- import { getBase64EncodedWireTransaction, } from "@solana/transactions";
4
- import { ComputeBudgetProgram, Connection, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction, Keypair, } from "@solana/web3.js";
2
+ import { fetchMint, findAssociatedTokenPda, getCreateAssociatedTokenIdempotentInstruction, getTransferCheckedInstruction, TOKEN_PROGRAM_ADDRESS, } from "@solana-program/token";
3
+ import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction, } from "@solana-program/compute-budget";
4
+ import { address, appendTransactionMessageInstructions, compileTransaction, createKeyPairFromPrivateKeyBytes, createKeyPairSignerFromBytes, createNoopSigner, createTransactionMessage, getBase64EncodedWireTransaction, pipe, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, } from "@solana/kit";
5
5
  import { PaymentRequirementsExtra } from "./facilitator.js";
6
6
  import { generateMatcher } from "./common.js";
7
- import { createMemoInstruction, generateMemoNonce } from "./memo.js";
8
- import { logger } from "./logger.js";
9
- function generateGetAssociatedTokenAddressSyncRest(tokenConfig) {
10
- const { allowOwnerOffCurve, programId, associatedTokenProgramId } = tokenConfig;
11
- // NOTE: These map to the trailing default args of
12
- // getAssociatedTokenAddressSync, so order matters. If things are
13
- // refactored, they should be updated to match the reality of the
14
- // implementation.
15
- return [allowOwnerOffCurve, programId, associatedTokenProgramId];
16
- }
7
+ import { generateMemoNonce } from "./memo.js";
8
+ import { getAddMemoInstruction } from "@solana-program/memo";
9
+ import { toAddress, toRpc } from "../compat.js";
17
10
  const PaymentMode = {
18
11
  ToSpec: "toSpec",
19
12
  SettlementAccount: "settlementAccount",
20
13
  };
21
14
  async function extractMetadata(args) {
22
- const { connection, mint, requirements, wallet, options } = args;
15
+ const { rpc, mint, requirements, wallet, options } = args;
23
16
  const extra = PaymentRequirementsExtra(requirements.extra);
24
17
  if (isValidationError(extra)) {
25
18
  throwValidationError("couldn't validate requirements extra field", extra);
26
19
  }
27
- let recentBlockhash;
20
+ let lifetimeConstraint;
28
21
  if (extra.recentBlockhash !== undefined) {
29
- recentBlockhash = extra.recentBlockhash;
22
+ // The server supplied a blockhash but no lastValidBlockHeight. Kit
23
+ // requires both; use a sentinel lastValidBlockHeight of 0n since it
24
+ // only affects client-side retry timing, not wire format.
25
+ lifetimeConstraint = {
26
+ blockhash: extra.recentBlockhash,
27
+ lastValidBlockHeight: 0n,
28
+ };
30
29
  }
31
- else if (connection !== undefined) {
32
- recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
30
+ else if (rpc !== undefined) {
31
+ const { value } = await rpc.getLatestBlockhash().send();
32
+ lifetimeConstraint = {
33
+ blockhash: value.blockhash,
34
+ lastValidBlockHeight: value.lastValidBlockHeight,
35
+ };
33
36
  }
34
37
  else {
35
38
  throw new Error("couldn't get the latest Solana network block hash");
@@ -38,15 +41,15 @@ async function extractMetadata(args) {
38
41
  if (extra.decimals !== undefined) {
39
42
  decimals = extra.decimals;
40
43
  }
41
- else if (connection !== undefined) {
42
- const mintInfo = await getMint(connection, mint);
43
- decimals = mintInfo.decimals;
44
+ else if (rpc !== undefined) {
45
+ const mintInfo = await fetchMint(rpc, mint);
46
+ decimals = mintInfo.data.decimals;
44
47
  }
45
48
  else {
46
49
  throw new Error("couldn't get the decimal information for the mint");
47
50
  }
48
- const payerKey = new PublicKey(extra.feePayer);
49
- const payTo = new PublicKey(requirements.payTo);
51
+ const payerKey = address(extra.feePayer);
52
+ const payTo = address(requirements.payTo);
50
53
  const amount = Number(requirements.amount);
51
54
  let paymentMode = PaymentMode.ToSpec;
52
55
  if (options?.features?.enableSettlementAccounts &&
@@ -55,11 +58,11 @@ async function extractMetadata(args) {
55
58
  paymentMode = PaymentMode.SettlementAccount;
56
59
  }
57
60
  const tokenProgramId = extra.tokenProgram
58
- ? new PublicKey(extra.tokenProgram)
59
- : (options?.token?.programId ?? TOKEN_PROGRAM_ID);
61
+ ? address(extra.tokenProgram)
62
+ : (options?.token?.programId ?? TOKEN_PROGRAM_ADDRESS);
60
63
  const memo = extra.memo;
61
64
  return {
62
- recentBlockhash,
65
+ lifetimeConstraint,
63
66
  decimals,
64
67
  payTo,
65
68
  amount,
@@ -69,6 +72,39 @@ async function extractMetadata(args) {
69
72
  memo,
70
73
  };
71
74
  }
75
+ export async function buildAndSignClientTransaction(wallet, instructions, payerKey, lifetimeConstraint) {
76
+ const sign = async (tx) => {
77
+ if (wallet.partiallySignTransaction) {
78
+ return wallet.partiallySignTransaction(tx);
79
+ }
80
+ return tx;
81
+ };
82
+ let tx;
83
+ if (wallet.buildTransaction) {
84
+ tx = await wallet.buildTransaction(instructions, lifetimeConstraint);
85
+ }
86
+ else {
87
+ const message = pipe(createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayer(payerKey, m), (m) => setTransactionMessageLifetimeUsingBlockhash(lifetimeConstraint, m), (m) => appendTransactionMessageInstructions(instructions, m));
88
+ tx = compileTransaction(message);
89
+ }
90
+ return sign(tx);
91
+ }
92
+ async function generateSettleSigner() {
93
+ // Build a fresh ed25519 keypair locally and also return its wire-format
94
+ // bytes (32-byte privkey || 32-byte pubkey) so the facilitator can
95
+ // reconstruct the signer on settlement via createKeyPairSignerFromBytes.
96
+ const privateKeyBytes = crypto.getRandomValues(new Uint8Array(32));
97
+ // @solana/keys' CryptoKeyPair is returned here; the lib in our tsconfig
98
+ // doesn't include DOM types so we widen to a minimal local shape.
99
+ const keyPair = (await createKeyPairFromPrivateKeyBytes(privateKeyBytes,
100
+ /* extractable */ true));
101
+ const publicKeyBytes = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
102
+ const secretKey = new Uint8Array(64);
103
+ secretKey.set(privateKeyBytes);
104
+ secretKey.set(publicKeyBytes, 32);
105
+ const signer = await createKeyPairSignerFromBytes(secretKey);
106
+ return { signer, secretKey };
107
+ }
72
108
  /**
73
109
  * Creates a payment handler for the Solana exact payment scheme.
74
110
  *
@@ -76,101 +112,93 @@ async function extractMetadata(args) {
76
112
  * and submitted by the client to fulfill x402 payment requirements.
77
113
  *
78
114
  * @param wallet - Wallet providing signing capabilities
79
- * @param mint - SPL token mint public key
80
- * @param connection - Optional Solana connection for fetching blockhash and mint info
115
+ * @param mintInput - SPL token mint address
116
+ * @param rpcInput - Optional Solana RPC client for fetching blockhash and mint info
81
117
  * @param options - Optional configuration for token address and features
82
118
  * @returns A PaymentHandler function for use with the x402 client
83
119
  */
84
- export function createPaymentHandler(wallet, mint, connection, options) {
85
- const tokenConfig = options?.token ?? {};
86
- let hasWarnedAboutDeprecation = false;
87
- const signTransaction = async (tx) => {
88
- if (wallet.partiallySignTransaction) {
89
- return wallet.partiallySignTransaction(tx);
90
- }
91
- if (wallet.updateTransaction) {
92
- if (!hasWarnedAboutDeprecation) {
93
- logger.warning("wallet.partiallySignTransaction is not available, falling back to updateTransaction");
94
- hasWarnedAboutDeprecation = true;
95
- }
96
- return wallet.updateTransaction(tx);
97
- }
98
- return tx;
99
- };
100
- const { isMatchingRequirement } = generateMatcher(wallet.network, mint ? mint.toBase58() : "sol");
120
+ export function createPaymentHandler(wallet, mintInput, rpcInput, options) {
121
+ const mint = toAddress(mintInput);
122
+ const rpc = rpcInput ? toRpc(rpcInput) : undefined;
123
+ const { isMatchingRequirement } = generateMatcher(wallet.network, mint);
101
124
  return async (_context, accepts) => {
102
125
  const compatibleRequirements = accepts.filter(isMatchingRequirement);
103
126
  const res = compatibleRequirements.map((requirements) => {
104
127
  const exec = async () => {
105
- const { recentBlockhash, decimals, payTo, amount, payerKey, paymentMode, tokenProgramId, memo, } = await extractMetadata({
106
- connection,
128
+ const { lifetimeConstraint, decimals, payTo, amount, payerKey, paymentMode, tokenProgramId, memo, } = await extractMetadata({
129
+ rpc,
107
130
  mint,
108
131
  requirements,
109
132
  wallet,
110
133
  options,
111
134
  });
112
- const getAssociatedTokenAddressSyncRest = generateGetAssociatedTokenAddressSyncRest({
113
- ...tokenConfig,
114
- programId: tokenProgramId,
115
- });
116
- const instructions = [
117
- ComputeBudgetProgram.setComputeUnitLimit({
118
- units: 50_000,
119
- }),
120
- ComputeBudgetProgram.setComputeUnitPrice({
121
- microLamports: 1,
122
- }),
135
+ const baseInstructions = [
136
+ getSetComputeUnitLimitInstruction({ units: 50_000 }),
137
+ getSetComputeUnitPriceInstruction({ microLamports: 1n }),
123
138
  ];
124
- const sourceAccount = getAssociatedTokenAddressSync(mint, wallet.publicKey, ...getAssociatedTokenAddressSyncRest);
139
+ const walletSigner = createNoopSigner(wallet.publicKey);
140
+ const [sourceAccount] = await findAssociatedTokenPda({
141
+ mint,
142
+ owner: wallet.publicKey,
143
+ tokenProgram: tokenProgramId,
144
+ });
125
145
  switch (paymentMode) {
126
146
  case PaymentMode.ToSpec: {
127
- const receiverAccount = getAssociatedTokenAddressSync(mint, payTo, ...getAssociatedTokenAddressSyncRest);
128
- instructions.push(createTransferCheckedInstruction(sourceAccount, mint, receiverAccount, wallet.publicKey, amount, decimals, undefined, tokenProgramId), createMemoInstruction(memo ?? generateMemoNonce()));
129
- let tx;
130
- if (wallet.buildTransaction) {
131
- tx = await wallet.buildTransaction(instructions, recentBlockhash);
132
- }
133
- else {
134
- const message = new TransactionMessage({
135
- instructions,
136
- payerKey,
137
- recentBlockhash,
138
- }).compileToV0Message();
139
- tx = new VersionedTransaction(message);
140
- }
141
- tx = await signTransaction(tx);
142
- const base64EncodedWireTransaction = getBase64EncodedWireTransaction({
143
- messageBytes: tx.message.serialize(),
144
- signatures: tx.signatures,
147
+ const [receiverAccount] = await findAssociatedTokenPda({
148
+ mint,
149
+ owner: payTo,
150
+ tokenProgram: tokenProgramId,
145
151
  });
152
+ const instructions = [
153
+ ...baseInstructions,
154
+ getTransferCheckedInstruction({
155
+ source: sourceAccount,
156
+ mint,
157
+ destination: receiverAccount,
158
+ authority: walletSigner,
159
+ amount,
160
+ decimals,
161
+ }, { programAddress: tokenProgramId }),
162
+ getAddMemoInstruction({ memo: memo ?? generateMemoNonce() }),
163
+ ];
164
+ const tx = await buildAndSignClientTransaction(wallet, instructions, payerKey, lifetimeConstraint);
146
165
  const payload = {
147
- transaction: base64EncodedWireTransaction,
166
+ transaction: getBase64EncodedWireTransaction(tx),
148
167
  };
149
168
  return { payload };
150
169
  }
151
170
  case PaymentMode.SettlementAccount: {
152
- const settleKeypair = Keypair.generate();
153
- const settleATA = getAssociatedTokenAddressSync(mint, settleKeypair.publicKey, ...getAssociatedTokenAddressSyncRest);
154
- instructions.push(createAssociatedTokenAccountIdempotentInstruction(wallet.publicKey, settleATA, settleKeypair.publicKey, mint, tokenProgramId, tokenConfig.associatedTokenProgramId), createTransferCheckedInstruction(sourceAccount, mint, settleATA, wallet.publicKey, amount, decimals, undefined, tokenProgramId));
155
- let tx;
156
- if (wallet.buildTransaction) {
157
- tx = await wallet.buildTransaction(instructions, recentBlockhash);
158
- }
159
- else {
160
- const message = new TransactionMessage({
161
- instructions,
162
- payerKey,
163
- recentBlockhash,
164
- }).compileToV0Message();
165
- tx = new VersionedTransaction(message);
166
- }
167
- tx = await signTransaction(tx);
171
+ const { signer: settleSigner, secretKey: settleSecretBytes } = await generateSettleSigner();
172
+ const [settleATA] = await findAssociatedTokenPda({
173
+ mint,
174
+ owner: settleSigner.address,
175
+ tokenProgram: tokenProgramId,
176
+ });
177
+ const instructions = [
178
+ ...baseInstructions,
179
+ getCreateAssociatedTokenIdempotentInstruction({
180
+ ata: settleATA,
181
+ owner: settleSigner.address,
182
+ payer: walletSigner,
183
+ mint,
184
+ tokenProgram: tokenProgramId,
185
+ }),
186
+ getTransferCheckedInstruction({
187
+ source: sourceAccount,
188
+ mint,
189
+ destination: settleATA,
190
+ authority: walletSigner,
191
+ amount,
192
+ decimals,
193
+ }, { programAddress: tokenProgramId }),
194
+ ];
195
+ const tx = await buildAndSignClientTransaction(wallet, instructions, payerKey, lifetimeConstraint);
168
196
  if (!wallet.sendTransaction) {
169
197
  throw new Error("wallet must support sending transactions to use settlement accounts with exact");
170
198
  }
171
199
  const transactionSignature = await wallet.sendTransaction(tx);
172
- const settleSecretKey = Buffer.from(settleKeypair.secretKey).toString("base64");
173
- const settlementRentDestination = options?.settlementRentDestination ?? wallet.publicKey.toBase58();
200
+ const settleSecretKey = Buffer.from(settleSecretBytes).toString("base64");
201
+ const settlementRentDestination = options?.settlementRentDestination ?? wallet.publicKey;
174
202
  const payload = {
175
203
  settleSecretKey,
176
204
  transactionSignature,
@@ -1,7 +1,7 @@
1
1
  import { type SolanaCAIP2Network } from "@faremeter/info/solana";
2
2
  export declare const x402Scheme = "exact";
3
3
  export declare function generateMatcher(network: string | SolanaCAIP2Network, asset: string): {
4
- matchTuple: import("arktype/internal/methods/object.ts").ObjectType<{
4
+ matchTuple: import("arktype/internal/variants/object.ts").ObjectType<{
5
5
  scheme: (In: string) => import("arktype/internal/attributes.ts").To<Lowercase<string>>;
6
6
  network: (In: string) => import("arktype/internal/attributes.ts").To<Lowercase<string>>;
7
7
  asset: (In: string) => import("arktype/internal/attributes.ts").To<Lowercase<string>>;
@@ -2,15 +2,14 @@ import { type x402PaymentRequirements, type x402PaymentPayload, type x402SettleR
2
2
  import type { FacilitatorHandler } from "@faremeter/types/facilitator";
3
3
  import { type SolanaCAIP2Network } from "@faremeter/info/solana";
4
4
  import { fetchMint } from "@solana-program/token";
5
- import { type Rpc, type SolanaRpcApi } from "@solana/kit";
5
+ import { type Address, type KeyPairSigner, type Rpc, type SolanaRpcApi } from "@solana/kit";
6
6
  import type { TransactionError } from "@solana/rpc-types";
7
- import { Keypair, PublicKey } from "@solana/web3.js";
8
7
  import { logger } from "./logger.js";
9
8
  export interface HookBaseArgs {
10
9
  network: string | SolanaCAIP2Network;
11
10
  rpc: Rpc<SolanaRpcApi>;
12
- feePayerKeypair: Keypair;
13
- mint: PublicKey;
11
+ feePayerSigner: KeyPairSigner;
12
+ mint: Address;
14
13
  mintInfo: Awaited<ReturnType<typeof fetchMint>>;
15
14
  requirements: x402PaymentRequirements;
16
15
  payment: x402PaymentPayload;
@@ -24,11 +23,11 @@ export interface FacilitatorHooks {
24
23
  afterVerify?: HookResponseFuncs<x402VerifyResponse>;
25
24
  afterSettle?: HookResponseFuncs<x402SettleResponse>;
26
25
  }
27
- export declare const PaymentRequirementsExtraFeatures: import("arktype/internal/methods/object.ts").ObjectType<{
26
+ export declare const PaymentRequirementsExtraFeatures: import("arktype/internal/variants/object.ts").ObjectType<{
28
27
  xSettlementAccountSupported?: boolean;
29
28
  }, {}>;
30
29
  export type PaymentRequirementsExtraFeatures = typeof PaymentRequirementsExtraFeatures.infer;
31
- export declare const PaymentRequirementsExtra: import("arktype/internal/methods/object.ts").ObjectType<{
30
+ export declare const PaymentRequirementsExtra: import("arktype/internal/variants/object.ts").ObjectType<{
32
31
  feePayer: string;
33
32
  decimals?: number;
34
33
  recentBlockhash?: string;
@@ -43,20 +42,21 @@ interface FacilitatorOptions {
43
42
  retryDelayMs?: number;
44
43
  maxPriorityFee?: number;
45
44
  maxTransactionAge?: number;
45
+ requireMemo?: boolean;
46
46
  features?: {
47
47
  enableSettlementAccounts?: boolean;
48
48
  enableDuplicateCheck?: boolean;
49
49
  };
50
50
  hooks?: readonly FacilitatorHooks[];
51
51
  }
52
- export declare const PaymentPayloadTransaction: import("arktype/internal/methods/object.ts").ObjectType<{
52
+ export declare const PaymentPayloadTransaction: import("arktype/internal/variants/object.ts").ObjectType<{
53
53
  transaction: (In: string) => import("arktype").Out<Readonly<{
54
54
  messageBytes: import("@solana/transactions").TransactionMessageBytes;
55
55
  signatures: import("@solana/transactions").SignaturesMap;
56
56
  }>>;
57
57
  }, {}>;
58
58
  export type PaymentPayloadTransaction = typeof PaymentPayloadTransaction.infer;
59
- export declare const PaymentPayloadSettlementAccount: import("arktype/internal/methods/object.ts").ObjectType<{
59
+ export declare const PaymentPayloadSettlementAccount: import("arktype/internal/variants/object.ts").ObjectType<{
60
60
  transactionSignature: string;
61
61
  settleSecretKey: (In: string) => import("arktype").Out<Uint8Array<ArrayBuffer>>;
62
62
  settlementRentDestination?: string;
@@ -70,12 +70,19 @@ export declare function transactionErrorToString(t: TransactionError): string;
70
70
  * fee payer keypair, and submits them to the Solana network.
71
71
  *
72
72
  * @param network - Solana network identifier (cluster name, CAIP-2 string, or SolanaCAIP2Network object)
73
- * @param rpc - Solana RPC client
74
- * @param feePayerKeypair - Keypair for paying transaction fees
75
- * @param mint - SPL token mint public key
73
+ * @param rpcInput - Solana RPC client
74
+ * @param feePayerSignerInput - Keypair or signer for paying transaction fees
75
+ * @param mintInput - SPL token mint public key
76
76
  * @param config - Optional configuration for retries, fees, and hooks
77
77
  * @returns A FacilitatorHandler for processing Solana exact payments
78
78
  */
79
- export declare const createFacilitatorHandler: (network: string | SolanaCAIP2Network, rpc: Rpc<SolanaRpcApi>, feePayerKeypair: Keypair, mint: PublicKey, config?: FacilitatorOptions) => Promise<FacilitatorHandler>;
79
+ export declare const createFacilitatorHandler: (network: string | SolanaCAIP2Network, rpcInput: Rpc<SolanaRpcApi> | string, feePayerSignerInput: KeyPairSigner | {
80
+ secretKey: Uint8Array;
81
+ publicKey: {
82
+ toBase58(): string;
83
+ };
84
+ }, mintInput: Address | {
85
+ toBase58(): string;
86
+ }, config?: FacilitatorOptions) => Promise<FacilitatorHandler>;
80
87
  export {};
81
88
  //# sourceMappingURL=facilitator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"facilitator.d.ts","sourceRoot":"","sources":["../../../src/exact/facilitator.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EAExB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,SAAS,EAIV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAYL,KAAK,GAAG,EACR,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAOrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGrD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAKlC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAAC;IACrC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;IAChD,YAAY,EAAE,uBAAuB,CAAC;IACtC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,OAAO,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,gBAAgB,CAAC,QAAQ,IAAI,YAAY,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,iBAAiB,CAAC,QAAQ,IAAI,CACxC,IAAI,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvC,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;IACpD,WAAW,CAAC,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;CACrD;AAED,eAAO,MAAM,gCAAgC;;MAE3C,CAAC;AAEH,MAAM,MAAM,gCAAgC,GAC1C,OAAO,gCAAgC,CAAC,KAAK,CAAC;AAEhD,eAAO,MAAM,wBAAwB;;;;;;;;;MAOnC,CAAC;AAEH,UAAU,kBAAkB;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE;QACT,wBAAwB,CAAC,EAAE,OAAO,CAAC;QACnC,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,CAAC;IACF,KAAK,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACrC;AASD,eAAO,MAAM,yBAAyB;;;;;MAEpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,OAAO,yBAAyB,CAAC,KAAK,CAAC;AAE/E,eAAO,MAAM,+BAA+B;;;;MAM1C,CAAC;AACH,MAAM,MAAM,+BAA+B,GACzC,OAAO,+BAA+B,CAAC,KAAK,CAAC;AAE/C,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,gBAAgB,UAY3D;AAiDD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,MAAM,GAAG,kBAAkB,EACpC,KAAK,GAAG,CAAC,YAAY,CAAC,EACtB,iBAAiB,OAAO,EACxB,MAAM,SAAS,EACf,SAAS,kBAAkB,KAC1B,OAAO,CAAC,kBAAkB,CA0c5B,CAAC"}
1
+ {"version":3,"file":"facilitator.d.ts","sourceRoot":"","sources":["../../../src/exact/facilitator.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EAExB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,SAAS,EAIV,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAYL,KAAK,OAAO,EACZ,KAAK,aAAa,EAClB,KAAK,GAAG,EACR,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAOrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAI1D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAKlC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAAC;IACrC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC;IACvB,cAAc,EAAE,aAAa,CAAC;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;IAChD,YAAY,EAAE,uBAAuB,CAAC;IACtC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,OAAO,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,gBAAgB,CAAC,QAAQ,IAAI,YAAY,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,iBAAiB,CAAC,QAAQ,IAAI,CACxC,IAAI,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvC,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;IACpD,WAAW,CAAC,EAAE,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;CACrD;AAED,eAAO,MAAM,gCAAgC;;MAE3C,CAAC;AAEH,MAAM,MAAM,gCAAgC,GAC1C,OAAO,gCAAgC,CAAC,KAAK,CAAC;AAEhD,eAAO,MAAM,wBAAwB;;;;;;;;;MAOnC,CAAC;AAEH,UAAU,kBAAkB;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAI3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE;QACT,wBAAwB,CAAC,EAAE,OAAO,CAAC;QACnC,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,CAAC;IACF,KAAK,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACrC;AASD,eAAO,MAAM,yBAAyB;;;;;MAEpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,OAAO,yBAAyB,CAAC,KAAK,CAAC;AAE/E,eAAO,MAAM,+BAA+B;;;;MAM1C,CAAC;AACH,MAAM,MAAM,+BAA+B,GACzC,OAAO,+BAA+B,CAAC,KAAK,CAAC;AAE/C,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,gBAAgB,UAY3D;AAiDD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,MAAM,GAAG,kBAAkB,EACpC,UAAU,GAAG,CAAC,YAAY,CAAC,GAAG,MAAM,EACpC,qBACI,aAAa,GACb;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAAA;CAAE,EAChE,WAAW,OAAO,GAAG;IAAE,QAAQ,IAAI,MAAM,CAAA;CAAE,EAC3C,SAAS,kBAAkB,KAC1B,OAAO,CAAC,kBAAkB,CAuc5B,CAAC"}