@x402/aptos 2.3.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 (35) hide show
  1. package/README.md +74 -0
  2. package/dist/cjs/exact/client/index.d.ts +29 -0
  3. package/dist/cjs/exact/client/index.js +141 -0
  4. package/dist/cjs/exact/client/index.js.map +1 -0
  5. package/dist/cjs/exact/facilitator/index.d.ts +52 -0
  6. package/dist/cjs/exact/facilitator/index.js +373 -0
  7. package/dist/cjs/exact/facilitator/index.js.map +1 -0
  8. package/dist/cjs/exact/server/index.d.ts +67 -0
  9. package/dist/cjs/exact/server/index.js +143 -0
  10. package/dist/cjs/exact/server/index.js.map +1 -0
  11. package/dist/cjs/index.d.ts +116 -0
  12. package/dist/cjs/index.js +269 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/signer-DPT9P1NX.d.ts +59 -0
  15. package/dist/cjs/signer-DfwN1I5I.d.ts +59 -0
  16. package/dist/esm/chunk-6BMAMLXJ.mjs +93 -0
  17. package/dist/esm/chunk-6BMAMLXJ.mjs.map +1 -0
  18. package/dist/esm/chunk-D4UVBSUH.mjs +53 -0
  19. package/dist/esm/chunk-D4UVBSUH.mjs.map +1 -0
  20. package/dist/esm/chunk-FG4ANPDN.mjs +46 -0
  21. package/dist/esm/chunk-FG4ANPDN.mjs.map +1 -0
  22. package/dist/esm/exact/client/index.d.mts +29 -0
  23. package/dist/esm/exact/client/index.mjs +9 -0
  24. package/dist/esm/exact/client/index.mjs.map +1 -0
  25. package/dist/esm/exact/facilitator/index.d.mts +52 -0
  26. package/dist/esm/exact/facilitator/index.mjs +294 -0
  27. package/dist/esm/exact/facilitator/index.mjs.map +1 -0
  28. package/dist/esm/exact/server/index.d.mts +67 -0
  29. package/dist/esm/exact/server/index.mjs +116 -0
  30. package/dist/esm/exact/server/index.mjs.map +1 -0
  31. package/dist/esm/index.d.mts +116 -0
  32. package/dist/esm/index.mjs +105 -0
  33. package/dist/esm/index.mjs.map +1 -0
  34. package/dist/esm/signer-DPT9P1NX.d.mts +59 -0
  35. package/package.json +95 -0
@@ -0,0 +1,373 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/exact/facilitator/index.ts
21
+ var facilitator_exports = {};
22
+ __export(facilitator_exports, {
23
+ ExactAptosScheme: () => ExactAptosScheme
24
+ });
25
+ module.exports = __toCommonJS(facilitator_exports);
26
+
27
+ // src/exact/facilitator/scheme.ts
28
+ var import_ts_sdk3 = require("@aptos-labs/ts-sdk");
29
+
30
+ // src/utils.ts
31
+ var import_ts_sdk2 = require("@aptos-labs/ts-sdk");
32
+
33
+ // src/constants.ts
34
+ var import_ts_sdk = require("@aptos-labs/ts-sdk");
35
+ var APTOS_MAINNET_CAIP2 = "aptos:1";
36
+ var APTOS_TESTNET_CAIP2 = "aptos:2";
37
+ var MAX_GAS_AMOUNT = 500000n;
38
+ function getAptosChainId(network) {
39
+ switch (network) {
40
+ case APTOS_MAINNET_CAIP2:
41
+ return 1;
42
+ case APTOS_TESTNET_CAIP2:
43
+ return 2;
44
+ default:
45
+ throw new Error(`Unsupported Aptos network: ${network}`);
46
+ }
47
+ }
48
+ function getAptosNetwork(network) {
49
+ switch (network) {
50
+ case APTOS_MAINNET_CAIP2:
51
+ return import_ts_sdk.Network.MAINNET;
52
+ case APTOS_TESTNET_CAIP2:
53
+ return import_ts_sdk.Network.TESTNET;
54
+ default:
55
+ throw new Error(`Unsupported Aptos network: ${network}`);
56
+ }
57
+ }
58
+ function getAptosRpcUrl(network) {
59
+ return import_ts_sdk.NetworkToNodeAPI[network];
60
+ }
61
+
62
+ // src/utils.ts
63
+ function deserializeAptosPayment(transactionBase64) {
64
+ const decoded = Buffer.from(transactionBase64, "base64").toString("utf8");
65
+ const parsed = JSON.parse(decoded);
66
+ const transactionBytes = Uint8Array.from(parsed.transaction);
67
+ const transaction = import_ts_sdk2.SimpleTransaction.deserialize(new import_ts_sdk2.Deserializer(transactionBytes));
68
+ const authBytes = Uint8Array.from(parsed.senderAuthenticator);
69
+ const senderAuthenticator = import_ts_sdk2.AccountAuthenticator.deserialize(new import_ts_sdk2.Deserializer(authBytes));
70
+ if (!isEntryFunctionPayload(transaction.rawTransaction.payload)) {
71
+ return { transaction, senderAuthenticator };
72
+ }
73
+ const entryFunction = transaction.rawTransaction.payload.entryFunction;
74
+ return { transaction, senderAuthenticator, entryFunction };
75
+ }
76
+ function isEntryFunctionPayload(payload) {
77
+ return "entryFunction" in payload;
78
+ }
79
+ function createAptosClient(network, rpcUrl) {
80
+ const aptosNetwork = getAptosNetwork(network);
81
+ const fullnodeUrl = rpcUrl || getAptosRpcUrl(aptosNetwork);
82
+ const config = new import_ts_sdk2.AptosConfig({
83
+ network: aptosNetwork,
84
+ fullnode: fullnodeUrl
85
+ });
86
+ return new import_ts_sdk2.Aptos(config);
87
+ }
88
+
89
+ // src/exact/facilitator/scheme.ts
90
+ var ExactAptosScheme = class {
91
+ /**
92
+ * Creates a new ExactAptosFacilitator instance.
93
+ *
94
+ * @param signer - The Aptos facilitator signer for transaction submission
95
+ * @param sponsorTransactions - Whether to sponsor transactions (pay gas fees). Defaults to true.
96
+ */
97
+ constructor(signer, sponsorTransactions = true) {
98
+ this.signer = signer;
99
+ this.sponsorTransactions = sponsorTransactions;
100
+ this.scheme = "exact";
101
+ this.caipFamily = "aptos:*";
102
+ }
103
+ /**
104
+ * Get mechanism-specific extra data for the supported kinds endpoint.
105
+ *
106
+ * @param _ - The network identifier (unused)
107
+ * @returns Extra data with fee payer address, or undefined if sponsorship is disabled
108
+ */
109
+ getExtra(_) {
110
+ if (!this.sponsorTransactions) {
111
+ return void 0;
112
+ }
113
+ const addresses = this.signer.getAddresses();
114
+ const randomIndex = Math.floor(Math.random() * addresses.length);
115
+ return { feePayer: addresses[randomIndex] };
116
+ }
117
+ /**
118
+ * Get signer addresses used by this facilitator.
119
+ *
120
+ * @param _ - The network identifier (unused)
121
+ * @returns Array of fee payer addresses
122
+ */
123
+ getSigners(_) {
124
+ return [...this.signer.getAddresses()];
125
+ }
126
+ /**
127
+ * Verifies a payment payload.
128
+ *
129
+ * @param payload - The payment payload to verify
130
+ * @param requirements - The payment requirements
131
+ * @returns Promise resolving to verification response
132
+ */
133
+ async verify(payload, requirements) {
134
+ try {
135
+ const aptosPayload = payload.payload;
136
+ const signerAddresses = this.signer.getAddresses();
137
+ const isSponsored = typeof requirements.extra?.feePayer === "string";
138
+ if (payload.x402Version !== 2) {
139
+ return {
140
+ isValid: false,
141
+ invalidReason: "invalid_exact_aptos_payload_unsupported_version",
142
+ payer: ""
143
+ };
144
+ }
145
+ if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
146
+ return { isValid: false, invalidReason: "unsupported_scheme", payer: "" };
147
+ }
148
+ if (payload.accepted.network !== requirements.network) {
149
+ return { isValid: false, invalidReason: "network_mismatch", payer: "" };
150
+ }
151
+ if (isSponsored && !signerAddresses.includes(requirements.extra.feePayer)) {
152
+ return { isValid: false, invalidReason: "fee_payer_not_managed_by_facilitator", payer: "" };
153
+ }
154
+ const { transaction, senderAuthenticator, entryFunction } = deserializeAptosPayment(
155
+ aptosPayload.transaction
156
+ );
157
+ const senderAddress = transaction.rawTransaction.sender.toString();
158
+ const expectedChainId = getAptosChainId(requirements.network);
159
+ const txChainId = Number(transaction.rawTransaction.chain_id.chainId);
160
+ if (txChainId !== expectedChainId) {
161
+ return {
162
+ isValid: false,
163
+ invalidReason: `invalid_exact_aptos_payload_chain_id_mismatch: expected ${expectedChainId}, got ${txChainId}`,
164
+ payer: senderAddress
165
+ };
166
+ }
167
+ if (senderAuthenticator.isEd25519()) {
168
+ const pubKey = senderAuthenticator.public_key;
169
+ const derivedAddress = import_ts_sdk3.AccountAddress.from(pubKey.authKey().derivedAddress());
170
+ if (!derivedAddress.equals(transaction.rawTransaction.sender)) {
171
+ return {
172
+ isValid: false,
173
+ invalidReason: "invalid_exact_aptos_payload_sender_authenticator_mismatch",
174
+ payer: senderAddress
175
+ };
176
+ }
177
+ }
178
+ if (isSponsored) {
179
+ const maxGasAmount = BigInt(transaction.rawTransaction.max_gas_amount);
180
+ if (maxGasAmount > MAX_GAS_AMOUNT) {
181
+ return {
182
+ isValid: false,
183
+ invalidReason: `invalid_exact_aptos_payload_gas_too_high: ${maxGasAmount} > ${MAX_GAS_AMOUNT}`,
184
+ payer: senderAddress
185
+ };
186
+ }
187
+ }
188
+ if (isSponsored) {
189
+ const expectedFeePayer = import_ts_sdk3.AccountAddress.from(requirements.extra.feePayer);
190
+ if (!transaction.feePayerAddress || !expectedFeePayer.equals(transaction.feePayerAddress)) {
191
+ return {
192
+ isValid: false,
193
+ invalidReason: "invalid_exact_aptos_payload_fee_payer_mismatch",
194
+ payer: senderAddress
195
+ };
196
+ }
197
+ }
198
+ if (isSponsored && signerAddresses.includes(senderAddress)) {
199
+ return {
200
+ isValid: false,
201
+ invalidReason: "invalid_exact_aptos_payload_fee_payer_transferring_funds",
202
+ payer: senderAddress
203
+ };
204
+ }
205
+ const EXPIRATION_BUFFER_SECONDS = 5;
206
+ const expirationTimestamp = Number(transaction.rawTransaction.expiration_timestamp_secs);
207
+ if (expirationTimestamp < Math.floor(Date.now() / 1e3) + EXPIRATION_BUFFER_SECONDS) {
208
+ return {
209
+ isValid: false,
210
+ invalidReason: "invalid_exact_aptos_payload_transaction_expired",
211
+ payer: senderAddress
212
+ };
213
+ }
214
+ if (!entryFunction) {
215
+ return {
216
+ isValid: false,
217
+ invalidReason: "invalid_exact_aptos_payload_missing_entry_function",
218
+ payer: senderAddress
219
+ };
220
+ }
221
+ const moduleAddress = entryFunction.module_name.address;
222
+ const moduleName = entryFunction.module_name.name.identifier;
223
+ const functionName = entryFunction.function_name.identifier;
224
+ const isPrimaryFungibleStore = import_ts_sdk3.AccountAddress.ONE.equals(moduleAddress) && moduleName === "primary_fungible_store" && functionName === "transfer";
225
+ const isFungibleAsset = import_ts_sdk3.AccountAddress.ONE.equals(moduleAddress) && moduleName === "fungible_asset" && functionName === "transfer";
226
+ if (!isPrimaryFungibleStore && !isFungibleAsset) {
227
+ return {
228
+ isValid: false,
229
+ invalidReason: "invalid_exact_aptos_payload_wrong_function",
230
+ payer: senderAddress
231
+ };
232
+ }
233
+ if (entryFunction.type_args.length !== 1) {
234
+ return {
235
+ isValid: false,
236
+ invalidReason: "invalid_exact_aptos_payload_wrong_type_args",
237
+ payer: senderAddress
238
+ };
239
+ }
240
+ const args = entryFunction.args;
241
+ if (args.length !== 3) {
242
+ return {
243
+ isValid: false,
244
+ invalidReason: "invalid_exact_aptos_payload_wrong_args",
245
+ payer: senderAddress
246
+ };
247
+ }
248
+ const [faAddressArg, recipientAddressArg, amountArg] = args;
249
+ const faAddress = import_ts_sdk3.AccountAddress.from(faAddressArg.bcsToBytes());
250
+ if (!faAddress.equals(import_ts_sdk3.AccountAddress.from(requirements.asset))) {
251
+ return {
252
+ isValid: false,
253
+ invalidReason: "invalid_exact_aptos_payload_asset_mismatch",
254
+ payer: senderAddress
255
+ };
256
+ }
257
+ const amount = new import_ts_sdk3.Deserializer(amountArg.bcsToBytes()).deserializeU64().toString(10);
258
+ if (amount !== requirements.amount) {
259
+ return {
260
+ isValid: false,
261
+ invalidReason: "invalid_exact_aptos_payload_amount_mismatch",
262
+ payer: senderAddress
263
+ };
264
+ }
265
+ const recipientAddress = import_ts_sdk3.AccountAddress.from(recipientAddressArg.bcsToBytes());
266
+ if (!recipientAddress.equals(import_ts_sdk3.AccountAddress.from(requirements.payTo))) {
267
+ return {
268
+ isValid: false,
269
+ invalidReason: "invalid_exact_aptos_payload_recipient_mismatch",
270
+ payer: senderAddress
271
+ };
272
+ }
273
+ const aptos = createAptosClient(requirements.network);
274
+ const balance = await aptos.getCurrentFungibleAssetBalances({
275
+ options: {
276
+ where: {
277
+ owner_address: { _eq: senderAddress },
278
+ asset_type: { _eq: requirements.asset }
279
+ }
280
+ }
281
+ });
282
+ const currentBalance = BigInt(balance[0]?.amount ?? 0);
283
+ if (currentBalance < BigInt(requirements.amount)) {
284
+ return {
285
+ isValid: false,
286
+ invalidReason: "invalid_exact_aptos_payload_insufficient_balance",
287
+ payer: senderAddress
288
+ };
289
+ }
290
+ let publicKey;
291
+ if (senderAuthenticator.isEd25519()) {
292
+ publicKey = senderAuthenticator.public_key;
293
+ } else if (senderAuthenticator.isSingleKey()) {
294
+ publicKey = senderAuthenticator.public_key;
295
+ } else if (senderAuthenticator.isMultiKey()) {
296
+ publicKey = senderAuthenticator.public_keys;
297
+ }
298
+ const simulationResult = (await aptos.transaction.simulate.simple({ signerPublicKey: publicKey, transaction }))[0];
299
+ if (!simulationResult.success) {
300
+ return {
301
+ isValid: false,
302
+ invalidReason: `invalid_exact_aptos_payload_simulation_failed: ${simulationResult.vm_status}`,
303
+ payer: senderAddress
304
+ };
305
+ }
306
+ return { isValid: true, invalidReason: void 0, payer: senderAddress };
307
+ } catch (error) {
308
+ const errorMessage = error instanceof Error ? error.message : String(error);
309
+ return {
310
+ isValid: false,
311
+ invalidReason: `invalid_exact_aptos_payload_verification_error: ${errorMessage}`,
312
+ payer: ""
313
+ };
314
+ }
315
+ }
316
+ /**
317
+ * Settles a payment by submitting the transaction.
318
+ *
319
+ * @param payload - The payment payload to settle
320
+ * @param requirements - The payment requirements
321
+ * @returns Promise resolving to settlement response
322
+ */
323
+ async settle(payload, requirements) {
324
+ const aptosPayload = payload.payload;
325
+ const valid = await this.verify(payload, requirements);
326
+ if (!valid.isValid) {
327
+ return {
328
+ success: false,
329
+ network: payload.accepted.network,
330
+ transaction: "",
331
+ errorReason: valid.invalidReason ?? "verification_failed",
332
+ payer: valid.payer || ""
333
+ };
334
+ }
335
+ try {
336
+ const { transaction, senderAuthenticator } = deserializeAptosPayment(
337
+ aptosPayload.transaction
338
+ );
339
+ const senderAddress = transaction.rawTransaction.sender.toStringLong();
340
+ const isSponsored = typeof requirements.extra?.feePayer === "string";
341
+ const pendingTxn = isSponsored ? await this.signer.signAndSubmitAsFeePayer(
342
+ transaction,
343
+ senderAuthenticator,
344
+ requirements.network
345
+ ) : await this.signer.submitTransaction(
346
+ transaction,
347
+ senderAuthenticator,
348
+ requirements.network
349
+ );
350
+ await this.signer.waitForTransaction(pendingTxn.hash, requirements.network);
351
+ return {
352
+ success: true,
353
+ transaction: pendingTxn.hash,
354
+ network: payload.accepted.network,
355
+ payer: senderAddress
356
+ };
357
+ } catch (error) {
358
+ const errorMessage = error instanceof Error ? error.message : String(error);
359
+ return {
360
+ success: false,
361
+ errorReason: `transaction_failed: ${errorMessage}`,
362
+ transaction: "",
363
+ network: payload.accepted.network,
364
+ payer: valid.payer || ""
365
+ };
366
+ }
367
+ }
368
+ };
369
+ // Annotate the CommonJS export names for ESM import in node:
370
+ 0 && (module.exports = {
371
+ ExactAptosScheme
372
+ });
373
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/exact/facilitator/index.ts","../../../../src/exact/facilitator/scheme.ts","../../../../src/utils.ts","../../../../src/constants.ts"],"sourcesContent":["export { ExactAptosScheme } from \"./scheme\";\n","import { AccountAddress, Deserializer, Ed25519PublicKey, PublicKey } from \"@aptos-labs/ts-sdk\";\nimport type {\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from \"@x402/core/types\";\nimport type { FacilitatorAptosSigner } from \"../../signer\";\nimport type { ExactAptosPayload } from \"../../types\";\nimport { createAptosClient, deserializeAptosPayment } from \"../../utils\";\nimport { getAptosChainId, MAX_GAS_AMOUNT } from \"../../constants\";\n\n/**\n * Aptos facilitator implementation for the Exact payment scheme.\n */\nexport class ExactAptosScheme implements SchemeNetworkFacilitator {\n readonly scheme = \"exact\";\n readonly caipFamily = \"aptos:*\";\n\n /**\n * Creates a new ExactAptosFacilitator instance.\n *\n * @param signer - The Aptos facilitator signer for transaction submission\n * @param sponsorTransactions - Whether to sponsor transactions (pay gas fees). Defaults to true.\n */\n constructor(\n private readonly signer: FacilitatorAptosSigner,\n private readonly sponsorTransactions: boolean = true,\n ) {}\n\n /**\n * Get mechanism-specific extra data for the supported kinds endpoint.\n *\n * @param _ - The network identifier (unused)\n * @returns Extra data with fee payer address, or undefined if sponsorship is disabled\n */\n getExtra(_: string): Record<string, unknown> | undefined {\n if (!this.sponsorTransactions) {\n return undefined;\n }\n const addresses = this.signer.getAddresses();\n const randomIndex = Math.floor(Math.random() * addresses.length);\n return { feePayer: addresses[randomIndex] };\n }\n\n /**\n * Get signer addresses used by this facilitator.\n *\n * @param _ - The network identifier (unused)\n * @returns Array of fee payer addresses\n */\n getSigners(_: string): string[] {\n return [...this.signer.getAddresses()];\n }\n\n /**\n * Verifies a payment payload.\n *\n * @param payload - The payment payload to verify\n * @param requirements - The payment requirements\n * @returns Promise resolving to verification response\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n try {\n const aptosPayload = payload.payload as ExactAptosPayload;\n const signerAddresses = this.signer.getAddresses();\n const isSponsored = typeof requirements.extra?.feePayer === \"string\";\n\n // Step 2: Verify x402Version is 2\n if (payload.x402Version !== 2) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_unsupported_version\",\n payer: \"\",\n };\n }\n\n // Step 3: Verify the network matches\n if (payload.accepted.scheme !== \"exact\" || requirements.scheme !== \"exact\") {\n return { isValid: false, invalidReason: \"unsupported_scheme\", payer: \"\" };\n }\n\n if (payload.accepted.network !== requirements.network) {\n return { isValid: false, invalidReason: \"network_mismatch\", payer: \"\" };\n }\n\n // If sponsored, verify the fee payer is managed by this facilitator\n if (isSponsored && !signerAddresses.includes(requirements.extra.feePayer as string)) {\n return { isValid: false, invalidReason: \"fee_payer_not_managed_by_facilitator\", payer: \"\" };\n }\n\n // Step 4: Deserialize the BCS-encoded transaction and verify the signature\n const { transaction, senderAuthenticator, entryFunction } = deserializeAptosPayment(\n aptosPayload.transaction,\n );\n const senderAddress = transaction.rawTransaction.sender.toString();\n\n // Verify chain ID matches expected network\n const expectedChainId = getAptosChainId(requirements.network);\n const txChainId = Number(transaction.rawTransaction.chain_id.chainId);\n if (txChainId !== expectedChainId) {\n return {\n isValid: false,\n invalidReason: `invalid_exact_aptos_payload_chain_id_mismatch: expected ${expectedChainId}, got ${txChainId}`,\n payer: senderAddress,\n };\n }\n\n // Verify sender matches authenticator public key (for Ed25519 accounts)\n // Note: SingleKey and MultiKey authenticators are validated during simulation (step 11)\n if (senderAuthenticator.isEd25519()) {\n const pubKey = senderAuthenticator.public_key as Ed25519PublicKey;\n const derivedAddress = AccountAddress.from(pubKey.authKey().derivedAddress());\n if (!derivedAddress.equals(transaction.rawTransaction.sender)) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_sender_authenticator_mismatch\",\n payer: senderAddress,\n };\n }\n }\n\n // For sponsored transactions, verify max gas to prevent gas draining\n if (isSponsored) {\n const maxGasAmount = BigInt(transaction.rawTransaction.max_gas_amount);\n if (maxGasAmount > MAX_GAS_AMOUNT) {\n return {\n isValid: false,\n invalidReason: `invalid_exact_aptos_payload_gas_too_high: ${maxGasAmount} > ${MAX_GAS_AMOUNT}`,\n payer: senderAddress,\n };\n }\n }\n\n // For sponsored transactions, verify fee payer address matches\n if (isSponsored) {\n const expectedFeePayer = AccountAddress.from(requirements.extra.feePayer as string);\n if (!transaction.feePayerAddress || !expectedFeePayer.equals(transaction.feePayerAddress)) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_fee_payer_mismatch\",\n payer: senderAddress,\n };\n }\n }\n\n // SECURITY (reference implementation): Prevent facilitator from signing away their own tokens\n if (isSponsored && signerAddresses.includes(senderAddress)) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_fee_payer_transferring_funds\",\n payer: senderAddress,\n };\n }\n\n // Step 5: Verify the transaction has not expired\n const EXPIRATION_BUFFER_SECONDS = 5;\n const expirationTimestamp = Number(transaction.rawTransaction.expiration_timestamp_secs);\n if (expirationTimestamp < Math.floor(Date.now() / 1000) + EXPIRATION_BUFFER_SECONDS) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_transaction_expired\",\n payer: senderAddress,\n };\n }\n\n // Step 6: Verify the transaction contains a fungible asset transfer operation\n // We accept both primary_fungible_store::transfer and fungible_asset::transfer:\n // - primary_fungible_store::transfer operates on primary stores (the default store for each asset)\n // and automatically creates the recipient's store if it doesn't exist\n // - fungible_asset::transfer is a lower-level function for arbitrary store-to-store transfers\n // and is more gas efficient when stores already exist\n if (!entryFunction) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_missing_entry_function\",\n payer: senderAddress,\n };\n }\n\n const moduleAddress = entryFunction.module_name.address;\n const moduleName = entryFunction.module_name.name.identifier;\n const functionName = entryFunction.function_name.identifier;\n\n const isPrimaryFungibleStore =\n AccountAddress.ONE.equals(moduleAddress) &&\n moduleName === \"primary_fungible_store\" &&\n functionName === \"transfer\";\n\n const isFungibleAsset =\n AccountAddress.ONE.equals(moduleAddress) &&\n moduleName === \"fungible_asset\" &&\n functionName === \"transfer\";\n\n if (!isPrimaryFungibleStore && !isFungibleAsset) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_wrong_function\",\n payer: senderAddress,\n };\n }\n\n if (entryFunction.type_args.length !== 1) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_wrong_type_args\",\n payer: senderAddress,\n };\n }\n\n const args = entryFunction.args;\n if (args.length !== 3) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_wrong_args\",\n payer: senderAddress,\n };\n }\n\n const [faAddressArg, recipientAddressArg, amountArg] = args;\n\n // Step 7: Verify the transfer is for the correct asset\n const faAddress = AccountAddress.from(faAddressArg.bcsToBytes());\n if (!faAddress.equals(AccountAddress.from(requirements.asset))) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_asset_mismatch\",\n payer: senderAddress,\n };\n }\n\n // Step 8: Verify the transfer amount matches\n const amount = new Deserializer(amountArg.bcsToBytes()).deserializeU64().toString(10);\n if (amount !== requirements.amount) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_amount_mismatch\",\n payer: senderAddress,\n };\n }\n\n // Step 9: Verify the transfer recipient matches\n const recipientAddress = AccountAddress.from(recipientAddressArg.bcsToBytes());\n if (!recipientAddress.equals(AccountAddress.from(requirements.payTo))) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_recipient_mismatch\",\n payer: senderAddress,\n };\n }\n\n // Step 10: Verify the sender has sufficient balance\n const aptos = createAptosClient(requirements.network);\n const balance = await aptos.getCurrentFungibleAssetBalances({\n options: {\n where: {\n owner_address: { _eq: senderAddress },\n asset_type: { _eq: requirements.asset },\n },\n },\n });\n const currentBalance = BigInt(balance[0]?.amount ?? 0);\n if (currentBalance < BigInt(requirements.amount)) {\n return {\n isValid: false,\n invalidReason: \"invalid_exact_aptos_payload_insufficient_balance\",\n payer: senderAddress,\n };\n }\n\n // Step 11: Simulate the transaction\n let publicKey: PublicKey | undefined;\n if (senderAuthenticator.isEd25519()) {\n publicKey = senderAuthenticator.public_key;\n } else if (senderAuthenticator.isSingleKey()) {\n publicKey = senderAuthenticator.public_key;\n } else if (senderAuthenticator.isMultiKey()) {\n publicKey = senderAuthenticator.public_keys;\n }\n\n const simulationResult = (\n await aptos.transaction.simulate.simple({ signerPublicKey: publicKey, transaction })\n )[0];\n\n if (!simulationResult.success) {\n return {\n isValid: false,\n invalidReason: `invalid_exact_aptos_payload_simulation_failed: ${simulationResult.vm_status}`,\n payer: senderAddress,\n };\n }\n\n return { isValid: true, invalidReason: undefined, payer: senderAddress };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n isValid: false,\n invalidReason: `invalid_exact_aptos_payload_verification_error: ${errorMessage}`,\n payer: \"\",\n };\n }\n }\n\n /**\n * Settles a payment by submitting the transaction.\n *\n * @param payload - The payment payload to settle\n * @param requirements - The payment requirements\n * @returns Promise resolving to settlement response\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n const aptosPayload = payload.payload as ExactAptosPayload;\n\n const valid = await this.verify(payload, requirements);\n if (!valid.isValid) {\n return {\n success: false,\n network: payload.accepted.network,\n transaction: \"\",\n errorReason: valid.invalidReason ?? \"verification_failed\",\n payer: valid.payer || \"\",\n };\n }\n\n try {\n const { transaction, senderAuthenticator } = deserializeAptosPayment(\n aptosPayload.transaction,\n );\n const senderAddress = transaction.rawTransaction.sender.toStringLong();\n const isSponsored = typeof requirements.extra?.feePayer === \"string\";\n\n const pendingTxn = isSponsored\n ? await this.signer.signAndSubmitAsFeePayer(\n transaction,\n senderAuthenticator,\n requirements.network,\n )\n : await this.signer.submitTransaction(\n transaction,\n senderAuthenticator,\n requirements.network,\n );\n\n await this.signer.waitForTransaction(pendingTxn.hash, requirements.network);\n\n return {\n success: true,\n transaction: pendingTxn.hash,\n network: payload.accepted.network,\n payer: senderAddress,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n errorReason: `transaction_failed: ${errorMessage}`,\n transaction: \"\",\n network: payload.accepted.network,\n payer: valid.payer || \"\",\n };\n }\n }\n}\n","import {\n Deserializer,\n SimpleTransaction,\n AccountAuthenticator,\n TransactionPayloadEntryFunction,\n TransactionPayload,\n EntryFunction,\n Aptos,\n AptosConfig,\n} from \"@aptos-labs/ts-sdk\";\nimport type { DecodedAptosPayload } from \"./types\";\nimport { getAptosNetwork, getAptosRpcUrl } from \"./constants\";\n\n/**\n * Deserialize an Aptos transaction and authenticator from the payment payload.\n *\n * @param transactionBase64 - The base64 encoded transaction payload\n * @returns The deserialized transaction and authenticator\n */\nexport function deserializeAptosPayment(transactionBase64: string): {\n transaction: SimpleTransaction;\n senderAuthenticator: AccountAuthenticator;\n entryFunction?: EntryFunction;\n} {\n // Decode the base64 payload\n const decoded = Buffer.from(transactionBase64, \"base64\").toString(\"utf8\");\n const parsed: DecodedAptosPayload = JSON.parse(decoded);\n\n // Deserialize the transaction bytes\n const transactionBytes = Uint8Array.from(parsed.transaction);\n const transaction = SimpleTransaction.deserialize(new Deserializer(transactionBytes));\n\n // Deserialize the authenticator bytes\n const authBytes = Uint8Array.from(parsed.senderAuthenticator);\n const senderAuthenticator = AccountAuthenticator.deserialize(new Deserializer(authBytes));\n\n // Only Entry Function transactions are supported\n if (!isEntryFunctionPayload(transaction.rawTransaction.payload)) {\n return { transaction, senderAuthenticator };\n }\n\n const entryFunction = transaction.rawTransaction.payload.entryFunction;\n\n return { transaction, senderAuthenticator, entryFunction };\n}\n\n/**\n * Checks if it's an entry function payload.\n *\n * @param payload - The payload to check\n * @returns True if it's an entry function payload\n */\nexport function isEntryFunctionPayload(\n payload: TransactionPayload,\n): payload is TransactionPayloadEntryFunction {\n return \"entryFunction\" in payload;\n}\n\n/**\n * Create an Aptos SDK client for the given network\n *\n * @param network - CAIP-2 network identifier (e.g., \"aptos:1\")\n * @param rpcUrl - Optional custom RPC URL\n * @returns Aptos SDK client\n */\nexport function createAptosClient(network: string, rpcUrl?: string): Aptos {\n const aptosNetwork = getAptosNetwork(network);\n const fullnodeUrl = rpcUrl || getAptosRpcUrl(aptosNetwork);\n\n const config = new AptosConfig({\n network: aptosNetwork,\n fullnode: fullnodeUrl,\n });\n\n return new Aptos(config);\n}\n\n/**\n * Encode an Aptos payment payload to base64\n *\n * @param transactionBytes - The serialized transaction bytes\n * @param authenticatorBytes - The serialized authenticator bytes\n * @returns Base64 encoded payload\n */\nexport function encodeAptosPayload(\n transactionBytes: Uint8Array,\n authenticatorBytes: Uint8Array,\n): string {\n const payload: DecodedAptosPayload = {\n transaction: Array.from(transactionBytes),\n senderAuthenticator: Array.from(authenticatorBytes),\n };\n return Buffer.from(JSON.stringify(payload)).toString(\"base64\");\n}\n","import { Network, NetworkToNodeAPI } from \"@aptos-labs/ts-sdk\";\n\n/**\n * CAIP-2 network identifier for Aptos Mainnet\n */\nexport const APTOS_MAINNET_CAIP2 = \"aptos:1\";\n\n/**\n * CAIP-2 network identifier for Aptos Testnet\n */\nexport const APTOS_TESTNET_CAIP2 = \"aptos:2\";\n\n/**\n * Regex pattern for validating Aptos addresses\n * Matches 64 hex characters with 0x prefix\n */\nexport const APTOS_ADDRESS_REGEX = /^0x[a-fA-F0-9]{64}$/;\n\n/**\n * The primary fungible store transfer function\n */\nexport const TRANSFER_FUNCTION = \"0x1::primary_fungible_store::transfer\";\n\n/**\n * Maximum gas amount allowed for sponsored transactions to prevent gas draining attacks.\n * The Aptos SDK defaults to 200000 for simple transactions, so we allow some headroom.\n */\nexport const MAX_GAS_AMOUNT = 500000n;\n\n/**\n * Maps CAIP-2 network identifiers to Aptos chain IDs.\n *\n * @param network - The CAIP-2 network identifier (e.g., \"aptos:1\")\n * @returns The corresponding chain ID\n */\nexport function getAptosChainId(network: string): number {\n switch (network) {\n case APTOS_MAINNET_CAIP2:\n return 1;\n case APTOS_TESTNET_CAIP2:\n return 2;\n default:\n throw new Error(`Unsupported Aptos network: ${network}`);\n }\n}\n\n/**\n * Default USDC fungible asset metadata address on mainnet.\n */\nexport const USDC_MAINNET_FA = \"0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b\";\n\n/**\n * Default USDC fungible asset metadata address on testnet.\n */\nexport const USDC_TESTNET_FA = \"0x69091fbab5f7d635ee7ac5098cf0c1efbe31d68fec0f2cd565e8d168daf52832\";\n\n/**\n * Maps CAIP-2 network identifiers to Aptos SDK Network enum.\n *\n * @param network - The CAIP-2 network identifier (e.g., \"aptos:1\")\n * @returns The corresponding Aptos SDK Network enum value\n */\nexport function getAptosNetwork(network: string): Network {\n switch (network) {\n case APTOS_MAINNET_CAIP2:\n return Network.MAINNET;\n case APTOS_TESTNET_CAIP2:\n return Network.TESTNET;\n default:\n throw new Error(`Unsupported Aptos network: ${network}`);\n }\n}\n\n/**\n * Gets the default RPC URL for the given Aptos network.\n *\n * @param network - The Aptos SDK Network enum value\n * @returns The default RPC URL for the network\n */\nexport function getAptosRpcUrl(network: Network): string {\n return NetworkToNodeAPI[network];\n}\n"],"mappings":"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","names":["import_ts_sdk","import_ts_sdk"]}
@@ -0,0 +1,67 @@
1
+ import { SchemeNetworkServer, MoneyParser, Price, Network, AssetAmount, PaymentRequirements } from '@x402/core/types';
2
+
3
+ /**
4
+ * Aptos server implementation for the Exact payment scheme.
5
+ */
6
+ declare class ExactAptosScheme implements SchemeNetworkServer {
7
+ readonly scheme = "exact";
8
+ private moneyParsers;
9
+ /**
10
+ * Register a custom money parser in the parser chain.
11
+ *
12
+ * @param parser - Custom function to convert amount to AssetAmount (or null to skip)
13
+ * @returns The service instance for chaining
14
+ */
15
+ registerMoneyParser(parser: MoneyParser): ExactAptosScheme;
16
+ /**
17
+ * Parses a price into an asset amount.
18
+ *
19
+ * @param price - The price to parse
20
+ * @param network - The network to use
21
+ * @returns Promise that resolves to the parsed asset amount
22
+ */
23
+ parsePrice(price: Price, network: Network): Promise<AssetAmount>;
24
+ /**
25
+ * Build payment requirements for this scheme/network combination
26
+ *
27
+ * @param paymentRequirements - The base payment requirements
28
+ * @param supportedKind - The supported kind configuration
29
+ * @param supportedKind.x402Version - The x402 protocol version
30
+ * @param supportedKind.scheme - The payment scheme
31
+ * @param supportedKind.network - The network identifier
32
+ * @param supportedKind.extra - Extra metadata including feePayer address
33
+ * @param extensionKeys - Extension keys supported by the facilitator
34
+ * @returns Enhanced payment requirements with feePayer in extra
35
+ */
36
+ enhancePaymentRequirements(paymentRequirements: PaymentRequirements, supportedKind: {
37
+ x402Version: number;
38
+ scheme: string;
39
+ network: Network;
40
+ extra?: Record<string, unknown>;
41
+ }, extensionKeys: string[]): Promise<PaymentRequirements>;
42
+ /**
43
+ * Parse Money to a decimal number.
44
+ *
45
+ * @param money - The money value to parse
46
+ * @returns Decimal number
47
+ */
48
+ private parseMoneyToDecimal;
49
+ /**
50
+ * Default money conversion to USDC.
51
+ *
52
+ * @param amount - The decimal amount
53
+ * @param network - The network to use
54
+ * @returns The parsed asset amount in USDC
55
+ */
56
+ private defaultMoneyConversion;
57
+ /**
58
+ * Convert a decimal amount string to a token amount string.
59
+ *
60
+ * @param amount - The decimal amount
61
+ * @param decimals - Number of decimals for the token
62
+ * @returns The amount in atomic units as a string
63
+ */
64
+ private convertToTokenAmount;
65
+ }
66
+
67
+ export { ExactAptosScheme };
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/exact/server/index.ts
21
+ var server_exports = {};
22
+ __export(server_exports, {
23
+ ExactAptosScheme: () => ExactAptosScheme
24
+ });
25
+ module.exports = __toCommonJS(server_exports);
26
+
27
+ // src/constants.ts
28
+ var import_ts_sdk = require("@aptos-labs/ts-sdk");
29
+ var APTOS_ADDRESS_REGEX = /^0x[a-fA-F0-9]{64}$/;
30
+ var USDC_MAINNET_FA = "0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b";
31
+ var USDC_TESTNET_FA = "0x69091fbab5f7d635ee7ac5098cf0c1efbe31d68fec0f2cd565e8d168daf52832";
32
+
33
+ // src/exact/server/scheme.ts
34
+ var ExactAptosScheme = class {
35
+ constructor() {
36
+ this.scheme = "exact";
37
+ this.moneyParsers = [];
38
+ }
39
+ /**
40
+ * Register a custom money parser in the parser chain.
41
+ *
42
+ * @param parser - Custom function to convert amount to AssetAmount (or null to skip)
43
+ * @returns The service instance for chaining
44
+ */
45
+ registerMoneyParser(parser) {
46
+ this.moneyParsers.push(parser);
47
+ return this;
48
+ }
49
+ /**
50
+ * Parses a price into an asset amount.
51
+ *
52
+ * @param price - The price to parse
53
+ * @param network - The network to use
54
+ * @returns Promise that resolves to the parsed asset amount
55
+ */
56
+ async parsePrice(price, network) {
57
+ if (typeof price === "object" && price !== null && "amount" in price) {
58
+ if (!price.asset) {
59
+ throw new Error(`Asset address must be specified for AssetAmount on network ${network}`);
60
+ }
61
+ if (!APTOS_ADDRESS_REGEX.test(price.asset)) {
62
+ throw new Error(`Invalid asset address format: ${price.asset}`);
63
+ }
64
+ return { amount: price.amount, asset: price.asset, extra: price.extra || {} };
65
+ }
66
+ const amount = this.parseMoneyToDecimal(price);
67
+ for (const parser of this.moneyParsers) {
68
+ const result = await parser(amount, network);
69
+ if (result !== null) {
70
+ return result;
71
+ }
72
+ }
73
+ return this.defaultMoneyConversion(amount, network);
74
+ }
75
+ /**
76
+ * Build payment requirements for this scheme/network combination
77
+ *
78
+ * @param paymentRequirements - The base payment requirements
79
+ * @param supportedKind - The supported kind configuration
80
+ * @param supportedKind.x402Version - The x402 protocol version
81
+ * @param supportedKind.scheme - The payment scheme
82
+ * @param supportedKind.network - The network identifier
83
+ * @param supportedKind.extra - Extra metadata including feePayer address
84
+ * @param extensionKeys - Extension keys supported by the facilitator
85
+ * @returns Enhanced payment requirements with feePayer in extra
86
+ */
87
+ enhancePaymentRequirements(paymentRequirements, supportedKind, extensionKeys) {
88
+ void extensionKeys;
89
+ const extra = { ...paymentRequirements.extra };
90
+ if (typeof supportedKind.extra?.feePayer === "string") {
91
+ extra.feePayer = supportedKind.extra.feePayer;
92
+ }
93
+ return Promise.resolve({ ...paymentRequirements, extra });
94
+ }
95
+ /**
96
+ * Parse Money to a decimal number.
97
+ *
98
+ * @param money - The money value to parse
99
+ * @returns Decimal number
100
+ */
101
+ parseMoneyToDecimal(money) {
102
+ if (typeof money === "number") {
103
+ return money;
104
+ }
105
+ const cleanMoney = money.replace(/^\$/, "").trim();
106
+ const amount = parseFloat(cleanMoney);
107
+ if (isNaN(amount)) {
108
+ throw new Error(`Invalid money format: ${money}`);
109
+ }
110
+ return amount;
111
+ }
112
+ /**
113
+ * Default money conversion to USDC.
114
+ *
115
+ * @param amount - The decimal amount
116
+ * @param network - The network to use
117
+ * @returns The parsed asset amount in USDC
118
+ */
119
+ defaultMoneyConversion(amount, network) {
120
+ const decimals = 6;
121
+ const tokenAmount = this.convertToTokenAmount(amount.toString(), decimals);
122
+ const asset = network === "aptos:2" ? USDC_TESTNET_FA : USDC_MAINNET_FA;
123
+ return { amount: tokenAmount, asset, extra: {} };
124
+ }
125
+ /**
126
+ * Convert a decimal amount string to a token amount string.
127
+ *
128
+ * @param amount - The decimal amount
129
+ * @param decimals - Number of decimals for the token
130
+ * @returns The amount in atomic units as a string
131
+ */
132
+ convertToTokenAmount(amount, decimals) {
133
+ const parts = amount.split(".");
134
+ const wholePart = parts[0] || "0";
135
+ const fractionalPart = (parts[1] || "").padEnd(decimals, "0").slice(0, decimals);
136
+ return BigInt(wholePart + fractionalPart).toString();
137
+ }
138
+ };
139
+ // Annotate the CommonJS export names for ESM import in node:
140
+ 0 && (module.exports = {
141
+ ExactAptosScheme
142
+ });
143
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/exact/server/index.ts","../../../../src/constants.ts","../../../../src/exact/server/scheme.ts"],"sourcesContent":["export { ExactAptosScheme } from \"./scheme\";\n","import { Network, NetworkToNodeAPI } from \"@aptos-labs/ts-sdk\";\n\n/**\n * CAIP-2 network identifier for Aptos Mainnet\n */\nexport const APTOS_MAINNET_CAIP2 = \"aptos:1\";\n\n/**\n * CAIP-2 network identifier for Aptos Testnet\n */\nexport const APTOS_TESTNET_CAIP2 = \"aptos:2\";\n\n/**\n * Regex pattern for validating Aptos addresses\n * Matches 64 hex characters with 0x prefix\n */\nexport const APTOS_ADDRESS_REGEX = /^0x[a-fA-F0-9]{64}$/;\n\n/**\n * The primary fungible store transfer function\n */\nexport const TRANSFER_FUNCTION = \"0x1::primary_fungible_store::transfer\";\n\n/**\n * Maximum gas amount allowed for sponsored transactions to prevent gas draining attacks.\n * The Aptos SDK defaults to 200000 for simple transactions, so we allow some headroom.\n */\nexport const MAX_GAS_AMOUNT = 500000n;\n\n/**\n * Maps CAIP-2 network identifiers to Aptos chain IDs.\n *\n * @param network - The CAIP-2 network identifier (e.g., \"aptos:1\")\n * @returns The corresponding chain ID\n */\nexport function getAptosChainId(network: string): number {\n switch (network) {\n case APTOS_MAINNET_CAIP2:\n return 1;\n case APTOS_TESTNET_CAIP2:\n return 2;\n default:\n throw new Error(`Unsupported Aptos network: ${network}`);\n }\n}\n\n/**\n * Default USDC fungible asset metadata address on mainnet.\n */\nexport const USDC_MAINNET_FA = \"0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b\";\n\n/**\n * Default USDC fungible asset metadata address on testnet.\n */\nexport const USDC_TESTNET_FA = \"0x69091fbab5f7d635ee7ac5098cf0c1efbe31d68fec0f2cd565e8d168daf52832\";\n\n/**\n * Maps CAIP-2 network identifiers to Aptos SDK Network enum.\n *\n * @param network - The CAIP-2 network identifier (e.g., \"aptos:1\")\n * @returns The corresponding Aptos SDK Network enum value\n */\nexport function getAptosNetwork(network: string): Network {\n switch (network) {\n case APTOS_MAINNET_CAIP2:\n return Network.MAINNET;\n case APTOS_TESTNET_CAIP2:\n return Network.TESTNET;\n default:\n throw new Error(`Unsupported Aptos network: ${network}`);\n }\n}\n\n/**\n * Gets the default RPC URL for the given Aptos network.\n *\n * @param network - The Aptos SDK Network enum value\n * @returns The default RPC URL for the network\n */\nexport function getAptosRpcUrl(network: Network): string {\n return NetworkToNodeAPI[network];\n}\n","import type {\n AssetAmount,\n Money,\n MoneyParser,\n Network,\n PaymentRequirements,\n Price,\n SchemeNetworkServer,\n} from \"@x402/core/types\";\nimport { APTOS_ADDRESS_REGEX, USDC_MAINNET_FA, USDC_TESTNET_FA } from \"../../constants\";\n\n/**\n * Aptos server implementation for the Exact payment scheme.\n */\nexport class ExactAptosScheme implements SchemeNetworkServer {\n readonly scheme = \"exact\";\n private moneyParsers: MoneyParser[] = [];\n\n /**\n * Register a custom money parser in the parser chain.\n *\n * @param parser - Custom function to convert amount to AssetAmount (or null to skip)\n * @returns The service instance for chaining\n */\n registerMoneyParser(parser: MoneyParser): ExactAptosScheme {\n this.moneyParsers.push(parser);\n return this;\n }\n\n /**\n * Parses a price into an asset amount.\n *\n * @param price - The price to parse\n * @param network - The network to use\n * @returns Promise that resolves to the parsed asset amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n if (typeof price === \"object\" && price !== null && \"amount\" in price) {\n if (!price.asset) {\n throw new Error(`Asset address must be specified for AssetAmount on network ${network}`);\n }\n if (!APTOS_ADDRESS_REGEX.test(price.asset)) {\n throw new Error(`Invalid asset address format: ${price.asset}`);\n }\n return { amount: price.amount, asset: price.asset, extra: price.extra || {} };\n }\n\n const amount = this.parseMoneyToDecimal(price as Money);\n\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, network);\n if (result !== null) {\n return result;\n }\n }\n\n return this.defaultMoneyConversion(amount, network);\n }\n\n /**\n * Build payment requirements for this scheme/network combination\n *\n * @param paymentRequirements - The base payment requirements\n * @param supportedKind - The supported kind configuration\n * @param supportedKind.x402Version - The x402 protocol version\n * @param supportedKind.scheme - The payment scheme\n * @param supportedKind.network - The network identifier\n * @param supportedKind.extra - Extra metadata including feePayer address\n * @param extensionKeys - Extension keys supported by the facilitator\n * @returns Enhanced payment requirements with feePayer in extra\n */\n enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n x402Version: number;\n scheme: string;\n network: Network;\n extra?: Record<string, unknown>;\n },\n extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n void extensionKeys;\n\n const extra: Record<string, unknown> = { ...paymentRequirements.extra };\n if (typeof supportedKind.extra?.feePayer === \"string\") {\n extra.feePayer = supportedKind.extra.feePayer;\n }\n\n return Promise.resolve({ ...paymentRequirements, extra });\n }\n\n /**\n * Parse Money to a decimal number.\n *\n * @param money - The money value to parse\n * @returns Decimal number\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === \"number\") {\n return money;\n }\n const cleanMoney = money.replace(/^\\$/, \"\").trim();\n const amount = parseFloat(cleanMoney);\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n return amount;\n }\n\n /**\n * Default money conversion to USDC.\n *\n * @param amount - The decimal amount\n * @param network - The network to use\n * @returns The parsed asset amount in USDC\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const decimals = 6;\n const tokenAmount = this.convertToTokenAmount(amount.toString(), decimals);\n const asset = network === \"aptos:2\" ? USDC_TESTNET_FA : USDC_MAINNET_FA;\n return { amount: tokenAmount, asset, extra: {} };\n }\n\n /**\n * Convert a decimal amount string to a token amount string.\n *\n * @param amount - The decimal amount\n * @param decimals - Number of decimals for the token\n * @returns The amount in atomic units as a string\n */\n private convertToTokenAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA0C;AAgBnC,IAAM,sBAAsB;AAiC5B,IAAM,kBAAkB;AAKxB,IAAM,kBAAkB;;;ACxCxB,IAAM,mBAAN,MAAsD;AAAA,EAAtD;AACL,SAAS,SAAS;AAClB,SAAQ,eAA8B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvC,oBAAoB,QAAuC;AACzD,SAAK,aAAa,KAAK,MAAM;AAC7B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,WAAW,OAAc,SAAwC;AACrE,QAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,OAAO;AACpE,UAAI,CAAC,MAAM,OAAO;AAChB,cAAM,IAAI,MAAM,8DAA8D,OAAO,EAAE;AAAA,MACzF;AACA,UAAI,CAAC,oBAAoB,KAAK,MAAM,KAAK,GAAG;AAC1C,cAAM,IAAI,MAAM,iCAAiC,MAAM,KAAK,EAAE;AAAA,MAChE;AACA,aAAO,EAAE,QAAQ,MAAM,QAAQ,OAAO,MAAM,OAAO,OAAO,MAAM,SAAS,CAAC,EAAE;AAAA,IAC9E;AAEA,UAAM,SAAS,KAAK,oBAAoB,KAAc;AAEtD,eAAW,UAAU,KAAK,cAAc;AACtC,YAAM,SAAS,MAAM,OAAO,QAAQ,OAAO;AAC3C,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,KAAK,uBAAuB,QAAQ,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,2BACE,qBACA,eAMA,eAC8B;AAC9B,SAAK;AAEL,UAAM,QAAiC,EAAE,GAAG,oBAAoB,MAAM;AACtE,QAAI,OAAO,cAAc,OAAO,aAAa,UAAU;AACrD,YAAM,WAAW,cAAc,MAAM;AAAA,IACvC;AAEA,WAAO,QAAQ,QAAQ,EAAE,GAAG,qBAAqB,MAAM,CAAC;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,oBAAoB,OAAgC;AAC1D,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AACA,UAAM,aAAa,MAAM,QAAQ,OAAO,EAAE,EAAE,KAAK;AACjD,UAAM,SAAS,WAAW,UAAU;AACpC,QAAI,MAAM,MAAM,GAAG;AACjB,YAAM,IAAI,MAAM,yBAAyB,KAAK,EAAE;AAAA,IAClD;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,uBAAuB,QAAgB,SAA+B;AAC5E,UAAM,WAAW;AACjB,UAAM,cAAc,KAAK,qBAAqB,OAAO,SAAS,GAAG,QAAQ;AACzE,UAAM,QAAQ,YAAY,YAAY,kBAAkB;AACxD,WAAO,EAAE,QAAQ,aAAa,OAAO,OAAO,CAAC,EAAE;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,qBAAqB,QAAgB,UAA0B;AACrE,UAAM,QAAQ,OAAO,MAAM,GAAG;AAC9B,UAAM,YAAY,MAAM,CAAC,KAAK;AAC9B,UAAM,kBAAkB,MAAM,CAAC,KAAK,IAAI,OAAO,UAAU,GAAG,EAAE,MAAM,GAAG,QAAQ;AAC/E,WAAO,OAAO,YAAY,cAAc,EAAE,SAAS;AAAA,EACrD;AACF;","names":[]}