@faremeter/payment-solana 0.10.2 → 0.11.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,14 @@ 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
+ interface GetAssociatedTokenAddressSyncOptions {
11
+ allowOwnerOffCurve?: boolean;
12
+ programId?: PublicKey;
13
+ associatedTokenProgramId?: PublicKey;
14
+ }
15
+ interface CreatePaymentHandlerOptions {
16
+ token?: GetAssociatedTokenAddressSyncOptions;
17
+ }
18
+ export declare function createPaymentHandler(wallet: Wallet, mint: PublicKey, connection?: Connection, options?: CreatePaymentHandlerOptions): PaymentHandler;
19
+ export {};
11
20
  //# 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;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
+ {"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,UAAU,oCAAoC;IAC5C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,wBAAwB,CAAC,EAAE,SAAS,CAAC;CACtC;AAgBD,UAAU,2BAA2B;IACnC,KAAK,CAAC,EAAE,oCAAoC,CAAC;CAC9C;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,UAAU,CAAC,EAAE,UAAU,EACvB,OAAO,CAAC,EAAE,2BAA2B,GACpC,cAAc,CAuHhB"}
@@ -4,12 +4,19 @@ import { getBase64EncodedWireTransaction, } from "@solana/transactions";
4
4
  import { ComputeBudgetProgram, Connection, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction, } from "@solana/web3.js";
5
5
  import { PaymentRequirementsExtra } from "./facilitator.js";
6
6
  import { generateMatcher } from "./common.js";
7
- export function createPaymentHandler(wallet, mint, connection) {
8
- const { matchTupleAndAsset } = generateMatcher(wallet.network, mint ? mint.toBase58() : "sol");
7
+ function generateGetAssociatedTokenAddressSyncRest(tokenConfig) {
8
+ const { allowOwnerOffCurve, programId, associatedTokenProgramId } = tokenConfig;
9
+ // NOTE: These map to the trailing default args of
10
+ // getAssociatedTokenAddressSync, so order matters. If things are
11
+ // refactored, they should be updated to match the reality of the
12
+ // implementation.
13
+ return [allowOwnerOffCurve, programId, associatedTokenProgramId];
14
+ }
15
+ export function createPaymentHandler(wallet, mint, connection, options) {
16
+ const getAssociatedTokenAddressSyncRest = generateGetAssociatedTokenAddressSyncRest(options?.token ?? {});
17
+ const { isMatchingRequirement } = generateMatcher(wallet.network, mint ? mint.toBase58() : "sol");
9
18
  return async (context, accepts) => {
10
- const res = accepts
11
- .filter((r) => !isValidationError(matchTupleAndAsset(r)))
12
- .map((requirements) => {
19
+ const res = accepts.filter(isMatchingRequirement).map((requirements) => {
13
20
  const extra = PaymentRequirementsExtra(requirements.extra);
14
21
  if (isValidationError(extra)) {
15
22
  throwValidationError("couldn't validate requirements extra field", extra);
@@ -41,8 +48,8 @@ export function createPaymentHandler(wallet, mint, connection) {
41
48
  amount: Number(requirements.maxAmountRequired),
42
49
  receiver: new PublicKey(requirements.payTo),
43
50
  };
44
- const sourceAccount = getAssociatedTokenAddressSync(mint, wallet.publicKey);
45
- const receiverAccount = getAssociatedTokenAddressSync(mint, paymentRequirements.receiver);
51
+ const sourceAccount = getAssociatedTokenAddressSync(mint, wallet.publicKey, ...getAssociatedTokenAddressSyncRest);
52
+ const receiverAccount = getAssociatedTokenAddressSync(mint, paymentRequirements.receiver, ...getAssociatedTokenAddressSyncRest);
46
53
  const instructions = [
47
54
  ComputeBudgetProgram.setComputeUnitLimit({
48
55
  units: 50_000,
@@ -1,13 +1,14 @@
1
1
  export declare const x402Scheme = "exact";
2
2
  export declare function generateMatcher(network: string, asset: string): {
3
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">;
4
+ scheme: (In: string) => import("arktype/internal/attributes.ts").To<Lowercase<string>>;
9
5
  network: (In: string) => import("arktype/internal/attributes.ts").To<Lowercase<string>>;
10
6
  asset: (In: string) => import("arktype/internal/attributes.ts").To<Lowercase<string>>;
11
7
  }, {}>;
8
+ isMatchingRequirement: (req: {
9
+ scheme: string;
10
+ network: string;
11
+ asset: string;
12
+ }) => boolean;
12
13
  };
13
14
  //# sourceMappingURL=common.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/exact/common.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;EAI7D"}
@@ -1,17 +1,8 @@
1
- import { type } from "arktype";
2
- import { caseInsensitiveLiteral } from "@faremeter/types";
1
+ import { generateRequirementsMatcher } from "@faremeter/types/x402";
3
2
  import { lookupX402Network } from "@faremeter/info/solana";
4
3
  export const x402Scheme = "exact";
5
4
  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
- };
5
+ return generateRequirementsMatcher([x402Scheme], lookupX402Network(network), [
6
+ asset,
7
+ ]);
17
8
  }
@@ -10,15 +10,14 @@ await t.test("testBasicMatching", async (t) => {
10
10
  t.bailout("couldn't find SPL token");
11
11
  return;
12
12
  }
13
- const { matchTuple, matchTupleAndAsset } = generateMatcher("mainnet-beta", tokenInfo.address);
13
+ const { matchTuple } = generateMatcher("mainnet-beta", tokenInfo.address);
14
14
  const req = {
15
15
  network: "solana-mainnet-beta",
16
16
  scheme: "exact",
17
17
  asset: tokenInfo.address,
18
18
  };
19
19
  t.ok(!iVE(matchTuple(req)));
20
- t.ok(!iVE(matchTupleAndAsset(req)));
21
- t.ok(!iVE(matchTupleAndAsset({
20
+ t.ok(!iVE(matchTuple({
22
21
  ...req,
23
22
  network: "solana",
24
23
  })));
@@ -1 +1 @@
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;AAEH,UAAU,kBAAkB;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAmBD,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,SAAS,kBAAkB,KAC1B,kBA+HF,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;AAEH,UAAU,kBAAkB;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAmBD,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,SAAS,kBAAkB,KAC1B,kBA0HF,CAAC"}
@@ -73,7 +73,7 @@ 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, config) => {
76
- const { matchTuple, matchTupleAndAsset } = generateMatcher(network, mint.toBase58());
76
+ const { isMatchingRequirement } = generateMatcher(network, mint.toBase58());
77
77
  const { maxRetries = 30, retryDelayMs = 1000, maxPriorityFee = 100_000, } = config ?? {};
78
78
  const getSupported = () => {
79
79
  return lookupX402Network(network).map((network) => Promise.resolve({
@@ -89,9 +89,7 @@ export const createFacilitatorHandler = (network, rpc, feePayerKeypair, mint, co
89
89
  const recentBlockhash = (await rpc.getLatestBlockhash().send()).value
90
90
  .blockhash;
91
91
  const mintInfo = await fetchMint(rpc, address(mint.toBase58()));
92
- return req
93
- .filter((x) => !isValidationError(matchTupleAndAsset(x)))
94
- .map((x) => {
92
+ return req.filter(isMatchingRequirement).map((x) => {
95
93
  return {
96
94
  ...x,
97
95
  asset: mint.toBase58(),
@@ -104,7 +102,7 @@ export const createFacilitatorHandler = (network, rpc, feePayerKeypair, mint, co
104
102
  });
105
103
  };
106
104
  const handleSettle = async (requirements, payment) => {
107
- if (isValidationError(matchTuple(payment))) {
105
+ if (!isMatchingRequirement(requirements)) {
108
106
  return null;
109
107
  }
110
108
  const paymentPayload = PaymentPayload(payment.payload);
@@ -1 +1 @@
1
- {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/exact/verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAYrE,OAAO,EAEL,KAAK,4BAA4B,EAElC,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AA2GjD,wBAAsB,kBAAkB,CACtC,kBAAkB,EAAE,4BAA4B,EAChD,mBAAmB,EAAE,uBAAuB,EAC5C,kBAAkB,EAAE,SAAS,EAC7B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,CAAC,CAiGlB"}
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/exact/verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAYrE,OAAO,EAEL,KAAK,4BAA4B,EAElC,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAqHjD,wBAAsB,kBAAkB,CACtC,kBAAkB,EAAE,4BAA4B,EAChD,mBAAmB,EAAE,uBAAuB,EAC5C,kBAAkB,EAAE,SAAS,EAC7B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,CAAC,CAkGlB"}
@@ -69,16 +69,20 @@ async function verifyTransferInstruction(instruction, paymentRequirements, desti
69
69
  transfer.accounts.mint.address === paymentRequirements.asset &&
70
70
  transfer.accounts.destination.address === destination);
71
71
  }
72
- function verifyCreateATAInstruction(instruction) {
72
+ function verifyCreateATAInstruction(instruction, facilitatorAddress) {
73
73
  if (!instruction.data || !instruction.accounts) {
74
74
  return false;
75
75
  }
76
76
  try {
77
- parseCreateAssociatedTokenInstruction({
77
+ const createInstruction = parseCreateAssociatedTokenInstruction({
78
78
  accounts: instruction.accounts,
79
79
  programAddress: instruction.programAddress,
80
80
  data: new Uint8Array(instruction.data),
81
81
  });
82
+ if (createInstruction.accounts.payer.address === facilitatorAddress) {
83
+ logger.error("Dropping transaction where the facilitator pays for a token account creation");
84
+ return false;
85
+ }
82
86
  return true;
83
87
  }
84
88
  catch {
@@ -99,6 +103,7 @@ export async function isValidTransaction(transactionMessage, paymentRequirements
99
103
  tokenProgram: TOKEN_PROGRAM_ADDRESS,
100
104
  });
101
105
  const instructions = transactionMessage.instructions;
106
+ const facilitatorBase58 = facilitatorAddress.toBase58();
102
107
  if (instructions.length === 3) {
103
108
  // Make typescript happy...
104
109
  const [ix0, ix1, ix2] = instructions;
@@ -119,7 +124,7 @@ export async function isValidTransaction(transactionMessage, paymentRequirements
119
124
  return false;
120
125
  }
121
126
  }
122
- return await verifyTransferInstruction(ix2, paymentRequirements, destination, facilitatorAddress.toBase58());
127
+ return await verifyTransferInstruction(ix2, paymentRequirements, destination, facilitatorBase58);
123
128
  }
124
129
  else if (instructions.length === 4) {
125
130
  const [ix0, ix1, ix2, ix3] = instructions;
@@ -140,8 +145,8 @@ export async function isValidTransaction(transactionMessage, paymentRequirements
140
145
  return false;
141
146
  }
142
147
  }
143
- return (verifyCreateATAInstruction(ix2) &&
144
- (await verifyTransferInstruction(ix3, paymentRequirements, destination, facilitatorAddress.toBase58())));
148
+ return (verifyCreateATAInstruction(ix2, facilitatorBase58) &&
149
+ (await verifyTransferInstruction(ix3, paymentRequirements, destination, facilitatorBase58)));
145
150
  }
146
151
  return false;
147
152
  }