@t402/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 (42) hide show
  1. package/README.md +171 -0
  2. package/dist/exact-direct/client/index.d.cts +91 -0
  3. package/dist/exact-direct/client/index.d.ts +91 -0
  4. package/dist/exact-direct/client/index.js +203 -0
  5. package/dist/exact-direct/client/index.js.map +1 -0
  6. package/dist/exact-direct/client/index.mjs +175 -0
  7. package/dist/exact-direct/client/index.mjs.map +1 -0
  8. package/dist/exact-direct/facilitator/index.d.cts +110 -0
  9. package/dist/exact-direct/facilitator/index.d.ts +110 -0
  10. package/dist/exact-direct/facilitator/index.js +352 -0
  11. package/dist/exact-direct/facilitator/index.js.map +1 -0
  12. package/dist/exact-direct/facilitator/index.mjs +324 -0
  13. package/dist/exact-direct/facilitator/index.mjs.map +1 -0
  14. package/dist/exact-direct/server/index.d.cts +106 -0
  15. package/dist/exact-direct/server/index.d.ts +106 -0
  16. package/dist/exact-direct/server/index.js +220 -0
  17. package/dist/exact-direct/server/index.js.map +1 -0
  18. package/dist/exact-direct/server/index.mjs +192 -0
  19. package/dist/exact-direct/server/index.mjs.map +1 -0
  20. package/dist/index.d.cts +145 -0
  21. package/dist/index.d.ts +145 -0
  22. package/dist/index.js +759 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/index.mjs +687 -0
  25. package/dist/index.mjs.map +1 -0
  26. package/dist/types-kOweBf4U.d.cts +149 -0
  27. package/dist/types-kOweBf4U.d.ts +149 -0
  28. package/package.json +100 -0
  29. package/src/constants.ts +48 -0
  30. package/src/exact-direct/client/index.ts +12 -0
  31. package/src/exact-direct/client/register.ts +83 -0
  32. package/src/exact-direct/client/scheme.ts +148 -0
  33. package/src/exact-direct/facilitator/index.ts +12 -0
  34. package/src/exact-direct/facilitator/register.ts +74 -0
  35. package/src/exact-direct/facilitator/scheme.ts +300 -0
  36. package/src/exact-direct/server/index.ts +12 -0
  37. package/src/exact-direct/server/register.ts +65 -0
  38. package/src/exact-direct/server/scheme.ts +196 -0
  39. package/src/index.ts +58 -0
  40. package/src/tokens.ts +114 -0
  41. package/src/types.ts +174 -0
  42. package/src/utils.ts +240 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Aptos Exact-Direct Client Scheme
3
+ *
4
+ * The client executes the FA transfer directly and provides
5
+ * the transaction hash as proof of payment.
6
+ */
7
+
8
+ import type {
9
+ SchemeNetworkClient,
10
+ PaymentPayload,
11
+ PaymentRequirements,
12
+ } from "@t402/core/types";
13
+ import { SCHEME_EXACT_DIRECT, APTOS_CAIP2_NAMESPACE } from "../../constants.js";
14
+ import type { ClientAptosSigner, ExactDirectAptosPayload } from "../../types.js";
15
+ import { getTokenConfig } from "../../tokens.js";
16
+ import {
17
+ isValidAptosAddress,
18
+ parseAssetIdentifier,
19
+ compareAddresses,
20
+ } from "../../utils.js";
21
+
22
+ /**
23
+ * Configuration for ExactDirectAptosClient
24
+ */
25
+ export interface ExactDirectAptosClientConfig {
26
+ /**
27
+ * Whether to verify the transfer was successful before returning
28
+ * @default true
29
+ */
30
+ verifyTransfer?: boolean;
31
+ }
32
+
33
+ /**
34
+ * Aptos Exact-Direct Client
35
+ *
36
+ * Implements the client-side payment flow where the client:
37
+ * 1. Receives payment requirements
38
+ * 2. Executes the FA transfer transaction
39
+ * 3. Returns transaction hash as proof
40
+ */
41
+ export class ExactDirectAptosClient implements SchemeNetworkClient {
42
+ readonly scheme = SCHEME_EXACT_DIRECT;
43
+
44
+ constructor(
45
+ private readonly signer: ClientAptosSigner,
46
+ config: ExactDirectAptosClientConfig = {},
47
+ ) {
48
+ // Config reserved for future use (e.g., verifyTransfer option)
49
+ void config;
50
+ }
51
+
52
+ /**
53
+ * Create a payment payload by executing the transfer
54
+ */
55
+ async createPaymentPayload(
56
+ t402Version: number,
57
+ paymentRequirements: PaymentRequirements,
58
+ ): Promise<Pick<PaymentPayload, "t402Version" | "payload">> {
59
+ // Validate requirements
60
+ this.validateRequirements(paymentRequirements);
61
+
62
+ // Get sender address
63
+ const from = await this.signer.getAddress();
64
+
65
+ // Parse asset to get metadata address
66
+ const assetInfo = parseAssetIdentifier(paymentRequirements.asset);
67
+ if (!assetInfo) {
68
+ throw new Error(`Invalid asset identifier: ${paymentRequirements.asset}`);
69
+ }
70
+
71
+ // Get amount
72
+ const amount = BigInt(paymentRequirements.amount);
73
+
74
+ // Check balance
75
+ const balance = await this.signer.getBalance(assetInfo.metadataAddress);
76
+ if (balance < amount) {
77
+ throw new Error(
78
+ `Insufficient balance: have ${balance}, need ${amount}`,
79
+ );
80
+ }
81
+
82
+ // Execute transfer
83
+ const txHash = await this.signer.transfer(
84
+ paymentRequirements.payTo,
85
+ assetInfo.metadataAddress,
86
+ amount,
87
+ );
88
+
89
+ // Create payload
90
+ const payload: ExactDirectAptosPayload = {
91
+ txHash,
92
+ from,
93
+ to: paymentRequirements.payTo,
94
+ amount: paymentRequirements.amount,
95
+ metadataAddress: assetInfo.metadataAddress,
96
+ };
97
+
98
+ return {
99
+ t402Version,
100
+ payload,
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Validate payment requirements
106
+ */
107
+ private validateRequirements(requirements: PaymentRequirements): void {
108
+ // Check scheme
109
+ if (requirements.scheme !== SCHEME_EXACT_DIRECT) {
110
+ throw new Error(
111
+ `Invalid scheme: expected ${SCHEME_EXACT_DIRECT}, got ${requirements.scheme}`,
112
+ );
113
+ }
114
+
115
+ // Check network
116
+ if (!requirements.network.startsWith(`${APTOS_CAIP2_NAMESPACE}:`)) {
117
+ throw new Error(`Invalid network: ${requirements.network}`);
118
+ }
119
+
120
+ // Check payTo address
121
+ if (!isValidAptosAddress(requirements.payTo)) {
122
+ throw new Error(`Invalid payTo address: ${requirements.payTo}`);
123
+ }
124
+
125
+ // Check amount
126
+ const amount = BigInt(requirements.amount);
127
+ if (amount <= 0n) {
128
+ throw new Error(`Invalid amount: ${requirements.amount}`);
129
+ }
130
+
131
+ // Check asset
132
+ const assetInfo = parseAssetIdentifier(requirements.asset);
133
+ if (!assetInfo) {
134
+ throw new Error(`Invalid asset: ${requirements.asset}`);
135
+ }
136
+
137
+ // Verify token is supported
138
+ const tokenConfig = getTokenConfig(requirements.network, "USDT");
139
+ if (tokenConfig && !compareAddresses(tokenConfig.metadataAddress, assetInfo.metadataAddress)) {
140
+ // Allow any valid FA, but log warning for unknown tokens
141
+ console.warn(
142
+ `Using non-standard token: ${assetInfo.metadataAddress}`,
143
+ );
144
+ }
145
+ }
146
+ }
147
+
148
+ export default ExactDirectAptosClient;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Aptos Exact-Direct Facilitator Exports
3
+ */
4
+
5
+ export {
6
+ ExactDirectAptosFacilitator,
7
+ type ExactDirectAptosFacilitatorConfig,
8
+ } from "./scheme.js";
9
+ export {
10
+ registerExactDirectAptosFacilitator,
11
+ type AptosFacilitatorConfig,
12
+ } from "./register.js";
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Registration function for Aptos Exact-Direct facilitator
3
+ */
4
+
5
+ import { t402Facilitator } from "@t402/core/facilitator";
6
+ import type { Network } from "@t402/core/types";
7
+ import type { FacilitatorAptosSigner } from "../../types.js";
8
+ import {
9
+ ExactDirectAptosFacilitator,
10
+ type ExactDirectAptosFacilitatorConfig,
11
+ } from "./scheme.js";
12
+
13
+ /**
14
+ * Configuration options for registering Aptos schemes to a t402Facilitator
15
+ */
16
+ export interface AptosFacilitatorConfig {
17
+ /**
18
+ * The Aptos signer for facilitator operations (verify and settle)
19
+ */
20
+ signer: FacilitatorAptosSigner;
21
+
22
+ /**
23
+ * Optional specific networks to register
24
+ * If not provided, registers wildcard support (aptos:*)
25
+ */
26
+ networks?: Network[];
27
+
28
+ /**
29
+ * Optional scheme configuration
30
+ */
31
+ schemeConfig?: ExactDirectAptosFacilitatorConfig;
32
+ }
33
+
34
+ /**
35
+ * Registers Aptos exact-direct payment schemes to a t402Facilitator instance.
36
+ *
37
+ * @param facilitator - The t402Facilitator instance to register schemes to
38
+ * @param config - Configuration for Aptos facilitator registration
39
+ * @returns The facilitator instance for chaining
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * import { registerExactDirectAptosFacilitator } from "@t402/aptos/exact-direct/facilitator";
44
+ * import { t402Facilitator } from "@t402/core/facilitator";
45
+ *
46
+ * const facilitator = new t402Facilitator();
47
+ * registerExactDirectAptosFacilitator(facilitator, {
48
+ * signer: myAptosSigner,
49
+ * networks: ["aptos:1"]
50
+ * });
51
+ * ```
52
+ */
53
+ export function registerExactDirectAptosFacilitator(
54
+ facilitator: t402Facilitator,
55
+ config: AptosFacilitatorConfig,
56
+ ): t402Facilitator {
57
+ const scheme = new ExactDirectAptosFacilitator(
58
+ config.signer,
59
+ config.schemeConfig,
60
+ );
61
+
62
+ // Register scheme
63
+ if (config.networks && config.networks.length > 0) {
64
+ // Register specific networks
65
+ config.networks.forEach((network) => {
66
+ facilitator.register(network, scheme);
67
+ });
68
+ } else {
69
+ // Register wildcard for all Aptos networks
70
+ facilitator.register("aptos:*", scheme);
71
+ }
72
+
73
+ return facilitator;
74
+ }
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Aptos Exact-Direct Facilitator Scheme
3
+ *
4
+ * Verifies FA transfer transactions and manages replay protection.
5
+ */
6
+
7
+ import type {
8
+ SchemeNetworkFacilitator,
9
+ PaymentPayload,
10
+ PaymentRequirements,
11
+ VerifyResponse,
12
+ SettleResponse,
13
+ Network,
14
+ } from "@t402/core/types";
15
+ import { SCHEME_EXACT_DIRECT, APTOS_CAIP2_NAMESPACE } from "../../constants.js";
16
+ import type {
17
+ FacilitatorAptosSigner,
18
+ ExactDirectAptosPayload,
19
+ } from "../../types.js";
20
+ import {
21
+ isValidTxHash,
22
+ compareAddresses,
23
+ parseAssetIdentifier,
24
+ extractTransferDetails,
25
+ isAptosNetwork,
26
+ } from "../../utils.js";
27
+ import { getDefaultToken } from "../../tokens.js";
28
+
29
+ /**
30
+ * Configuration for ExactDirectAptosFacilitator
31
+ */
32
+ export interface ExactDirectAptosFacilitatorConfig {
33
+ /**
34
+ * Maximum age of transaction in seconds (default: 3600 = 1 hour)
35
+ */
36
+ maxTransactionAge?: number;
37
+
38
+ /**
39
+ * Duration to cache used transaction hashes (in milliseconds)
40
+ */
41
+ usedTxCacheDuration?: number;
42
+ }
43
+
44
+ /**
45
+ * Aptos Exact-Direct Facilitator
46
+ *
47
+ * Implements the facilitator-side verification and settlement.
48
+ * For exact-direct, settlement is a no-op since client already executed.
49
+ */
50
+ export class ExactDirectAptosFacilitator implements SchemeNetworkFacilitator {
51
+ readonly scheme = SCHEME_EXACT_DIRECT;
52
+ readonly caipFamily = `${APTOS_CAIP2_NAMESPACE}:*`;
53
+
54
+ private readonly config: Required<ExactDirectAptosFacilitatorConfig>;
55
+ private usedTxs: Map<string, number> = new Map();
56
+
57
+ constructor(
58
+ private readonly signer: FacilitatorAptosSigner,
59
+ config?: ExactDirectAptosFacilitatorConfig,
60
+ ) {
61
+ this.config = {
62
+ maxTransactionAge: config?.maxTransactionAge ?? 3600,
63
+ usedTxCacheDuration: config?.usedTxCacheDuration ?? 24 * 60 * 60 * 1000, // 24 hours
64
+ };
65
+
66
+ // Start cleanup interval
67
+ this.startCleanupInterval();
68
+ }
69
+
70
+ /**
71
+ * Get extra data for a supported kind
72
+ */
73
+ getExtra(network: Network): Record<string, unknown> | undefined {
74
+ const token = getDefaultToken(network);
75
+ if (!token) {
76
+ return undefined;
77
+ }
78
+ return {
79
+ assetSymbol: token.symbol,
80
+ assetDecimals: token.decimals,
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Get facilitator signer addresses for a network
86
+ */
87
+ getSigners(network: Network): string[] {
88
+ return this.signer.getAddresses(network);
89
+ }
90
+
91
+ /**
92
+ * Verify a payment payload
93
+ */
94
+ async verify(
95
+ payload: PaymentPayload,
96
+ requirements: PaymentRequirements,
97
+ ): Promise<VerifyResponse> {
98
+ // Validate scheme
99
+ if (payload.accepted.scheme !== SCHEME_EXACT_DIRECT) {
100
+ return {
101
+ isValid: false,
102
+ invalidReason: "invalid_scheme",
103
+ };
104
+ }
105
+
106
+ // Validate network
107
+ if (!isAptosNetwork(payload.accepted.network)) {
108
+ return {
109
+ isValid: false,
110
+ invalidReason: "invalid_network",
111
+ };
112
+ }
113
+
114
+ // Extract Aptos-specific payload
115
+ const aptosPayload = payload.payload as ExactDirectAptosPayload;
116
+
117
+ // Validate transaction hash format
118
+ if (!isValidTxHash(aptosPayload.txHash)) {
119
+ return {
120
+ isValid: false,
121
+ invalidReason: "invalid_tx_hash_format",
122
+ };
123
+ }
124
+
125
+ // Check for replay attack
126
+ if (this.isTxUsed(aptosPayload.txHash)) {
127
+ return {
128
+ isValid: false,
129
+ invalidReason: "transaction_already_used",
130
+ payer: aptosPayload.from,
131
+ };
132
+ }
133
+
134
+ try {
135
+ // Query transaction
136
+ const tx = await this.signer.queryTransaction(aptosPayload.txHash);
137
+ if (!tx) {
138
+ return {
139
+ isValid: false,
140
+ invalidReason: "transaction_not_found",
141
+ payer: aptosPayload.from,
142
+ };
143
+ }
144
+
145
+ // Verify transaction was successful
146
+ if (!tx.success) {
147
+ return {
148
+ isValid: false,
149
+ invalidReason: `transaction_failed: ${tx.vmStatus}`,
150
+ payer: aptosPayload.from,
151
+ };
152
+ }
153
+
154
+ // Check transaction age
155
+ if (this.config.maxTransactionAge > 0) {
156
+ const txTimestamp = parseInt(tx.timestamp, 10) / 1000000; // Convert from microseconds
157
+ const now = Date.now() / 1000;
158
+ const age = now - txTimestamp;
159
+ if (age > this.config.maxTransactionAge) {
160
+ return {
161
+ isValid: false,
162
+ invalidReason: `transaction_too_old: ${Math.round(age)} seconds`,
163
+ payer: aptosPayload.from,
164
+ };
165
+ }
166
+ }
167
+
168
+ // Extract transfer details from transaction
169
+ const transferDetails = extractTransferDetails(tx);
170
+ if (!transferDetails) {
171
+ return {
172
+ isValid: false,
173
+ invalidReason: "could_not_extract_transfer_details",
174
+ payer: aptosPayload.from,
175
+ };
176
+ }
177
+
178
+ // Parse expected asset
179
+ const expectedAsset = parseAssetIdentifier(requirements.asset);
180
+ if (!expectedAsset) {
181
+ return {
182
+ isValid: false,
183
+ invalidReason: `invalid_asset_in_requirements: ${requirements.asset}`,
184
+ payer: aptosPayload.from,
185
+ };
186
+ }
187
+
188
+ // Verify recipient
189
+ if (!compareAddresses(transferDetails.to, requirements.payTo)) {
190
+ return {
191
+ isValid: false,
192
+ invalidReason: `recipient_mismatch: expected ${requirements.payTo}, got ${transferDetails.to}`,
193
+ payer: aptosPayload.from,
194
+ };
195
+ }
196
+
197
+ // Verify metadata address (token)
198
+ if (
199
+ !compareAddresses(
200
+ transferDetails.metadataAddress,
201
+ expectedAsset.metadataAddress,
202
+ )
203
+ ) {
204
+ return {
205
+ isValid: false,
206
+ invalidReason: `token_mismatch: expected ${expectedAsset.metadataAddress}, got ${transferDetails.metadataAddress}`,
207
+ payer: aptosPayload.from,
208
+ };
209
+ }
210
+
211
+ // Verify amount
212
+ const expectedAmount = BigInt(requirements.amount);
213
+ if (transferDetails.amount < expectedAmount) {
214
+ return {
215
+ isValid: false,
216
+ invalidReason: `insufficient_amount: expected ${expectedAmount}, got ${transferDetails.amount}`,
217
+ payer: aptosPayload.from,
218
+ };
219
+ }
220
+
221
+ // Mark transaction as used
222
+ this.markTxUsed(aptosPayload.txHash);
223
+
224
+ return {
225
+ isValid: true,
226
+ payer: transferDetails.from,
227
+ };
228
+ } catch (error) {
229
+ return {
230
+ isValid: false,
231
+ invalidReason: `verification_error: ${error instanceof Error ? error.message : String(error)}`,
232
+ payer: aptosPayload.from,
233
+ };
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Settle a payment (no-op for exact-direct since client already executed)
239
+ */
240
+ async settle(
241
+ payload: PaymentPayload,
242
+ requirements: PaymentRequirements,
243
+ ): Promise<SettleResponse> {
244
+ // Verify first
245
+ const verifyResult = await this.verify(payload, requirements);
246
+
247
+ if (!verifyResult.isValid) {
248
+ return {
249
+ success: false,
250
+ errorReason: verifyResult.invalidReason || "verification_failed",
251
+ payer: verifyResult.payer,
252
+ transaction: "",
253
+ network: requirements.network,
254
+ };
255
+ }
256
+
257
+ const aptosPayload = payload.payload as ExactDirectAptosPayload;
258
+
259
+ // For exact-direct, settlement is already complete
260
+ return {
261
+ success: true,
262
+ transaction: aptosPayload.txHash,
263
+ network: requirements.network,
264
+ payer: aptosPayload.from,
265
+ };
266
+ }
267
+
268
+ /**
269
+ * Check if a transaction has been used
270
+ */
271
+ private isTxUsed(txHash: string): boolean {
272
+ return this.usedTxs.has(txHash.toLowerCase());
273
+ }
274
+
275
+ /**
276
+ * Mark a transaction as used
277
+ */
278
+ private markTxUsed(txHash: string): void {
279
+ this.usedTxs.set(txHash.toLowerCase(), Date.now());
280
+ }
281
+
282
+ /**
283
+ * Start the cleanup interval for used transactions
284
+ */
285
+ private startCleanupInterval(): void {
286
+ setInterval(
287
+ () => {
288
+ const cutoff = Date.now() - this.config.usedTxCacheDuration;
289
+ for (const [txHash, usedAt] of this.usedTxs.entries()) {
290
+ if (usedAt < cutoff) {
291
+ this.usedTxs.delete(txHash);
292
+ }
293
+ }
294
+ },
295
+ 60 * 60 * 1000,
296
+ ); // Cleanup every hour
297
+ }
298
+ }
299
+
300
+ export default ExactDirectAptosFacilitator;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Aptos Exact-Direct Server Exports
3
+ */
4
+
5
+ export {
6
+ ExactDirectAptosServer,
7
+ type ExactDirectAptosServerConfig,
8
+ } from "./scheme.js";
9
+ export {
10
+ registerExactDirectAptosServer,
11
+ type AptosServerConfig,
12
+ } from "./register.js";
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Registration function for Aptos Exact-Direct server
3
+ */
4
+
5
+ import { t402ResourceServer } from "@t402/core/server";
6
+ import type { Network } from "@t402/core/types";
7
+ import {
8
+ ExactDirectAptosServer,
9
+ type ExactDirectAptosServerConfig,
10
+ } from "./scheme.js";
11
+
12
+ /**
13
+ * Configuration options for registering Aptos schemes to a t402ResourceServer
14
+ */
15
+ export interface AptosServerConfig {
16
+ /**
17
+ * Optional specific networks to register
18
+ * If not provided, registers wildcard support (aptos:*)
19
+ */
20
+ networks?: Network[];
21
+
22
+ /**
23
+ * Optional scheme configuration
24
+ */
25
+ schemeConfig?: ExactDirectAptosServerConfig;
26
+ }
27
+
28
+ /**
29
+ * Registers Aptos exact-direct payment schemes to a t402ResourceServer instance.
30
+ *
31
+ * @param server - The t402ResourceServer instance to register schemes to
32
+ * @param config - Configuration for Aptos server registration
33
+ * @returns The server instance for chaining
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * import { registerExactDirectAptosServer } from "@t402/aptos/exact-direct/server";
38
+ * import { t402ResourceServer } from "@t402/core/server";
39
+ *
40
+ * const server = new t402ResourceServer();
41
+ * registerExactDirectAptosServer(server, {
42
+ * networks: ["aptos:1"],
43
+ * schemeConfig: { preferredToken: "USDT" }
44
+ * });
45
+ * ```
46
+ */
47
+ export function registerExactDirectAptosServer(
48
+ server: t402ResourceServer,
49
+ config: AptosServerConfig = {},
50
+ ): t402ResourceServer {
51
+ const scheme = new ExactDirectAptosServer(config.schemeConfig);
52
+
53
+ // Register scheme
54
+ if (config.networks && config.networks.length > 0) {
55
+ // Register specific networks
56
+ config.networks.forEach((network) => {
57
+ server.register(network, scheme);
58
+ });
59
+ } else {
60
+ // Register wildcard for all Aptos networks
61
+ server.register("aptos:*", scheme);
62
+ }
63
+
64
+ return server;
65
+ }