@faremeter/payment-evm 0.7.0 → 0.9.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.
@@ -1,7 +1,11 @@
1
1
  import type { PaymentHandler } from "@faremeter/types/client";
2
+ import { type AssetNameOrContractInfo } from "@faremeter/info/evm";
2
3
  import type { Hex } from "viem";
3
4
  interface WalletForPayment {
4
- network: string;
5
+ chain: {
6
+ id: number;
7
+ name: string;
8
+ };
5
9
  address: Hex;
6
10
  account: {
7
11
  signTypedData: (params: {
@@ -12,6 +16,9 @@ interface WalletForPayment {
12
16
  }) => Promise<Hex>;
13
17
  };
14
18
  }
15
- export declare function createPaymentHandler(wallet: WalletForPayment): PaymentHandler;
19
+ export type CreatePaymentHandlerOpts = {
20
+ asset?: AssetNameOrContractInfo;
21
+ };
22
+ export declare function createPaymentHandler(wallet: WalletForPayment, opts?: CreatePaymentHandlerOpts): PaymentHandler;
16
23
  export {};
17
24
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/exact/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,yBAAyB,CAAC;AAOjC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAWhC,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,GAAG,CAAC;IACb,OAAO,EAAE;QACP,aAAa,EAAE,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC/B,WAAW,EAAE,MAAM,CAAC;YACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAClC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;KACpB,CAAC;CACH;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG,cAAc,CA+G7E"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/exact/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAWhC,UAAU,gBAAgB;IACxB,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,OAAO,EAAE,GAAG,CAAC;IACb,OAAO,EAAE;QACP,aAAa,EAAE,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC/B,WAAW,EAAE,MAAM,CAAC;YACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAClC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;KACpB,CAAC;CACH;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,CAAC,EAAE,uBAAuB,CAAC;CACjC,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,IAAI,GAAE,wBAA6B,GAClC,cAAc,CAkGhB"}
@@ -1,22 +1,16 @@
1
1
  import { randomBytes } from "crypto";
2
- import { isKnownNetwork, lookupKnownNetwork, lookupKnownAsset, } from "@faremeter/info/evm";
2
+ import { lookupX402Network, findAssetInfo, } from "@faremeter/info/evm";
3
3
  import { isAddress } from "viem";
4
4
  import { type } from "arktype";
5
5
  import { X402_EXACT_SCHEME, EIP712_TYPES, eip712Domain, } from "./constants.js";
6
- export function createPaymentHandler(wallet) {
7
- if (!isKnownNetwork(wallet.network)) {
8
- throw new Error(`Wallet was created for unsupported network '${wallet.network}'`);
9
- }
10
- const networkInfo = lookupKnownNetwork(wallet.network);
11
- if (!networkInfo) {
12
- throw new Error(`Couldn't look up network info for network '${wallet.network}'`);
13
- }
14
- const assetInfo = lookupKnownAsset(wallet.network, "USDC");
6
+ export function createPaymentHandler(wallet, opts = {}) {
7
+ const x402Network = lookupX402Network(wallet.chain.id);
8
+ const assetInfo = findAssetInfo(x402Network, opts.asset ?? "USDC");
15
9
  if (!assetInfo) {
16
- throw new Error(`Couldn't look up USDC information on network '${wallet.network}'`);
10
+ throw new Error(`Couldn't look up USDC information on network '${x402Network}'`);
17
11
  }
18
12
  return async function handlePayment(context, accepts) {
19
- const compatibleRequirements = accepts.filter((req) => req.scheme === X402_EXACT_SCHEME && req.network === wallet.network);
13
+ const compatibleRequirements = accepts.filter((req) => req.scheme === X402_EXACT_SCHEME && req.network === x402Network);
20
14
  return compatibleRequirements.map((requirements) => ({
21
15
  requirements,
22
16
  exec: async () => {
@@ -43,19 +37,17 @@ export function createPaymentHandler(wallet) {
43
37
  if (extraResult instanceof type.errors) {
44
38
  throw new Error(`Invalid EIP-712 domain parameters: ${extraResult.summary}`);
45
39
  }
40
+ const verifyingContract = extraResult.verifyingContract ??
41
+ requirements.asset ??
42
+ assetInfo.address;
43
+ if (!isAddress(verifyingContract)) {
44
+ throw new Error(`Invalid verifying contract: ${verifyingContract}`);
45
+ }
46
46
  const domain = {
47
47
  name: extraResult.name ?? assetInfo.contractName,
48
48
  version: extraResult.version ?? "2",
49
- chainId: extraResult.chainId ?? networkInfo.chainId,
50
- verifyingContract: (() => {
51
- const asset = extraResult.verifyingContract ??
52
- requirements.asset ??
53
- assetInfo.address;
54
- if (!isAddress(asset)) {
55
- throw new Error(`Invalid asset address: ${asset}`);
56
- }
57
- return asset;
58
- })(),
49
+ chainId: extraResult.chainId ?? wallet.chain.id,
50
+ verifyingContract,
59
51
  };
60
52
  const types = EIP712_TYPES;
61
53
  // Message for EIP-712 signing (using BigInt for signing)
@@ -5,4 +5,14 @@ export declare function generateDomain(publicClient: PublicClient, chainId: numb
5
5
  chainId: number;
6
6
  verifyingContract: `0x${string}`;
7
7
  }>;
8
+ export declare function generateForwarderDomain(chainId: number, domainInfo: {
9
+ version: string;
10
+ name: string;
11
+ verifyingContract: `0x${string}`;
12
+ }): {
13
+ chainId: number;
14
+ version: string;
15
+ name: string;
16
+ verifyingContract: `0x${string}`;
17
+ };
8
18
  //# sourceMappingURL=common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/exact/common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAI9C,wBAAsB,cAAc,CAClC,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,GAAG;;;;;GA8BX"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/exact/common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAI9C,wBAAsB,cAAc,CAClC,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,GAAG;;;;;GA8BX;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,KAAK,MAAM,EAAE,CAAC;CAClC;;aAHU,MAAM;UACT,MAAM;uBACO,KAAK,MAAM,EAAE;EAOnC"}
@@ -28,3 +28,9 @@ export async function generateDomain(publicClient, chainId, asset) {
28
28
  };
29
29
  return domain;
30
30
  }
31
+ export function generateForwarderDomain(chainId, domainInfo) {
32
+ return {
33
+ ...domainInfo,
34
+ chainId,
35
+ };
36
+ }
@@ -1,4 +1,10 @@
1
1
  import { type FacilitatorHandler } from "@faremeter/types/facilitator";
2
- import type { PublicClient, WalletClient } from "viem";
3
- export declare function createFacilitatorHandler(network: string, publicClient: PublicClient, walletClient: WalletClient, assetName: string): Promise<FacilitatorHandler>;
2
+ import type { Chain, Transport } from "viem";
3
+ import { type KnownX402Network, type AssetNameOrContractInfo } from "@faremeter/info/evm";
4
+ type CreateFacilitatorHandlerOpts = {
5
+ network?: KnownX402Network;
6
+ transport?: Transport;
7
+ };
8
+ export declare function createFacilitatorHandler(chain: Chain, privateKey: string, assetNameOrInfo: AssetNameOrContractInfo, opts?: CreateFacilitatorHandlerOpts): Promise<FacilitatorHandler>;
9
+ export {};
4
10
  //# sourceMappingURL=facilitator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"facilitator.d.ts","sourceRoot":"","sources":["../../../src/exact/facilitator.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,KAAK,EAAE,YAAY,EAAO,YAAY,EAAW,MAAM,MAAM,CAAC;AAqCrE,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CAiP7B"}
1
+ {"version":3,"file":"facilitator.d.ts","sourceRoot":"","sources":["../../../src/exact/facilitator.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,KAAK,EAAgB,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAY3D,OAAO,EAEL,KAAK,gBAAgB,EAErB,KAAK,uBAAuB,EAC7B,MAAM,qBAAqB,CAAC;AA8B7B,KAAK,4BAA4B,GAAG;IAClC,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AACF,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,uBAAuB,EACxC,IAAI,GAAE,4BAAiC,GACtC,OAAO,CAAC,kBAAkB,CAAC,CA+S7B"}
@@ -1,10 +1,12 @@
1
1
  import { isValidationError, caseInsensitiveLiteral } from "@faremeter/types";
2
+ import { isPrivateKey } from "@faremeter/types/evm";
2
3
  import {} from "@faremeter/types/facilitator";
3
4
  import { type } from "arktype";
4
- import { verifyTypedData, encodeFunctionData, isAddress } from "viem";
5
- import { isKnownAsset, isKnownNetwork, lookupKnownAsset, lookupKnownNetwork, } from "@faremeter/info/evm";
5
+ import { createPublicClient, createWalletClient, http, verifyTypedData, encodeFunctionData, isAddress, } from "viem";
6
+ import { privateKeyToAccount } from "viem/accounts";
7
+ import { lookupX402Network, findAssetInfo, } from "@faremeter/info/evm";
6
8
  import { X402_EXACT_SCHEME, TRANSFER_WITH_AUTHORIZATION_ABI, EIP712_TYPES, x402ExactPayload, } from "./constants.js";
7
- import { generateDomain } from "./common.js";
9
+ import { generateDomain, generateForwarderDomain } from "./common.js";
8
10
  function errorResponse(msg) {
9
11
  return {
10
12
  success: false,
@@ -21,28 +23,48 @@ function parseSignature(signature) {
21
23
  const v = parseInt(sig.slice(128, 130), 16);
22
24
  return { v, r, s };
23
25
  }
24
- export async function createFacilitatorHandler(network, publicClient, walletClient, assetName) {
25
- if (!isKnownNetwork(network)) {
26
- throw new Error(`Unknown network ${network}`);
27
- }
28
- const networkInfo = lookupKnownNetwork(network);
29
- if (!networkInfo) {
30
- throw new Error(`Couldn't look up information for ${network}`);
31
- }
32
- const { chainId } = networkInfo;
33
- if (!isKnownAsset(assetName)) {
34
- throw new Error(`Unknown asset: ${assetName}`);
35
- }
36
- const assetInfo = lookupKnownAsset(network, assetName);
37
- if (!assetInfo) {
38
- throw new Error(`Couldn't look up asset ${assetName} on ${network}`);
26
+ export async function createFacilitatorHandler(chain, privateKey, assetNameOrInfo, opts = {}) {
27
+ if (!isPrivateKey(privateKey)) {
28
+ throw new Error(`Invalid private key: ${privateKey}`);
39
29
  }
30
+ const network = opts.network ?? lookupX402Network(chain.id);
31
+ const chainId = chain.id;
32
+ const assetInfo = findAssetInfo(network, assetNameOrInfo);
40
33
  const asset = assetInfo.address;
34
+ const useForwarder = assetInfo.forwarder !== undefined && assetInfo.forwarderName !== undefined;
41
35
  if (!isAddress(asset)) {
42
36
  throw new Error(`Invalid asset address: ${asset}`);
43
37
  }
44
- {
45
- const domain = await generateDomain(publicClient, chainId, asset);
38
+ const transport = opts.transport ?? http(chain.rpcUrls.default.http[0]);
39
+ const account = privateKeyToAccount(privateKey);
40
+ const publicClient = createPublicClient({
41
+ chain,
42
+ transport,
43
+ });
44
+ const walletClient = createWalletClient({
45
+ account,
46
+ chain,
47
+ transport,
48
+ });
49
+ let domain;
50
+ if (useForwarder) {
51
+ if (!assetInfo.forwarder) {
52
+ throw new Error("Missing Forwarding Contract");
53
+ }
54
+ if (!assetInfo.forwarderVersion) {
55
+ throw new Error("Missing Forwarding Version");
56
+ }
57
+ if (!assetInfo.forwarderName) {
58
+ throw new Error("Missing Forwarding Name");
59
+ }
60
+ domain = generateForwarderDomain(chainId, {
61
+ version: assetInfo.forwarderVersion,
62
+ name: assetInfo.forwarderName,
63
+ verifyingContract: assetInfo.forwarder,
64
+ });
65
+ }
66
+ else {
67
+ domain = await generateDomain(publicClient, chainId, asset);
46
68
  if (domain.name != assetInfo.contractName) {
47
69
  throw new Error(`On chain contract name (${domain.name}) doesn't match configured asset name (${assetInfo.contractName})`);
48
70
  }
@@ -54,6 +76,15 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
54
76
  const checkTupleAndAsset = checkTuple.and({
55
77
  asset: caseInsensitiveLiteral(asset),
56
78
  });
79
+ const getSupported = () => {
80
+ return [
81
+ Promise.resolve({
82
+ x402Version: 1,
83
+ network,
84
+ scheme: X402_EXACT_SCHEME,
85
+ }),
86
+ ];
87
+ };
57
88
  const getRequirements = async (req) => {
58
89
  return req
59
90
  .filter((x) => !isValidationError(checkTupleAndAsset(x)))
@@ -63,10 +94,10 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
63
94
  maxTimeoutSeconds: 300,
64
95
  // Provide EIP-712 domain parameters for client signing
65
96
  extra: {
66
- name: assetInfo.contractName,
67
- version: "2",
97
+ name: useForwarder ? assetInfo.forwarderName : assetInfo.contractName,
98
+ version: useForwarder ? assetInfo.forwarderVersion : "2",
68
99
  chainId,
69
- verifyingContract: asset,
100
+ verifyingContract: useForwarder ? assetInfo.forwarder : asset,
70
101
  },
71
102
  }));
72
103
  };
@@ -112,7 +143,7 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
112
143
  let onChainUsed;
113
144
  try {
114
145
  onChainUsed = await publicClient.readContract({
115
- address: asset,
146
+ address: assetInfo.forwarder ?? asset,
116
147
  abi: TRANSFER_WITH_AUTHORIZATION_ABI,
117
148
  functionName: "authorizationState",
118
149
  args: [authorization.from, authorization.nonce],
@@ -124,7 +155,22 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
124
155
  if (onChainUsed) {
125
156
  return errorResponse("Authorization already used on-chain");
126
157
  }
127
- const domain = await generateDomain(publicClient, chainId, asset);
158
+ let domain;
159
+ if (useForwarder) {
160
+ if (!assetInfo.forwarderVersion ||
161
+ !assetInfo.forwarderName ||
162
+ !assetInfo.forwarder) {
163
+ throw new Error("Secondary Forwardign Information Missing");
164
+ }
165
+ domain = generateForwarderDomain(chainId, {
166
+ version: assetInfo.forwarderVersion,
167
+ name: assetInfo.forwarderName,
168
+ verifyingContract: assetInfo.forwarder,
169
+ });
170
+ }
171
+ else {
172
+ domain = await generateDomain(publicClient, chainId, asset);
173
+ }
128
174
  const types = EIP712_TYPES;
129
175
  const message = {
130
176
  from: authorization.from,
@@ -155,7 +201,7 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
155
201
  // Verify contract supports EIP-712
156
202
  try {
157
203
  await publicClient.readContract({
158
- address: asset,
204
+ address: useForwarder ? domain.verifyingContract : asset,
159
205
  abi: TRANSFER_WITH_AUTHORIZATION_ABI,
160
206
  functionName: "DOMAIN_SEPARATOR",
161
207
  });
@@ -186,7 +232,7 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
186
232
  // Build and send the transaction
187
233
  try {
188
234
  const request = await walletClient.prepareTransactionRequest({
189
- to: asset,
235
+ to: useForwarder ? domain.verifyingContract : asset,
190
236
  data,
191
237
  account: acct,
192
238
  chain: undefined,
@@ -214,6 +260,7 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
214
260
  }
215
261
  };
216
262
  return {
263
+ getSupported,
217
264
  getRequirements,
218
265
  handleSettle,
219
266
  };