@faremeter/payment-solana 0.8.0 → 0.10.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.
@@ -7,5 +7,5 @@ export type Wallet = {
7
7
  updateTransaction?: (tx: VersionedTransaction) => Promise<VersionedTransaction>;
8
8
  sendTransaction?: (tx: VersionedTransaction) => Promise<string>;
9
9
  };
10
- export declare function createPaymentHandler(wallet: Wallet, mint: PublicKey, connection: Connection): PaymentHandler;
10
+ export declare function createPaymentHandler(wallet: Wallet, mint: PublicKey, connection?: Connection): PaymentHandler;
11
11
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/exact/client.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,cAAc,EAEf,MAAM,yBAAyB,CAAC;AAWjC,OAAO,EAEL,UAAU,EACV,SAAS,EACT,sBAAsB,EAEtB,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAQzB,MAAM,MAAM,MAAM,GAAG;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,SAAS,CAAC;IACrB,gBAAgB,CAAC,EAAE,CACjB,YAAY,EAAE,sBAAsB,EAAE,EACtC,eAAe,EAAE,MAAM,KACpB,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,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,UAAU,GACrB,cAAc,CAqGhB"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/exact/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,cAAc,EAEf,MAAM,yBAAyB,CAAC;AAWjC,OAAO,EAEL,UAAU,EACV,SAAS,EACT,sBAAsB,EAEtB,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAIzB,MAAM,MAAM,MAAM,GAAG;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,SAAS,CAAC;IACrB,gBAAgB,CAAC,EAAE,CACjB,YAAY,EAAE,sBAAsB,EAAE,EACtC,eAAe,EAAE,MAAM,KACpB,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,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,UAAU,CAAC,EAAE,UAAU,GACtB,cAAc,CAwHhB"}
@@ -1,24 +1,41 @@
1
- import { isValidationError, throwValidationError, caseInsensitiveLiteral, } from "@faremeter/types";
1
+ import { isValidationError, throwValidationError } from "@faremeter/types";
2
2
  import { createTransferCheckedInstruction, getAssociatedTokenAddressSync, getMint, } from "@solana/spl-token";
3
3
  import { getBase64EncodedWireTransaction, } from "@solana/transactions";
4
4
  import { ComputeBudgetProgram, Connection, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction, } from "@solana/web3.js";
5
- import { type } from "arktype";
6
- import { PaymentRequirementsExtra, x402Scheme, lookupX402Network, } from "./facilitator.js";
5
+ import { PaymentRequirementsExtra } from "./facilitator.js";
6
+ import { generateMatcher } from "./common.js";
7
7
  export function createPaymentHandler(wallet, mint, connection) {
8
- const matcher = type({
9
- scheme: caseInsensitiveLiteral(x402Scheme),
10
- network: caseInsensitiveLiteral(lookupX402Network(wallet.network)),
11
- asset: caseInsensitiveLiteral(mint ? mint.toBase58() : "sol"),
12
- });
8
+ const { matchTupleAndAsset } = generateMatcher(wallet.network, mint ? mint.toBase58() : "sol");
13
9
  return async (context, accepts) => {
14
10
  const res = accepts
15
- .filter((r) => !isValidationError(matcher(r)))
11
+ .filter((r) => !isValidationError(matchTupleAndAsset(r)))
16
12
  .map((requirements) => {
17
13
  const extra = PaymentRequirementsExtra(requirements.extra);
18
14
  if (isValidationError(extra)) {
19
15
  throwValidationError("couldn't validate requirements extra field", extra);
20
16
  }
21
17
  const exec = async () => {
18
+ let recentBlockhash;
19
+ if (extra.recentBlockhash !== undefined) {
20
+ recentBlockhash = extra.recentBlockhash;
21
+ }
22
+ else if (connection !== undefined) {
23
+ recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
24
+ }
25
+ else {
26
+ throw new Error("couldn't get the latest Solana network block hash");
27
+ }
28
+ let decimals;
29
+ if (extra.decimals !== undefined) {
30
+ decimals = extra.decimals;
31
+ }
32
+ else if (connection !== undefined) {
33
+ const mintInfo = await getMint(connection, mint);
34
+ decimals = mintInfo.decimals;
35
+ }
36
+ else {
37
+ throw new Error("couldn't get the decimal information for the mint");
38
+ }
22
39
  const paymentRequirements = {
23
40
  ...extra,
24
41
  amount: Number(requirements.maxAmountRequired),
@@ -26,7 +43,6 @@ export function createPaymentHandler(wallet, mint, connection) {
26
43
  };
27
44
  const sourceAccount = getAssociatedTokenAddressSync(mint, wallet.publicKey);
28
45
  const receiverAccount = getAssociatedTokenAddressSync(mint, paymentRequirements.receiver);
29
- const mintInfo = await getMint(connection, mint);
30
46
  const instructions = [
31
47
  ComputeBudgetProgram.setComputeUnitLimit({
32
48
  units: 50_000,
@@ -34,10 +50,8 @@ export function createPaymentHandler(wallet, mint, connection) {
34
50
  ComputeBudgetProgram.setComputeUnitPrice({
35
51
  microLamports: 1,
36
52
  }),
37
- createTransferCheckedInstruction(sourceAccount, mint, receiverAccount, wallet.publicKey, paymentRequirements.amount, mintInfo.decimals),
53
+ createTransferCheckedInstruction(sourceAccount, mint, receiverAccount, wallet.publicKey, paymentRequirements.amount, decimals),
38
54
  ];
39
- const recentBlockhash = (await connection.getLatestBlockhash())
40
- .blockhash;
41
55
  let tx;
42
56
  if (wallet.buildTransaction) {
43
57
  tx = await wallet.buildTransaction(instructions, recentBlockhash);
@@ -0,0 +1,13 @@
1
+ export declare const x402Scheme = "exact";
2
+ export declare function generateMatcher(network: string, asset: string): {
3
+ matchTuple: import("arktype/internal/methods/object.ts").ObjectType<{
4
+ scheme: (In: string) => import("arktype/internal/attributes.ts").To<"exact">;
5
+ network: (In: string) => import("arktype/internal/attributes.ts").To<Lowercase<string>>;
6
+ }, {}>;
7
+ matchTupleAndAsset: import("arktype/internal/methods/object.ts").ObjectType<{
8
+ scheme: (In: string) => import("arktype/internal/attributes.ts").To<"exact">;
9
+ network: (In: string) => import("arktype/internal/attributes.ts").To<Lowercase<string>>;
10
+ asset: (In: string) => import("arktype/internal/attributes.ts").To<Lowercase<string>>;
11
+ }, {}>;
12
+ };
13
+ //# sourceMappingURL=common.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/exact/common.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;EAa7D"}
@@ -0,0 +1,17 @@
1
+ import { type } from "arktype";
2
+ import { caseInsensitiveLiteral } from "@faremeter/types";
3
+ import { lookupX402Network } from "@faremeter/info/solana";
4
+ export const x402Scheme = "exact";
5
+ export function generateMatcher(network, asset) {
6
+ const matchTuple = type({
7
+ scheme: caseInsensitiveLiteral(x402Scheme),
8
+ network: caseInsensitiveLiteral(...lookupX402Network(network)),
9
+ });
10
+ const matchTupleAndAsset = matchTuple.and({
11
+ asset: caseInsensitiveLiteral(asset),
12
+ });
13
+ return {
14
+ matchTuple,
15
+ matchTupleAndAsset,
16
+ };
17
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env pnpm tsx
2
+ export {};
3
+ //# sourceMappingURL=common.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.test.d.ts","sourceRoot":"","sources":["../../../src/exact/common.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env pnpm tsx
2
+ import t from "tap";
3
+ import { isValidationError as iVE } from "@faremeter/types";
4
+ import { lookupKnownSPLToken } from "@faremeter/info/solana";
5
+ import { generateMatcher } from "./common.js";
6
+ await t.test("testBasicMatching", async (t) => {
7
+ {
8
+ const tokenInfo = lookupKnownSPLToken("mainnet-beta", "USDC");
9
+ if (tokenInfo === undefined) {
10
+ t.bailout("couldn't find SPL token");
11
+ return;
12
+ }
13
+ const { matchTuple, matchTupleAndAsset } = generateMatcher("mainnet-beta", tokenInfo.address);
14
+ const req = {
15
+ network: "solana-mainnet-beta",
16
+ scheme: "exact",
17
+ asset: tokenInfo.address,
18
+ };
19
+ t.ok(!iVE(matchTuple(req)));
20
+ t.ok(!iVE(matchTupleAndAsset(req)));
21
+ t.ok(!iVE(matchTupleAndAsset({
22
+ ...req,
23
+ network: "solana",
24
+ })));
25
+ t.ok(iVE(matchTuple({
26
+ ...req,
27
+ network: "foobar",
28
+ })));
29
+ t.ok(iVE(matchTuple({
30
+ ...req,
31
+ scheme: "fner",
32
+ })));
33
+ }
34
+ t.end();
35
+ });
@@ -2,9 +2,10 @@ import type { FacilitatorHandler } from "@faremeter/types/facilitator";
2
2
  import { type Rpc, type SolanaRpcApi } from "@solana/kit";
3
3
  import type { TransactionError } from "@solana/rpc-types";
4
4
  import { Keypair, type PublicKey } from "@solana/web3.js";
5
- export declare const x402Scheme = "exact";
6
5
  export declare const PaymentRequirementsExtra: import("arktype/internal/methods/object.ts").ObjectType<{
7
6
  feePayer: string;
7
+ decimals?: number;
8
+ recentBlockhash?: string;
8
9
  }, {}>;
9
10
  export declare const PaymentPayload: import("arktype/internal/methods/object.ts").ObjectType<{
10
11
  transaction: (In: string) => import("arktype").Out<Readonly<{
@@ -12,7 +13,6 @@ export declare const PaymentPayload: import("arktype/internal/methods/object.ts"
12
13
  signatures: import("@solana/transactions").SignaturesMap;
13
14
  }>>;
14
15
  }, {}>;
15
- export declare const lookupX402Network: (network: string) => string;
16
16
  export declare function transactionErrorToString(t: TransactionError): string;
17
17
  export declare const createFacilitatorHandler: (network: string, rpc: Rpc<SolanaRpcApi>, feePayerKeypair: Keypair, mint: PublicKey, maxRetries?: number, retryDelayMs?: number) => FacilitatorHandler;
18
18
  //# sourceMappingURL=facilitator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"facilitator.d.ts","sourceRoot":"","sources":["../../../src/exact/facilitator.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,OAAO,EAML,KAAK,GAAG,EACR,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AAOrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK1D,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,eAAO,MAAM,wBAAwB;;MAEnC,CAAC;AAmBH,eAAO,MAAM,cAAc;;;;;MAEzB,CAAC;AAEH,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,WAEhD,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,gBAAgB,UAY3D;AAiDD,eAAO,MAAM,wBAAwB,GACnC,SAAS,MAAM,EACf,KAAK,GAAG,CAAC,YAAY,CAAC,EACtB,iBAAiB,OAAO,EACxB,MAAM,SAAS,EACf,mBAAe,EACf,qBAAmB,KAClB,kBAuGF,CAAC"}
1
+ {"version":3,"file":"facilitator.d.ts","sourceRoot":"","sources":["../../../src/exact/facilitator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAML,KAAK,GAAG,EACR,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AAOrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAM1D,eAAO,MAAM,wBAAwB;;;;MAInC,CAAC;AAmBH,eAAO,MAAM,cAAc;;;;;MAEzB,CAAC;AAEH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,gBAAgB,UAY3D;AAiDD,eAAO,MAAM,wBAAwB,GACnC,SAAS,MAAM,EACf,KAAK,GAAG,CAAC,YAAY,CAAC,EACtB,iBAAiB,OAAO,EACxB,MAAM,SAAS,EACf,mBAAe,EACf,qBAAmB,KAClB,kBAkHF,CAAC"}
@@ -1,4 +1,5 @@
1
- import { isValidationError, caseInsensitiveLiteral } from "@faremeter/types";
1
+ import { isValidationError } from "@faremeter/types";
2
+ import { lookupX402Network } from "@faremeter/info/solana";
2
3
  import { fetchMint } from "@solana-program/token";
3
4
  import { address, createKeyPairSignerFromBytes, decompileTransactionMessage, getBase64Encoder, getCompiledTransactionMessageDecoder, } from "@solana/kit";
4
5
  import { getBase64EncodedWireTransaction, getTransactionDecoder, partiallySignTransaction, } from "@solana/transactions";
@@ -6,9 +7,11 @@ import { Keypair } from "@solana/web3.js";
6
7
  import { type } from "arktype";
7
8
  import { isValidTransaction } from "./verify.js";
8
9
  import { logger } from "./logger.js";
9
- export const x402Scheme = "exact";
10
+ import { x402Scheme, generateMatcher } from "./common.js";
10
11
  export const PaymentRequirementsExtra = type({
11
12
  feePayer: "string",
13
+ decimals: "number?",
14
+ recentBlockhash: "string?",
12
15
  });
13
16
  function errorResponse(msg) {
14
17
  logger.error(msg);
@@ -28,9 +31,6 @@ const TransactionString = type("string").pipe.try((tx) => {
28
31
  export const PaymentPayload = type({
29
32
  transaction: TransactionString,
30
33
  });
31
- export const lookupX402Network = (network) => {
32
- return `solana-${network}`;
33
- };
34
34
  export function transactionErrorToString(t) {
35
35
  if (typeof t == "string") {
36
36
  return t;
@@ -73,19 +73,23 @@ const sendTransaction = async (rpc, signedTransaction, maxRetries, retryDelayMs)
73
73
  return { success: false, error: "Transaction confirmation timeout" };
74
74
  };
75
75
  export const createFacilitatorHandler = (network, rpc, feePayerKeypair, mint, maxRetries = 30, retryDelayMs = 1000) => {
76
- const checkTuple = type({
77
- scheme: caseInsensitiveLiteral(x402Scheme),
78
- network: caseInsensitiveLiteral(lookupX402Network(network)),
79
- });
80
- const checkTupleAndAsset = checkTuple.and({
81
- asset: caseInsensitiveLiteral(mint.toBase58()),
82
- });
76
+ const { matchTuple, matchTupleAndAsset } = generateMatcher(network, mint.toBase58());
77
+ const getSupported = () => {
78
+ return lookupX402Network(network).map((network) => Promise.resolve({
79
+ x402Version: 1,
80
+ scheme: x402Scheme,
81
+ network,
82
+ extra: {
83
+ feePayer: feePayerKeypair.publicKey.toString(),
84
+ },
85
+ }));
86
+ };
83
87
  const getRequirements = async (req) => {
84
88
  const recentBlockhash = (await rpc.getLatestBlockhash().send()).value
85
89
  .blockhash;
86
90
  const mintInfo = await fetchMint(rpc, address(mint.toBase58()));
87
91
  return req
88
- .filter((x) => !isValidationError(checkTupleAndAsset(x)))
92
+ .filter((x) => !isValidationError(matchTupleAndAsset(x)))
89
93
  .map((x) => {
90
94
  return {
91
95
  ...x,
@@ -99,7 +103,7 @@ export const createFacilitatorHandler = (network, rpc, feePayerKeypair, mint, ma
99
103
  });
100
104
  };
101
105
  const handleSettle = async (requirements, payment) => {
102
- if (isValidationError(checkTuple(payment))) {
106
+ if (isValidationError(matchTuple(payment))) {
103
107
  return null;
104
108
  }
105
109
  const paymentPayload = PaymentPayload(payment.payload);
@@ -150,6 +154,7 @@ export const createFacilitatorHandler = (network, rpc, feePayerKeypair, mint, ma
150
154
  };
151
155
  };
152
156
  return {
157
+ getSupported,
153
158
  getRequirements,
154
159
  handleSettle,
155
160
  };
@@ -1,3 +1,3 @@
1
1
  export { createPaymentHandler } from "./client.js";
2
- export { createFacilitatorHandler, lookupX402Network } from "./facilitator.js";
2
+ export { createFacilitatorHandler } from "./facilitator.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/exact/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/exact/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1,2 +1,2 @@
1
1
  export { createPaymentHandler } from "./client.js";
2
- export { createFacilitatorHandler, lookupX402Network } from "./facilitator.js";
2
+ export { createFacilitatorHandler } from "./facilitator.js";