@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.
- package/README.md +171 -0
- package/dist/exact-direct/client/index.d.cts +91 -0
- package/dist/exact-direct/client/index.d.ts +91 -0
- package/dist/exact-direct/client/index.js +203 -0
- package/dist/exact-direct/client/index.js.map +1 -0
- package/dist/exact-direct/client/index.mjs +175 -0
- package/dist/exact-direct/client/index.mjs.map +1 -0
- package/dist/exact-direct/facilitator/index.d.cts +110 -0
- package/dist/exact-direct/facilitator/index.d.ts +110 -0
- package/dist/exact-direct/facilitator/index.js +352 -0
- package/dist/exact-direct/facilitator/index.js.map +1 -0
- package/dist/exact-direct/facilitator/index.mjs +324 -0
- package/dist/exact-direct/facilitator/index.mjs.map +1 -0
- package/dist/exact-direct/server/index.d.cts +106 -0
- package/dist/exact-direct/server/index.d.ts +106 -0
- package/dist/exact-direct/server/index.js +220 -0
- package/dist/exact-direct/server/index.js.map +1 -0
- package/dist/exact-direct/server/index.mjs +192 -0
- package/dist/exact-direct/server/index.mjs.map +1 -0
- package/dist/index.d.cts +145 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.js +759 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +687 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types-kOweBf4U.d.cts +149 -0
- package/dist/types-kOweBf4U.d.ts +149 -0
- package/package.json +100 -0
- package/src/constants.ts +48 -0
- package/src/exact-direct/client/index.ts +12 -0
- package/src/exact-direct/client/register.ts +83 -0
- package/src/exact-direct/client/scheme.ts +148 -0
- package/src/exact-direct/facilitator/index.ts +12 -0
- package/src/exact-direct/facilitator/register.ts +74 -0
- package/src/exact-direct/facilitator/scheme.ts +300 -0
- package/src/exact-direct/server/index.ts +12 -0
- package/src/exact-direct/server/register.ts +65 -0
- package/src/exact-direct/server/scheme.ts +196 -0
- package/src/index.ts +58 -0
- package/src/tokens.ts +114 -0
- package/src/types.ts +174 -0
- 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,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
|
+
}
|