@faremeter/payment-evm 0.7.0 → 0.8.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,10 @@
1
1
  import type { PaymentHandler } from "@faremeter/types/client";
2
2
  import type { Hex } from "viem";
3
3
  interface WalletForPayment {
4
- network: string;
4
+ chain: {
5
+ id: number;
6
+ name: string;
7
+ };
5
8
  address: Hex;
6
9
  account: {
7
10
  signTypedData: (params: {
@@ -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;AAGjC,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,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG,cAAc,CAkG7E"}
@@ -1,22 +1,16 @@
1
1
  import { randomBytes } from "crypto";
2
- import { isKnownNetwork, lookupKnownNetwork, lookupKnownAsset, } from "@faremeter/info/evm";
2
+ import { lookupX402Network, lookupKnownAsset } 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
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");
7
+ const x402Network = lookupX402Network(wallet.chain.id);
8
+ const assetInfo = lookupKnownAsset(wallet.chain.id, "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 KnownNetwork, type ContractInfo } from "@faremeter/info/evm";
4
+ type CreateFacilitatorHandlerOpts = {
5
+ network?: KnownNetwork;
6
+ transport?: Transport;
7
+ };
8
+ export declare function createFacilitatorHandler(chain: Chain, privateKey: string, assetNameOrInfo: string | ContractInfo, 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":"AAQA,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,KAAK,EAAgB,KAAK,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAY3D,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AA8B7B,KAAK,4BAA4B,GAAG;IAClC,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AACF,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GAAG,YAAY,EACtC,IAAI,GAAE,4BAAiC,GACtC,OAAO,CAAC,kBAAkB,CAAC,CAsT7B"}
@@ -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 { isKnownAsset, lookupKnownAsset, lookupX402Network, } 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,61 @@ 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}`);
26
+ export async function createFacilitatorHandler(chain, privateKey, assetNameOrInfo, opts = {}) {
27
+ if (!isPrivateKey(privateKey)) {
28
+ throw new Error(`Invalid private key: ${privateKey}`);
27
29
  }
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}`);
30
+ const network = opts.network ?? lookupX402Network(chain.id);
31
+ const chainId = chain.id;
32
+ let assetInfo;
33
+ if (typeof assetNameOrInfo == "string") {
34
+ if (!isKnownAsset(assetNameOrInfo)) {
35
+ throw new Error(`Unknown asset: ${assetNameOrInfo}`);
36
+ }
37
+ const t = lookupKnownAsset(network, assetNameOrInfo);
38
+ if (!t) {
39
+ throw new Error(`Couldn't look up asset ${assetNameOrInfo} on ${network}`);
40
+ }
41
+ assetInfo = t;
35
42
  }
36
- const assetInfo = lookupKnownAsset(network, assetName);
37
- if (!assetInfo) {
38
- throw new Error(`Couldn't look up asset ${assetName} on ${network}`);
43
+ else {
44
+ assetInfo = assetNameOrInfo;
39
45
  }
40
46
  const asset = assetInfo.address;
47
+ const useForwarder = assetInfo.forwarder !== undefined && assetInfo.forwarderName !== undefined;
41
48
  if (!isAddress(asset)) {
42
49
  throw new Error(`Invalid asset address: ${asset}`);
43
50
  }
44
- {
45
- const domain = await generateDomain(publicClient, chainId, asset);
51
+ const transport = opts.transport ?? http(chain.rpcUrls.default.http[0]);
52
+ const account = privateKeyToAccount(privateKey);
53
+ const publicClient = createPublicClient({
54
+ chain,
55
+ transport,
56
+ });
57
+ const walletClient = createWalletClient({
58
+ account,
59
+ chain,
60
+ transport,
61
+ });
62
+ let domain;
63
+ if (useForwarder) {
64
+ if (!assetInfo.forwarder) {
65
+ throw new Error("Missing Forwarding Contract");
66
+ }
67
+ if (!assetInfo.forwarderVersion) {
68
+ throw new Error("Missing Forwarding Version");
69
+ }
70
+ if (!assetInfo.forwarderName) {
71
+ throw new Error("Missing Forwarding Name");
72
+ }
73
+ domain = generateForwarderDomain(chainId, {
74
+ version: assetInfo.forwarderVersion,
75
+ name: assetInfo.forwarderName,
76
+ verifyingContract: assetInfo.forwarder,
77
+ });
78
+ }
79
+ else {
80
+ domain = await generateDomain(publicClient, chainId, asset);
46
81
  if (domain.name != assetInfo.contractName) {
47
82
  throw new Error(`On chain contract name (${domain.name}) doesn't match configured asset name (${assetInfo.contractName})`);
48
83
  }
@@ -63,10 +98,10 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
63
98
  maxTimeoutSeconds: 300,
64
99
  // Provide EIP-712 domain parameters for client signing
65
100
  extra: {
66
- name: assetInfo.contractName,
67
- version: "2",
101
+ name: useForwarder ? assetInfo.forwarderName : assetInfo.contractName,
102
+ version: useForwarder ? assetInfo.forwarderVersion : "2",
68
103
  chainId,
69
- verifyingContract: asset,
104
+ verifyingContract: useForwarder ? assetInfo.forwarder : asset,
70
105
  },
71
106
  }));
72
107
  };
@@ -112,7 +147,7 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
112
147
  let onChainUsed;
113
148
  try {
114
149
  onChainUsed = await publicClient.readContract({
115
- address: asset,
150
+ address: assetInfo.forwarder ?? asset,
116
151
  abi: TRANSFER_WITH_AUTHORIZATION_ABI,
117
152
  functionName: "authorizationState",
118
153
  args: [authorization.from, authorization.nonce],
@@ -124,7 +159,22 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
124
159
  if (onChainUsed) {
125
160
  return errorResponse("Authorization already used on-chain");
126
161
  }
127
- const domain = await generateDomain(publicClient, chainId, asset);
162
+ let domain;
163
+ if (useForwarder) {
164
+ if (!assetInfo.forwarderVersion ||
165
+ !assetInfo.forwarderName ||
166
+ !assetInfo.forwarder) {
167
+ throw new Error("Secondary Forwardign Information Missing");
168
+ }
169
+ domain = generateForwarderDomain(chainId, {
170
+ version: assetInfo.forwarderVersion,
171
+ name: assetInfo.forwarderName,
172
+ verifyingContract: assetInfo.forwarder,
173
+ });
174
+ }
175
+ else {
176
+ domain = await generateDomain(publicClient, chainId, asset);
177
+ }
128
178
  const types = EIP712_TYPES;
129
179
  const message = {
130
180
  from: authorization.from,
@@ -155,7 +205,7 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
155
205
  // Verify contract supports EIP-712
156
206
  try {
157
207
  await publicClient.readContract({
158
- address: asset,
208
+ address: useForwarder ? domain.verifyingContract : asset,
159
209
  abi: TRANSFER_WITH_AUTHORIZATION_ABI,
160
210
  functionName: "DOMAIN_SEPARATOR",
161
211
  });
@@ -186,7 +236,7 @@ export async function createFacilitatorHandler(network, publicClient, walletClie
186
236
  // Build and send the transaction
187
237
  try {
188
238
  const request = await walletClient.prepareTransactionRequest({
189
- to: asset,
239
+ to: useForwarder ? domain.verifyingContract : asset,
190
240
  data,
191
241
  account: acct,
192
242
  chain: undefined,