@envarai/envarpay 0.2.0-alpha.13 → 0.2.0-alpha.14
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/dist/commerce/chain.d.ts +1 -1
- package/dist/commerce/chain.js +39 -13
- package/package.json +1 -1
package/dist/commerce/chain.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface ExpiredUnusedProof {
|
|
|
14
14
|
authorizationUsed: false;
|
|
15
15
|
}
|
|
16
16
|
/** Checks the canonical chain receipt and exact official-token Transfer. */
|
|
17
|
-
export declare function evmReceiptVerifier(rpcUrls: Readonly<Record<string, string>>, confirmations?: number): (receipt: SettleResponse, payload: PaymentPayload, requirements: PaymentRequirements) => Promise<boolean>;
|
|
17
|
+
export declare function evmReceiptVerifier(rpcUrls: Readonly<Record<string, string>>, confirmations?: number, confirmationWaitMs?: number): (receipt: SettleResponse, payload: PaymentPayload, requirements: PaymentRequirements) => Promise<boolean>;
|
|
18
18
|
/** Locate an original EIP-3009 settlement even when facilitator lost its tx response. */
|
|
19
19
|
export declare function evmSettlementRecovery(rpcUrls: Readonly<Record<string, string>>): {
|
|
20
20
|
expiredUnusedProof: (payload: PaymentPayload, requirements: PaymentRequirements) => Promise<ExpiredUnusedProof | undefined>;
|
package/dist/commerce/chain.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createPublicClient, http, decodeEventLog, parseAbi } from 'viem';
|
|
1
|
+
import { createPublicClient, http, decodeEventLog, parseAbi, TransactionReceiptNotFoundError } from 'viem';
|
|
2
2
|
import { CommerceError } from './types.js';
|
|
3
3
|
import { validateUrl } from './config.js';
|
|
4
4
|
const abi = parseAbi(['event Transfer(address indexed from, address indexed to, uint256 value)']);
|
|
@@ -9,9 +9,11 @@ const officialUsdc = {
|
|
|
9
9
|
'eip155:8453': '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
|
|
10
10
|
};
|
|
11
11
|
/** Checks the canonical chain receipt and exact official-token Transfer. */
|
|
12
|
-
export function evmReceiptVerifier(rpcUrls, confirmations = 2) {
|
|
12
|
+
export function evmReceiptVerifier(rpcUrls, confirmations = 2, confirmationWaitMs = 12000) {
|
|
13
13
|
if (!Number.isInteger(confirmations) || confirmations < 1)
|
|
14
14
|
throw new CommerceError('confirmation_policy', 'Positive confirmation depth required');
|
|
15
|
+
if (!Number.isInteger(confirmationWaitMs) || confirmationWaitMs < 0 || confirmationWaitMs > 30000)
|
|
16
|
+
throw new CommerceError('confirmation_policy', 'Confirmation wait must be between 0 and 30000 milliseconds');
|
|
15
17
|
return async (receipt, payload, requirements) => {
|
|
16
18
|
const rpc = rpcUrls[requirements.network];
|
|
17
19
|
if (!rpc)
|
|
@@ -23,15 +25,38 @@ export function evmReceiptVerifier(rpcUrls, confirmations = 2) {
|
|
|
23
25
|
const client = createPublicClient({ transport: http(rpc, { timeout: 15000, retryCount: 0 }) });
|
|
24
26
|
if (await client.getChainId() !== chainId)
|
|
25
27
|
return false;
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
const deadline = Date.now() + confirmationWaitMs;
|
|
29
|
+
const wait = async () => {
|
|
30
|
+
const remaining = deadline - Date.now();
|
|
31
|
+
if (remaining <= 0)
|
|
32
|
+
return false;
|
|
33
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(1000, remaining)));
|
|
34
|
+
return Date.now() < deadline;
|
|
35
|
+
};
|
|
36
|
+
let mined;
|
|
37
|
+
for (;;) {
|
|
38
|
+
try {
|
|
39
|
+
mined = await client.getTransactionReceipt({ hash: receipt.transaction });
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (error instanceof TransactionReceiptNotFoundError) {
|
|
43
|
+
if (await wait())
|
|
44
|
+
continue;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
if (mined.status !== 'success' || mined.transactionHash.toLowerCase() !== receipt.transaction.toLowerCase())
|
|
50
|
+
return false;
|
|
51
|
+
const block = await client.getBlock({ blockNumber: mined.blockNumber });
|
|
52
|
+
if (block.hash !== mined.blockHash)
|
|
53
|
+
return false;
|
|
54
|
+
const latest = await client.getBlockNumber({ cacheTime: 0 });
|
|
55
|
+
if (latest - mined.blockNumber + 1n >= BigInt(confirmations))
|
|
56
|
+
break;
|
|
57
|
+
if (!await wait())
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
35
60
|
const auth = payload.payload.authorization;
|
|
36
61
|
if (!auth?.from || !auth.nonce || auth.to?.toLowerCase() !== requirements.payTo.toLowerCase() || auth.value !== requirements.amount)
|
|
37
62
|
return false;
|
|
@@ -131,8 +156,9 @@ export function evmSettlementRecovery(rpcUrls) {
|
|
|
131
156
|
// RPC lookup; never reinterpret incomplete search as proof of non-payment.
|
|
132
157
|
if (latest - BigInt(fromBlock) > 100000n)
|
|
133
158
|
throw new CommerceError('archive_reconciliation_required', 'Original authorization requires an archive log lookup');
|
|
134
|
-
|
|
135
|
-
|
|
159
|
+
// The default public Base RPCs limit eth_getLogs to 200-block ranges.
|
|
160
|
+
for (let start = BigInt(fromBlock); start <= latest; start += 200n) {
|
|
161
|
+
const end = start + 199n < latest ? start + 199n : latest;
|
|
136
162
|
const logs = await client.getLogs({ address: requirements.asset, event: authorizationAbi[0], args: { authorizer: auth.from, nonce: auth.nonce }, fromBlock: start, toBlock: end, strict: true });
|
|
137
163
|
const settled = logs.filter(log => !log.removed && log.transactionHash);
|
|
138
164
|
if (settled.length > 1)
|