@piprail/sdk 2.3.0 → 2.4.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/CHANGELOG.md +99 -0
- package/dist/algorand-TMA62DN2.cjs +678 -0
- package/dist/algorand-WB6PBJU4.js +678 -0
- package/dist/aptos-JQMZNTMD.cjs +673 -0
- package/dist/aptos-QAAXIUY3.js +673 -0
- package/dist/index.cjs +161 -17
- package/dist/index.d.cts +73 -23
- package/dist/index.d.ts +73 -23
- package/dist/index.js +158 -14
- package/dist/{solana-E4MD6JJ6.js → solana-3FMCWSEE.js} +20 -0
- package/dist/{solana-TLHL2KNY.cjs → solana-M3VOHCMO.cjs} +20 -0
- package/package.json +1 -1
- package/dist/algorand-GSFVZTBF.js +0 -389
- package/dist/algorand-HZS43N4P.cjs +0 -389
- package/dist/aptos-RIL56C7L.js +0 -352
- package/dist/aptos-TRCCJRZA.cjs +0 -352
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
var _chunkJG6KRAW6cjs = require('./chunk-JG6KRAW6.cjs');
|
|
13
|
+
|
|
14
|
+
// src/drivers/aptos/index.ts
|
|
15
|
+
var _tssdk = require('@aptos-labs/ts-sdk');
|
|
16
|
+
|
|
17
|
+
// src/drivers/aptos/chains.ts
|
|
18
|
+
var APT_DECIMALS = 8;
|
|
19
|
+
var APT_SYMBOL = "APT";
|
|
20
|
+
var APT_FA_METADATA = "0xa";
|
|
21
|
+
var APTOS_MAINNET = {
|
|
22
|
+
caip2: "aptos:1",
|
|
23
|
+
defaultRpc: "https://fullnode.mainnet.aptoslabs.com/v1",
|
|
24
|
+
tokens: {
|
|
25
|
+
// Circle USDC — FA metadata + 6 decimals verified live on mainnet
|
|
26
|
+
// (0x1::fungible_asset::Metadata → symbol "USDC", decimals 6) before shipping.
|
|
27
|
+
USDC: {
|
|
28
|
+
metadata: "0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b",
|
|
29
|
+
decimals: 6,
|
|
30
|
+
symbol: "USDC"
|
|
31
|
+
},
|
|
32
|
+
// Tether USD₮ — use the FA *metadata* address (NOT the issuer/creator address
|
|
33
|
+
// 0xf73e…73cb). Verified live (Metadata → name "Tether USD", symbol "USDt", decimals 6).
|
|
34
|
+
USDT: {
|
|
35
|
+
metadata: "0x357b0b74bc833e95a115ad22604854d6b0fca151cecd94111770e5d6ffc9dc2b",
|
|
36
|
+
decimals: 6,
|
|
37
|
+
symbol: "USDT"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// src/drivers/aptos/pay.ts
|
|
43
|
+
var MAX_GAS_AMOUNT = 5e4;
|
|
44
|
+
async function payAptos(params) {
|
|
45
|
+
const { client, signer, sender, accept } = params;
|
|
46
|
+
const metadata = accept.asset === "native" ? APT_FA_METADATA : accept.asset;
|
|
47
|
+
try {
|
|
48
|
+
const transaction = await client.build({
|
|
49
|
+
sender,
|
|
50
|
+
data: {
|
|
51
|
+
function: "0x1::primary_fungible_store::transfer",
|
|
52
|
+
typeArguments: ["0x1::fungible_asset::Metadata"],
|
|
53
|
+
// u64 amount goes as a base-unit string; the FA metadata object + recipient are addresses.
|
|
54
|
+
functionArguments: [metadata, accept.payTo, accept.amount]
|
|
55
|
+
},
|
|
56
|
+
options: { maxGasAmount: MAX_GAS_AMOUNT }
|
|
57
|
+
});
|
|
58
|
+
const res = await client.signSubmit({ signer, transaction });
|
|
59
|
+
return res.hash;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
if (err instanceof _chunkJG6KRAW6cjs.InsufficientFundsError) throw err;
|
|
62
|
+
if (isAptosAffordability(err)) {
|
|
63
|
+
throw new (0, _chunkJG6KRAW6cjs.InsufficientFundsError)(
|
|
64
|
+
err instanceof Error ? err.message : "Insufficient APT/token balance for the payment.",
|
|
65
|
+
{ cause: err }
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
throw _nullishCoalesce(_chunkJG6KRAW6cjs.toInsufficientFundsError.call(void 0, err), () => ( err));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function isAptosAffordability(err) {
|
|
72
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
73
|
+
return /INSUFFICIENT_BALANCE|EINSUFFICIENT_BALANCE|insufficient.*(balance|gas|funds)|coin store|balance is too low|EINSUFFICIENT/i.test(
|
|
74
|
+
m
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/drivers/aptos/exact.ts
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
var TRANSFER_MODULE = "0x1::primary_fungible_store";
|
|
89
|
+
var TRANSFER_FUNCTION = "transfer";
|
|
90
|
+
var METADATA_TYPE = "0x1::fungible_asset::Metadata";
|
|
91
|
+
var BUYER_MAX_GAS_AMOUNT = 5e4;
|
|
92
|
+
var MAX_GAS_AMOUNT_CAP = 100000n;
|
|
93
|
+
var MAX_GAS_UNIT_PRICE_CAP = 2000n;
|
|
94
|
+
var b64encode = (bytes) => Buffer.from(bytes).toString("base64");
|
|
95
|
+
var b64decode = (s) => new Uint8Array(Buffer.from(s, "base64"));
|
|
96
|
+
function parseAddr(addr) {
|
|
97
|
+
try {
|
|
98
|
+
return _tssdk.AccountAddress.fromString(addr);
|
|
99
|
+
} catch (e2) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function payExactAptos(input) {
|
|
104
|
+
const { client, sender, accept } = input;
|
|
105
|
+
if (accept.asset === "native") {
|
|
106
|
+
throw new (0, _chunkJG6KRAW6cjs.UnsupportedSchemeError)(
|
|
107
|
+
"Aptos exact is Fungible-Asset only (a primary_fungible_store transfer); native APT is not exact-payable. Pay via onchain-proof."
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
const feePayer = accept.extra.feePayer;
|
|
111
|
+
if (!feePayer) {
|
|
112
|
+
throw new (0, _chunkJG6KRAW6cjs.UnsupportedSchemeError)("Aptos exact rail must advertise extra.feePayer (the gas sponsor address).");
|
|
113
|
+
}
|
|
114
|
+
const feePayerAddr = parseAddr(feePayer);
|
|
115
|
+
if (!feePayerAddr) throw new (0, _chunkJG6KRAW6cjs.UnsupportedSchemeError)(`Aptos exact: extra.feePayer "${feePayer}" is not a valid Aptos address.`);
|
|
116
|
+
const payToAddr = parseAddr(accept.payTo);
|
|
117
|
+
if (!payToAddr) throw new (0, _chunkJG6KRAW6cjs.UnsupportedSchemeError)(`Aptos exact: payTo "${accept.payTo}" is not a valid Aptos address.`);
|
|
118
|
+
const metadataAddr = parseAddr(accept.asset);
|
|
119
|
+
if (!metadataAddr) throw new (0, _chunkJG6KRAW6cjs.UnsupportedSchemeError)(`Aptos exact: asset "${accept.asset}" is not a valid FA metadata address.`);
|
|
120
|
+
const senderAddr = parseAddr(sender);
|
|
121
|
+
if (!senderAddr) throw new (0, _chunkJG6KRAW6cjs.UnsupportedSchemeError)(`Aptos exact: payer "${sender}" is not a valid Aptos address.`);
|
|
122
|
+
if (feePayerAddr.equals(senderAddr)) {
|
|
123
|
+
throw new (0, _chunkJG6KRAW6cjs.UnsupportedSchemeError)(
|
|
124
|
+
"Aptos exact: the fee payer must differ from the payer \u2014 the buyer must pay ZERO APT (the sponsor pays gas)."
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const tx = await client.buildFeePayerTransfer({
|
|
128
|
+
sender,
|
|
129
|
+
metadata: metadataAddr.toString(),
|
|
130
|
+
payTo: payToAddr.toString(),
|
|
131
|
+
amount: accept.amount,
|
|
132
|
+
maxGasAmount: BUYER_MAX_GAS_AMOUNT
|
|
133
|
+
});
|
|
134
|
+
tx.feePayerAddress = feePayerAddr;
|
|
135
|
+
const senderAuth = client.signAsSender({ transaction: tx });
|
|
136
|
+
return {
|
|
137
|
+
payload: { transaction: b64encode(tx.bcsToBytes()), senderAuth: b64encode(senderAuth.bcsToBytes()) },
|
|
138
|
+
payerFrom: sender,
|
|
139
|
+
nonce: dedupeNonce(senderAuth)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function dedupeNonce(auth) {
|
|
143
|
+
if (auth instanceof _tssdk.AccountAuthenticatorEd25519) return auth.signature.toString();
|
|
144
|
+
return b64encode(auth.bcsToBytes());
|
|
145
|
+
}
|
|
146
|
+
function shorten(msg) {
|
|
147
|
+
const oneLine = msg.replace(/\s+/g, " ").trim();
|
|
148
|
+
return oneLine.length > 200 ? `${oneLine.slice(0, 200)}\u2026` : oneLine;
|
|
149
|
+
}
|
|
150
|
+
function fail(error, detail) {
|
|
151
|
+
return { ok: false, error, detail };
|
|
152
|
+
}
|
|
153
|
+
function argBytes(arg) {
|
|
154
|
+
const v = _optionalChain([arg, 'access', _ => _.value, 'optionalAccess', _2 => _2.value]);
|
|
155
|
+
if (v instanceof Uint8Array) return v;
|
|
156
|
+
throw new Error("unexpected entry-function argument shape");
|
|
157
|
+
}
|
|
158
|
+
async function verifyAndSettleExactAptos(input) {
|
|
159
|
+
const { client, feePayerAddr, payload, accept } = input;
|
|
160
|
+
const railFeePayer = accept.extra.feePayer;
|
|
161
|
+
if (!railFeePayer) throw new (0, _chunkJG6KRAW6cjs.SettlementError)("Aptos exact: rail is missing extra.feePayer.");
|
|
162
|
+
if (railFeePayer !== feePayerAddr) {
|
|
163
|
+
throw new (0, _chunkJG6KRAW6cjs.SettlementError)("Aptos exact: the gate relayer address does not match the rail extra.feePayer \u2014 misconfigured rail.");
|
|
164
|
+
}
|
|
165
|
+
if (accept.asset === "native") throw new (0, _chunkJG6KRAW6cjs.SettlementError)("Aptos exact: native APT is not exact-payable (misconfigured rail).");
|
|
166
|
+
const railFeePayerAddr = parseAddr(railFeePayer);
|
|
167
|
+
const railPayTo = parseAddr(accept.payTo);
|
|
168
|
+
const railMetadata = parseAddr(accept.asset);
|
|
169
|
+
if (!railFeePayerAddr || !railPayTo || !railMetadata) {
|
|
170
|
+
throw new (0, _chunkJG6KRAW6cjs.SettlementError)("Aptos exact: rail feePayer/payTo/asset is not a valid Aptos address (misconfigured rail).");
|
|
171
|
+
}
|
|
172
|
+
let tx;
|
|
173
|
+
let senderAuth;
|
|
174
|
+
try {
|
|
175
|
+
tx = _tssdk.SimpleTransaction.deserialize(new (0, _tssdk.Deserializer)(b64decode(payload.transaction)));
|
|
176
|
+
senderAuth = _tssdk.AccountAuthenticator.deserialize(new (0, _tssdk.Deserializer)(b64decode(payload.senderAuth)));
|
|
177
|
+
} catch (err) {
|
|
178
|
+
return fail("signature_invalid", `Unparseable Aptos transaction/authenticator: ${shorten(err instanceof Error ? err.message : String(err))}.`);
|
|
179
|
+
}
|
|
180
|
+
const raw = tx.rawTransaction;
|
|
181
|
+
if (!tx.feePayerAddress || !tx.feePayerAddress.equals(railFeePayerAddr)) {
|
|
182
|
+
return fail("signature_invalid", `The transaction fee payer ${_nullishCoalesce(_optionalChain([tx, 'access', _3 => _3.feePayerAddress, 'optionalAccess', _4 => _4.toString, 'call', _5 => _5()]), () => ( "(none)"))} is not the rail fee payer ${railFeePayer}.`);
|
|
183
|
+
}
|
|
184
|
+
const payloadKind = raw.payload;
|
|
185
|
+
if (!(payloadKind instanceof _tssdk.TransactionPayloadEntryFunction)) {
|
|
186
|
+
return fail("transfer_not_found", "The transaction is not an entry-function call.");
|
|
187
|
+
}
|
|
188
|
+
const ef = payloadKind.entryFunction;
|
|
189
|
+
const fnId = `${ef.module_name.address.toString()}::${ef.module_name.name.identifier}`;
|
|
190
|
+
if (fnId !== TRANSFER_MODULE || ef.function_name.identifier !== TRANSFER_FUNCTION) {
|
|
191
|
+
return fail("transfer_not_found", `Not a ${TRANSFER_MODULE}::${TRANSFER_FUNCTION} call (got ${fnId}::${ef.function_name.identifier}).`);
|
|
192
|
+
}
|
|
193
|
+
if (ef.type_args.length !== 1 || ef.type_args[0].toString() !== METADATA_TYPE) {
|
|
194
|
+
return fail("transfer_not_found", `Transfer type argument is not <${METADATA_TYPE}>.`);
|
|
195
|
+
}
|
|
196
|
+
if (ef.args.length !== 3) {
|
|
197
|
+
return fail("transfer_not_found", `Transfer expects 3 arguments (metadata, recipient, amount), got ${ef.args.length}.`);
|
|
198
|
+
}
|
|
199
|
+
let metadata, recipient, amount;
|
|
200
|
+
try {
|
|
201
|
+
metadata = _tssdk.AccountAddress.deserialize(new (0, _tssdk.Deserializer)(argBytes(ef.args[0])));
|
|
202
|
+
recipient = _tssdk.AccountAddress.deserialize(new (0, _tssdk.Deserializer)(argBytes(ef.args[1])));
|
|
203
|
+
amount = new (0, _tssdk.Deserializer)(argBytes(ef.args[2])).deserializeU64();
|
|
204
|
+
} catch (err) {
|
|
205
|
+
return fail("transfer_not_found", `Could not decode the transfer arguments: ${shorten(err instanceof Error ? err.message : String(err))}.`);
|
|
206
|
+
}
|
|
207
|
+
if (!metadata.equals(railMetadata)) {
|
|
208
|
+
return fail("transfer_not_found", `Transfer asset ${metadata.toString()} \u2260 rail asset ${accept.asset}.`);
|
|
209
|
+
}
|
|
210
|
+
if (!recipient.equals(railPayTo)) {
|
|
211
|
+
return fail("wrong_recipient", `Transfer pays ${recipient.toString()}, not payTo ${accept.payTo}.`);
|
|
212
|
+
}
|
|
213
|
+
if (amount < BigInt(accept.amount)) {
|
|
214
|
+
return fail("amount_too_low", `Transfer pays ${amount} of the FA, required ${accept.amount}.`);
|
|
215
|
+
}
|
|
216
|
+
if (raw.max_gas_amount > MAX_GAS_AMOUNT_CAP) {
|
|
217
|
+
return fail("signature_invalid", `max_gas_amount ${raw.max_gas_amount} exceeds the cap ${MAX_GAS_AMOUNT_CAP} (fee-payer drain guard).`);
|
|
218
|
+
}
|
|
219
|
+
if (raw.gas_unit_price > MAX_GAS_UNIT_PRICE_CAP) {
|
|
220
|
+
return fail("signature_invalid", `gas_unit_price ${raw.gas_unit_price} exceeds the cap ${MAX_GAS_UNIT_PRICE_CAP} (fee-payer drain guard).`);
|
|
221
|
+
}
|
|
222
|
+
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
223
|
+
if (raw.expiration_timestamp_secs <= now) {
|
|
224
|
+
return fail("payment_expired", `Transaction expired: expiration ${raw.expiration_timestamp_secs} <= now ${now}.`);
|
|
225
|
+
}
|
|
226
|
+
const payer = raw.sender.toString();
|
|
227
|
+
if (senderAuth instanceof _tssdk.AccountAuthenticatorEd25519) {
|
|
228
|
+
let sigOk = false;
|
|
229
|
+
try {
|
|
230
|
+
const message = _tssdk.generateSigningMessageForTransaction.call(void 0, tx);
|
|
231
|
+
sigOk = senderAuth.public_key.verifySignature({ message, signature: senderAuth.signature });
|
|
232
|
+
} catch (err) {
|
|
233
|
+
return fail("signature_invalid", `Could not verify the sender signature: ${shorten(err instanceof Error ? err.message : String(err))}.`);
|
|
234
|
+
}
|
|
235
|
+
if (!sigOk) return fail("signature_invalid", "The sender signature does not verify over the transaction.");
|
|
236
|
+
if (senderAuth.public_key.authKey().derivedAddress().toString() !== payer) {
|
|
237
|
+
return fail("signature_invalid", "The sender signature key does not derive to the transaction sender.");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
let feePayerAuth;
|
|
241
|
+
try {
|
|
242
|
+
feePayerAuth = client.signAsFeePayer({ transaction: tx });
|
|
243
|
+
} catch (err) {
|
|
244
|
+
throw new (0, _chunkJG6KRAW6cjs.SettlementError)(`Aptos exact settle: the gate could not sign as fee payer (${shorten(err instanceof Error ? err.message : String(err))}).`, { cause: err });
|
|
245
|
+
}
|
|
246
|
+
if (senderAuth instanceof _tssdk.AccountAuthenticatorEd25519) {
|
|
247
|
+
try {
|
|
248
|
+
const sim = await client.simulate({ transaction: tx, senderPublicKey: senderAuth.public_key });
|
|
249
|
+
if (!sim.success) {
|
|
250
|
+
const m = sim.vmStatus;
|
|
251
|
+
if (/EXPIRED|expired/i.test(m)) return fail("payment_expired", `Transaction would fail: ${shorten(m)}.`);
|
|
252
|
+
if (/SEQUENCE_NUMBER_TOO_OLD|already/i.test(m)) return fail("tx_already_used", `This payment was already settled: ${shorten(m)}.`);
|
|
253
|
+
if (/INSUFFICIENT/i.test(m)) return fail("tx_reverted", `Insufficient balance to settle: ${shorten(m)}.`);
|
|
254
|
+
return fail("tx_reverted", `Transaction would fail on-chain: ${shorten(m)}.`);
|
|
255
|
+
}
|
|
256
|
+
} catch (e3) {
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
let hash;
|
|
260
|
+
try {
|
|
261
|
+
hash = await client.submit({ transaction: tx, senderAuthenticator: senderAuth, feePayerAuthenticator: feePayerAuth });
|
|
262
|
+
} catch (err) {
|
|
263
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
264
|
+
if (/signature|invalid.*auth|INVALID_SIGNATURE/i.test(m)) return fail("signature_invalid", `Settle rejected \u2014 bad signature: ${shorten(m)}.`);
|
|
265
|
+
if (/SEQUENCE_NUMBER_TOO_OLD|already.*(committed|ledger)|duplicate/i.test(m)) return fail("tx_already_used", `This payment was already settled: ${shorten(m)}.`);
|
|
266
|
+
if (/EXPIRED|expired|TOO_NEW/i.test(m)) return fail("payment_expired", `Settle rejected \u2014 expired/stale: ${shorten(m)}.`);
|
|
267
|
+
throw new (0, _chunkJG6KRAW6cjs.SettlementError)(
|
|
268
|
+
`Aptos exact settle: the transaction failed to submit (${shorten(m)}). The buyer's signed transaction is still valid \u2014 fund/fix the fee payer (${railFeePayer}) and the buyer can re-present it.`,
|
|
269
|
+
{ cause: err }
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
let confirmed;
|
|
273
|
+
try {
|
|
274
|
+
confirmed = await client.waitForConfirmation(hash);
|
|
275
|
+
} catch (err) {
|
|
276
|
+
throw new (0, _chunkJG6KRAW6cjs.SettlementError)(`Aptos exact settle: submitted ${hash} but confirmation read failed (${shorten(err instanceof Error ? err.message : String(err))}). It likely landed \u2014 re-verify before re-presenting; do NOT re-pay.`, { cause: err });
|
|
277
|
+
}
|
|
278
|
+
if (confirmed === null) {
|
|
279
|
+
throw new (0, _chunkJG6KRAW6cjs.SettlementError)(`Aptos exact settle: submitted ${hash} but it did not confirm in time. It likely landed \u2014 re-verify before re-presenting; do NOT re-pay.`);
|
|
280
|
+
}
|
|
281
|
+
if (!confirmed.success) {
|
|
282
|
+
return fail("tx_reverted", `Settle tx ${hash} reverted on-chain (a post-simulate race).`);
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
ok: true,
|
|
286
|
+
receipt: {
|
|
287
|
+
scheme: "exact",
|
|
288
|
+
success: true,
|
|
289
|
+
network: accept.network,
|
|
290
|
+
transaction: hash,
|
|
291
|
+
asset: accept.asset,
|
|
292
|
+
amount: accept.amount,
|
|
293
|
+
payer,
|
|
294
|
+
payTo: accept.payTo,
|
|
295
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/drivers/aptos/verify.ts
|
|
301
|
+
async function verifyAptos(params) {
|
|
302
|
+
const { reader, hash, accept } = params;
|
|
303
|
+
const required = BigInt(accept.amount);
|
|
304
|
+
const wantMetadata = accept.asset === "native" ? APT_FA_METADATA : accept.asset;
|
|
305
|
+
let tx;
|
|
306
|
+
try {
|
|
307
|
+
tx = await reader.getTransaction(hash);
|
|
308
|
+
} catch (e4) {
|
|
309
|
+
return txNotFound(hash);
|
|
310
|
+
}
|
|
311
|
+
if (!tx) return txNotFound(hash);
|
|
312
|
+
if (!tx.success) {
|
|
313
|
+
return { ok: false, error: "tx_reverted", detail: `Aptos tx ${hash} did not succeed.` };
|
|
314
|
+
}
|
|
315
|
+
if (typeof tx.timestampSeconds === "number") {
|
|
316
|
+
const ageSeconds = Math.floor(Date.now() / 1e3) - tx.timestampSeconds;
|
|
317
|
+
if (ageSeconds > accept.maxTimeoutSeconds) {
|
|
318
|
+
return {
|
|
319
|
+
ok: false,
|
|
320
|
+
error: "payment_expired",
|
|
321
|
+
detail: `Payment is ${ageSeconds}s old; max allowed is ${accept.maxTimeoutSeconds}s.`
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
let wantStore;
|
|
326
|
+
try {
|
|
327
|
+
wantStore = await reader.primaryStore(accept.payTo, wantMetadata);
|
|
328
|
+
} catch (e5) {
|
|
329
|
+
return txNotFound(hash);
|
|
330
|
+
}
|
|
331
|
+
let paid = 0n;
|
|
332
|
+
for (const d of tx.deposits) {
|
|
333
|
+
if (d.store !== wantStore) continue;
|
|
334
|
+
try {
|
|
335
|
+
paid += BigInt(d.amount);
|
|
336
|
+
} catch (e6) {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (paid < required) {
|
|
341
|
+
return {
|
|
342
|
+
ok: false,
|
|
343
|
+
error: "transfer_not_found",
|
|
344
|
+
detail: `No Aptos transfer of >= ${required} (${wantMetadata}) to ${accept.payTo} in ${hash}.`
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
ok: true,
|
|
349
|
+
receipt: {
|
|
350
|
+
scheme: "onchain-proof",
|
|
351
|
+
success: true,
|
|
352
|
+
network: accept.network,
|
|
353
|
+
transaction: hash,
|
|
354
|
+
asset: accept.asset,
|
|
355
|
+
amount: accept.amount,
|
|
356
|
+
payer: tx.sender,
|
|
357
|
+
payTo: accept.payTo,
|
|
358
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function txNotFound(hash) {
|
|
363
|
+
return {
|
|
364
|
+
ok: false,
|
|
365
|
+
error: "tx_not_found",
|
|
366
|
+
detail: `Aptos tx ${hash} not found or not yet propagated \u2014 retry.`
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/drivers/aptos/wallet.ts
|
|
371
|
+
|
|
372
|
+
function assertAptosWallet(wallet, network) {
|
|
373
|
+
if (typeof wallet !== "object" || wallet === null) {
|
|
374
|
+
throw new (0, _chunkJG6KRAW6cjs.WrongFamilyError)(
|
|
375
|
+
`chain ${network} is Aptos; wallet must be { key } (ed25519-priv-0x\u2026) or { account }.`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
_chunkJG6KRAW6cjs.assertNoLegacyWalletKey.call(void 0, wallet, "Aptos");
|
|
379
|
+
if (!("key" in wallet) && !("account" in wallet)) {
|
|
380
|
+
throw new (0, _chunkJG6KRAW6cjs.WrongFamilyError)(
|
|
381
|
+
`chain ${network} is Aptos; wallet must be { key } (ed25519-priv-0x\u2026) or { account }.`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
return wallet;
|
|
385
|
+
}
|
|
386
|
+
function resolveAptosAccount(config) {
|
|
387
|
+
if (config.account) return config.account;
|
|
388
|
+
if (config.key != null) {
|
|
389
|
+
try {
|
|
390
|
+
return _tssdk.Account.fromPrivateKey({ privateKey: new (0, _tssdk.Ed25519PrivateKey)(config.key) });
|
|
391
|
+
} catch (cause) {
|
|
392
|
+
throw new (0, _chunkJG6KRAW6cjs.WrongFamilyError)(
|
|
393
|
+
"Aptos wallet { key } is not a valid ed25519 secret (ed25519-priv-0x\u2026 or 0x\u2026 hex).",
|
|
394
|
+
{ cause }
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
throw new (0, _chunkJG6KRAW6cjs.WrongFamilyError)("Aptos wallet needs { key } (ed25519-priv-0x\u2026) or { account }.");
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/drivers/aptos/index.ts
|
|
402
|
+
var aptosDriver = {
|
|
403
|
+
family: "aptos",
|
|
404
|
+
resolve(opts) {
|
|
405
|
+
if (opts.chain !== "aptos") return null;
|
|
406
|
+
const rpcUrl = _nullishCoalesce(opts.rpcUrl, () => ( APTOS_MAINNET.defaultRpc));
|
|
407
|
+
return makeAptosNetwork(APTOS_MAINNET, rpcUrl);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
function norm(addr) {
|
|
411
|
+
try {
|
|
412
|
+
return _tssdk.AccountAddress.from(addr).toString();
|
|
413
|
+
} catch (e7) {
|
|
414
|
+
return addr;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function makeAptosNetwork(preset, rpcUrl) {
|
|
418
|
+
const aptos = new (0, _tssdk.Aptos)(new (0, _tssdk.AptosConfig)({ network: _tssdk.Network.MAINNET, fullnode: rpcUrl }));
|
|
419
|
+
const network = preset.caip2;
|
|
420
|
+
const reader = {
|
|
421
|
+
async getTransaction(hash) {
|
|
422
|
+
const tx = await aptos.getTransactionByHash({ transactionHash: hash });
|
|
423
|
+
if (!tx || tx.type !== "user_transaction") return null;
|
|
424
|
+
const deposits = (_nullishCoalesce(tx.events, () => ( []))).filter((e) => e.type === "0x1::fungible_asset::Deposit").map((e) => ({ store: norm(String(e.data.store)), amount: String(e.data.amount) }));
|
|
425
|
+
return {
|
|
426
|
+
success: tx.success === true,
|
|
427
|
+
// Aptos timestamps are microseconds since epoch.
|
|
428
|
+
timestampSeconds: tx.timestamp != null ? Math.floor(Number(tx.timestamp) / 1e6) : void 0,
|
|
429
|
+
sender: tx.sender ? norm(String(tx.sender)) : "",
|
|
430
|
+
deposits
|
|
431
|
+
};
|
|
432
|
+
},
|
|
433
|
+
async primaryStore(owner, metadata) {
|
|
434
|
+
const [addr] = await aptos.view({
|
|
435
|
+
payload: {
|
|
436
|
+
function: "0x1::primary_fungible_store::primary_store_address",
|
|
437
|
+
typeArguments: ["0x1::fungible_asset::Metadata"],
|
|
438
|
+
functionArguments: [owner, metadata]
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
return norm(String(addr));
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
const payClient = {
|
|
445
|
+
build: (input) => aptos.transaction.build.simple({
|
|
446
|
+
sender: input.sender,
|
|
447
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
448
|
+
data: input.data,
|
|
449
|
+
...input.options ? { options: input.options } : {}
|
|
450
|
+
}),
|
|
451
|
+
signSubmit: (input) => aptos.signAndSubmitTransaction({
|
|
452
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
453
|
+
signer: input.signer,
|
|
454
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
455
|
+
transaction: input.transaction
|
|
456
|
+
})
|
|
457
|
+
};
|
|
458
|
+
return {
|
|
459
|
+
family: "aptos",
|
|
460
|
+
network,
|
|
461
|
+
supports: (n) => n === network,
|
|
462
|
+
resolveToken(token) {
|
|
463
|
+
if (token === "native") {
|
|
464
|
+
return { asset: "native", decimals: APT_DECIMALS, symbol: APT_SYMBOL };
|
|
465
|
+
}
|
|
466
|
+
if (typeof token === "string") {
|
|
467
|
+
const info = preset.tokens[token.toUpperCase()];
|
|
468
|
+
if (!info) {
|
|
469
|
+
const known = Object.keys(preset.tokens).join(", ") || "(none built in)";
|
|
470
|
+
throw new (0, _chunkJG6KRAW6cjs.UnknownTokenError)(
|
|
471
|
+
`token "${token}" isn't built in for Aptos (known: ${known}). Pass { metadata, decimals } for a custom Fungible Asset, or use 'native'.`
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
return { asset: info.metadata, decimals: info.decimals, symbol: info.symbol };
|
|
475
|
+
}
|
|
476
|
+
_chunkJG6KRAW6cjs.rejectForeignToken.call(void 0, token, "aptos", network);
|
|
477
|
+
const t = token;
|
|
478
|
+
if (!t.metadata || typeof t.decimals !== "number") {
|
|
479
|
+
throw new (0, _chunkJG6KRAW6cjs.WrongFamilyError)(
|
|
480
|
+
`chain ${network} is Aptos; a custom token must be { metadata, decimals }.`
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
asset: t.metadata,
|
|
485
|
+
decimals: t.decimals,
|
|
486
|
+
...t.symbol ? { symbol: t.symbol } : {}
|
|
487
|
+
};
|
|
488
|
+
},
|
|
489
|
+
describeAsset(asset) {
|
|
490
|
+
if (asset === "native") return { symbol: APT_SYMBOL, decimals: APT_DECIMALS };
|
|
491
|
+
for (const info of Object.values(preset.tokens)) {
|
|
492
|
+
if (info.metadata === asset) return { symbol: info.symbol, decimals: info.decimals };
|
|
493
|
+
}
|
|
494
|
+
return null;
|
|
495
|
+
},
|
|
496
|
+
assertValidPayTo(payTo) {
|
|
497
|
+
const evmLike = /^0x[0-9a-fA-F]{40}$/.test(payTo);
|
|
498
|
+
let valid = false;
|
|
499
|
+
try {
|
|
500
|
+
_tssdk.AccountAddress.from(payTo);
|
|
501
|
+
valid = true;
|
|
502
|
+
} catch (e8) {
|
|
503
|
+
valid = false;
|
|
504
|
+
}
|
|
505
|
+
if (!valid || evmLike) {
|
|
506
|
+
throw new (0, _chunkJG6KRAW6cjs.WrongFamilyError)(
|
|
507
|
+
`chain ${network} is Aptos, but payTo "${payTo}" is not a valid Aptos address (0x + 32 bytes).`
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
bindWallet(wallet) {
|
|
512
|
+
return { _native: assertAptosWallet(wallet, network) };
|
|
513
|
+
},
|
|
514
|
+
async send(wallet, accept) {
|
|
515
|
+
const account = resolveAptosAccount(wallet._native);
|
|
516
|
+
return payAptos({
|
|
517
|
+
client: payClient,
|
|
518
|
+
signer: account,
|
|
519
|
+
sender: account.accountAddress.toString(),
|
|
520
|
+
accept
|
|
521
|
+
});
|
|
522
|
+
},
|
|
523
|
+
async confirm(ref) {
|
|
524
|
+
try {
|
|
525
|
+
const tx = await aptos.waitForTransaction({ transactionHash: ref });
|
|
526
|
+
return { height: String(_nullishCoalesce(tx.version, () => ( "0"))) };
|
|
527
|
+
} catch (err) {
|
|
528
|
+
throw new (0, _chunkJG6KRAW6cjs.ConfirmationTimeoutError)(`Aptos tx ${ref} did not finalize in time.`, { cause: err });
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
async estimateCost(accept) {
|
|
532
|
+
if (accept.scheme === "exact") {
|
|
533
|
+
return _chunkJG6KRAW6cjs.nativeCost.call(void 0, {
|
|
534
|
+
symbol: APT_SYMBOL,
|
|
535
|
+
decimals: APT_DECIMALS,
|
|
536
|
+
fee: 0n,
|
|
537
|
+
basis: "estimated",
|
|
538
|
+
detail: "gasless \u2014 the fee payer (facilitator/relayer) sponsors gas; the buyer pays 0 APT"
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
return _chunkJG6KRAW6cjs.nativeCost.call(void 0, {
|
|
542
|
+
symbol: APT_SYMBOL,
|
|
543
|
+
decimals: APT_DECIMALS,
|
|
544
|
+
fee: 100000n,
|
|
545
|
+
// ~0.001 APT
|
|
546
|
+
basis: "heuristic",
|
|
547
|
+
detail: "\u22480.001 APT (a simple Fungible-Asset transfer; Aptos gas is sub-cent)"
|
|
548
|
+
});
|
|
549
|
+
},
|
|
550
|
+
async balanceOf(wallet, asset) {
|
|
551
|
+
let owner;
|
|
552
|
+
try {
|
|
553
|
+
owner = resolveAptosAccount(wallet._native).accountAddress.toString();
|
|
554
|
+
} catch (e9) {
|
|
555
|
+
return { token: null, native: null };
|
|
556
|
+
}
|
|
557
|
+
const native = await aptos.getAccountAPTAmount({ accountAddress: owner }).then((n) => BigInt(n)).catch(() => null);
|
|
558
|
+
if (asset === "native") return { token: native, native };
|
|
559
|
+
let token = null;
|
|
560
|
+
try {
|
|
561
|
+
const [bal] = await aptos.view({
|
|
562
|
+
payload: {
|
|
563
|
+
function: "0x1::primary_fungible_store::balance",
|
|
564
|
+
typeArguments: ["0x1::fungible_asset::Metadata"],
|
|
565
|
+
functionArguments: [owner, asset]
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
token = BigInt(String(bal));
|
|
569
|
+
} catch (e10) {
|
|
570
|
+
token = null;
|
|
571
|
+
}
|
|
572
|
+
return { token, native };
|
|
573
|
+
},
|
|
574
|
+
// No receive prerequisite — the recipient's primary FA store auto-creates on receipt.
|
|
575
|
+
async recipientReady() {
|
|
576
|
+
return { ready: "n/a" };
|
|
577
|
+
},
|
|
578
|
+
async verify(ref, accept) {
|
|
579
|
+
return verifyAptos({ reader, hash: ref, accept });
|
|
580
|
+
},
|
|
581
|
+
// Standard x402 `exact` rail, BUYER side — build a fee-payer (sponsored, AIP-39) FA transfer,
|
|
582
|
+
// sign ONLY the sender slot (the buyer spends ZERO APT). Never submits. Throws
|
|
583
|
+
// UnsupportedSchemeError for native / a missing feePayer / a feePayer that equals the payer.
|
|
584
|
+
async payExact(wallet, accept) {
|
|
585
|
+
const account = resolveAptosAccount(wallet._native);
|
|
586
|
+
const payClient2 = {
|
|
587
|
+
buildFeePayerTransfer: ({ sender, metadata, payTo, amount, maxGasAmount }) => aptos.transaction.build.simple({
|
|
588
|
+
sender,
|
|
589
|
+
withFeePayer: true,
|
|
590
|
+
data: {
|
|
591
|
+
function: "0x1::primary_fungible_store::transfer",
|
|
592
|
+
typeArguments: ["0x1::fungible_asset::Metadata"],
|
|
593
|
+
functionArguments: [metadata, payTo, amount]
|
|
594
|
+
},
|
|
595
|
+
options: { maxGasAmount }
|
|
596
|
+
}),
|
|
597
|
+
signAsSender: ({ transaction }) => aptos.transaction.sign({ signer: account, transaction })
|
|
598
|
+
};
|
|
599
|
+
const { payload, payerFrom, nonce } = await payExactAptos({
|
|
600
|
+
client: payClient2,
|
|
601
|
+
sender: account.accountAddress.toString(),
|
|
602
|
+
accept
|
|
603
|
+
});
|
|
604
|
+
return { payload, accepted: accept, payerFrom, nonce };
|
|
605
|
+
},
|
|
606
|
+
// Standard x402 `exact` rail, SELLER side — verify the inbound sponsored tx against the trusted
|
|
607
|
+
// accept, then add the fee-payer signature + submit (self-settle, no facilitator). The buyer
|
|
608
|
+
// stays gasless; the merchant's relayer pays gas to receive.
|
|
609
|
+
async settleExactSelf({ relayer, payload, accept }) {
|
|
610
|
+
if (!("senderAuth" in payload) || !("transaction" in payload)) {
|
|
611
|
+
return { ok: false, error: "signature_invalid", detail: "Aptos exact expects a { transaction, senderAuth } payload." };
|
|
612
|
+
}
|
|
613
|
+
const feePayerAccount = resolveAptosAccount(relayer._native);
|
|
614
|
+
const settleClient = {
|
|
615
|
+
signAsFeePayer: ({ transaction }) => aptos.transaction.signAsFeePayer({ signer: feePayerAccount, transaction }),
|
|
616
|
+
async simulate({ transaction, senderPublicKey }) {
|
|
617
|
+
const [sim] = await aptos.transaction.simulate.simple({
|
|
618
|
+
signerPublicKey: senderPublicKey,
|
|
619
|
+
feePayerPublicKey: feePayerAccount.publicKey,
|
|
620
|
+
transaction
|
|
621
|
+
});
|
|
622
|
+
return { success: _optionalChain([sim, 'optionalAccess', _6 => _6.success]) === true, vmStatus: String(_nullishCoalesce(_optionalChain([sim, 'optionalAccess', _7 => _7.vm_status]), () => ( ""))) };
|
|
623
|
+
},
|
|
624
|
+
async submit({ transaction, senderAuthenticator, feePayerAuthenticator }) {
|
|
625
|
+
const res = await aptos.transaction.submit.simple({ transaction, senderAuthenticator, feePayerAuthenticator });
|
|
626
|
+
return res.hash;
|
|
627
|
+
},
|
|
628
|
+
async waitForConfirmation(hash) {
|
|
629
|
+
try {
|
|
630
|
+
const tx = await aptos.waitForTransaction({
|
|
631
|
+
transactionHash: hash,
|
|
632
|
+
options: { checkSuccess: false }
|
|
633
|
+
});
|
|
634
|
+
return { success: tx.success === true };
|
|
635
|
+
} catch (e11) {
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
return verifyAndSettleExactAptos({
|
|
641
|
+
client: settleClient,
|
|
642
|
+
feePayerAddr: feePayerAccount.accountAddress.toString(),
|
|
643
|
+
payload,
|
|
644
|
+
accept
|
|
645
|
+
});
|
|
646
|
+
},
|
|
647
|
+
// The gate's rail-advertisement SPI. The fee payer is EITHER a keyless facilitator's sponsor
|
|
648
|
+
// address (`feePayer`) OR the merchant's own bound `relayer` (self mode); native APT isn't
|
|
649
|
+
// exact-payable. `null` ⇒ no exact rail. Aptos allows feePayer === payTo (the fee-payer
|
|
650
|
+
// signature is separate from the transfer, unlike Solana).
|
|
651
|
+
async resolveExactRail({ asset, relayer, feePayer }) {
|
|
652
|
+
if (asset === "native") return null;
|
|
653
|
+
let fp = feePayer;
|
|
654
|
+
if (!fp && relayer) {
|
|
655
|
+
try {
|
|
656
|
+
fp = resolveAptosAccount(relayer._native).accountAddress.toString();
|
|
657
|
+
} catch (e12) {
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (!fp) return null;
|
|
662
|
+
try {
|
|
663
|
+
_tssdk.AccountAddress.fromString(fp);
|
|
664
|
+
} catch (e13) {
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
return { method: "aptos", extra: { feePayer: fp } };
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
exports.aptosDriver = aptosDriver;
|