otto-intel-mcp 0.1.2 → 0.1.4
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 +76 -18
- package/dist/account-userop-policy-definition.d.ts +14 -0
- package/dist/account-userop-policy-definition.js +12 -0
- package/dist/account-userop-policy-relation.d.ts +371 -0
- package/dist/account-userop-policy-relation.js +87 -0
- package/dist/adapter/cdp-signer.d.ts +76 -9
- package/dist/adapter/cdp-signer.js +274 -33
- package/dist/adapter/evm-call-failure.d.ts +29 -0
- package/dist/adapter/evm-call-failure.js +109 -0
- package/dist/adapter/refusal.d.ts +1 -1
- package/dist/adapter/refusal.js +13 -0
- package/dist/adapter/route-simulator.d.ts +91 -0
- package/dist/adapter/route-simulator.js +143 -0
- package/dist/adapter-cdp-index.d.ts +1 -1
- package/dist/executable-equities.d.ts +55 -0
- package/dist/executable-equities.js +116 -0
- package/dist/executable-yield-markets.d.ts +261 -0
- package/dist/executable-yield-markets.js +154 -0
- package/dist/execution-account-gas-quote.d.ts +141 -0
- package/dist/execution-account-gas-quote.js +443 -0
- package/dist/execution-account-gas-review.d.ts +123 -0
- package/dist/execution-account-gas-review.js +104 -0
- package/dist/execution-account-gas-runtime.d.ts +33 -0
- package/dist/execution-account-gas-runtime.js +131 -0
- package/dist/execution-account-gas-view.d.ts +936 -0
- package/dist/execution-account-gas-view.js +56 -0
- package/dist/execution-config.d.ts +3 -0
- package/dist/execution-config.js +4 -0
- package/dist/execution-delegated-definition.d.ts +7 -6
- package/dist/execution-delegated-definition.js +13 -2
- package/dist/execution-delegation-admin-definition.d.ts +127 -5
- package/dist/execution-delegation-admin-definition.js +155 -5
- package/dist/execution-delegation-admin.d.ts +48 -2
- package/dist/execution-delegation-admin.js +422 -12
- package/dist/execution-delegation-cap-store.d.ts +386 -0
- package/dist/execution-delegation-cap-store.js +1150 -0
- package/dist/execution-delegation-exit-definition.d.ts +309 -0
- package/dist/execution-delegation-exit-definition.js +204 -0
- package/dist/execution-delegation-exit-store.d.ts +114 -0
- package/dist/execution-delegation-exit-store.js +671 -0
- package/dist/execution-delegation-exit.d.ts +4 -0
- package/dist/execution-delegation-exit.js +614 -0
- package/dist/execution-delegation-finality.d.ts +22 -0
- package/dist/execution-delegation-finality.js +90 -0
- package/dist/execution-delegation-operation-store.d.ts +129 -0
- package/dist/execution-delegation-operation-store.js +1107 -0
- package/dist/execution-delegation-operations-definition.d.ts +282 -0
- package/dist/execution-delegation-operations-definition.js +117 -0
- package/dist/execution-delegation-operations.d.ts +6 -0
- package/dist/execution-delegation-operations.js +282 -0
- package/dist/execution-delegation-policy.d.ts +154 -8
- package/dist/execution-delegation-policy.js +273 -40
- package/dist/execution-delegation.d.ts +51 -0
- package/dist/execution-delegation.js +441 -31
- package/dist/execution-errors.d.ts +1 -1
- package/dist/execution-errors.js +122 -0
- package/dist/execution-index.d.ts +7 -2
- package/dist/execution-index.js +7 -2
- package/dist/execution-journal-schema.d.ts +96 -0
- package/dist/execution-journal-schema.js +368 -0
- package/dist/execution-journal-tables.d.ts +14 -0
- package/dist/execution-journal-tables.js +227 -0
- package/dist/execution-policy-compatibility.d.ts +34 -0
- package/dist/execution-policy-compatibility.js +76 -0
- package/dist/execution-registration.d.ts +54 -2
- package/dist/execution-registration.js +151 -5
- package/dist/execution-sponsored-account-profile.d.ts +32 -0
- package/dist/execution-sponsored-account-profile.js +59 -0
- package/dist/execution-sponsored-account.d.ts +1519 -0
- package/dist/execution-sponsored-account.js +222 -0
- package/dist/execution-sponsored-definition.d.ts +1713 -0
- package/dist/execution-sponsored-definition.js +185 -0
- package/dist/execution-sponsored-gas-policy.d.ts +27 -0
- package/dist/execution-sponsored-gas-policy.js +110 -0
- package/dist/execution-sponsored-hosted.d.ts +3 -0
- package/dist/execution-sponsored-hosted.js +71 -0
- package/dist/execution-sponsored-index.d.ts +1322 -0
- package/dist/execution-sponsored-index.js +102 -0
- package/dist/execution-sponsored-mcp-definition.d.ts +1662 -0
- package/dist/execution-sponsored-mcp-definition.js +94 -0
- package/dist/execution-sponsored-receipt.d.ts +47 -0
- package/dist/execution-sponsored-receipt.js +74 -0
- package/dist/execution-sponsored-registration.d.ts +14 -0
- package/dist/execution-sponsored-registration.js +77 -0
- package/dist/execution-sponsored-runtime.d.ts +84 -0
- package/dist/execution-sponsored-runtime.js +149 -0
- package/dist/execution-sponsored-settlement.d.ts +15 -0
- package/dist/execution-sponsored-settlement.js +88 -0
- package/dist/execution-sponsored-store.d.ts +67 -0
- package/dist/execution-sponsored-store.js +517 -0
- package/dist/execution-sponsored-transport.d.ts +90 -0
- package/dist/execution-sponsored-transport.js +180 -0
- package/dist/execution-tool-definitions.d.ts +21 -21
- package/dist/execution-tool-definitions.js +0 -7
- package/dist/execution-tools.d.ts +53 -1
- package/dist/execution-tools.js +171 -13
- package/dist/execution-worker-guard.d.ts +28 -0
- package/dist/execution-worker-guard.js +124 -0
- package/dist/hybrid-yield-index.d.ts +10 -0
- package/dist/hybrid-yield-index.js +11 -0
- package/dist/hybrid-yield-markets.d.ts +96 -0
- package/dist/hybrid-yield-markets.js +124 -0
- package/dist/hybrid-yield.d.ts +411 -0
- package/dist/hybrid-yield.js +155 -0
- package/dist/hyperliquid-order-builder.js +4 -0
- package/dist/lifi-execution-client.d.ts +6 -0
- package/dist/lifi-execution-client.js +3 -0
- package/dist/lp-userop-account.d.ts +461 -0
- package/dist/lp-userop-account.js +417 -0
- package/dist/lp-userop-authority.d.ts +49 -0
- package/dist/lp-userop-authority.js +211 -0
- package/dist/lp-userop-definition.d.ts +233 -0
- package/dist/lp-userop-definition.js +85 -0
- package/dist/lp-userop-hosted.d.ts +3 -0
- package/dist/lp-userop-hosted.js +73 -0
- package/dist/lp-userop-index.d.ts +1569 -0
- package/dist/lp-userop-index.js +112 -0
- package/dist/lp-userop-mcp-definition.d.ts +1220 -0
- package/dist/lp-userop-mcp-definition.js +46 -0
- package/dist/lp-userop-operation.d.ts +1230 -0
- package/dist/lp-userop-operation.js +140 -0
- package/dist/lp-userop-registration.d.ts +14 -0
- package/dist/lp-userop-registration.js +70 -0
- package/dist/lp-userop-settlement.d.ts +89 -0
- package/dist/lp-userop-settlement.js +184 -0
- package/dist/lp-userop.d.ts +643 -0
- package/dist/lp-userop.js +343 -0
- package/dist/native-yield-account-definition.d.ts +1192 -0
- package/dist/native-yield-account-definition.js +232 -0
- package/dist/native-yield-account-gas.d.ts +58 -0
- package/dist/native-yield-account-gas.js +228 -0
- package/dist/native-yield-account-index.d.ts +23 -0
- package/dist/native-yield-account-index.js +28 -0
- package/dist/native-yield-account-policy.d.ts +168 -0
- package/dist/native-yield-account-policy.js +525 -0
- package/dist/native-yield-account-rpc.d.ts +8 -0
- package/dist/native-yield-account-rpc.js +128 -0
- package/dist/native-yield-account-runtime.d.ts +71 -0
- package/dist/native-yield-account-runtime.js +125 -0
- package/dist/native-yield-account-store.d.ts +40 -0
- package/dist/native-yield-account-store.js +307 -0
- package/dist/native-yield-account.d.ts +662 -0
- package/dist/native-yield-account.js +371 -0
- package/dist/native-yield-consent-store.d.ts +20 -0
- package/dist/native-yield-consent-store.js +317 -0
- package/dist/native-yield-definition.d.ts +288 -0
- package/dist/native-yield-definition.js +105 -0
- package/dist/native-yield-reader.d.ts +4 -0
- package/dist/native-yield-reader.js +137 -0
- package/dist/native-yield-settlement.d.ts +48 -0
- package/dist/native-yield-settlement.js +167 -0
- package/dist/native-yield-simulation.d.ts +18 -0
- package/dist/native-yield-simulation.js +45 -0
- package/dist/native-yield-userop-account.d.ts +675 -0
- package/dist/native-yield-userop-account.js +357 -0
- package/dist/native-yield-userop-authority.d.ts +214 -0
- package/dist/native-yield-userop-authority.js +296 -0
- package/dist/native-yield-userop-definition.d.ts +331 -0
- package/dist/native-yield-userop-definition.js +105 -0
- package/dist/native-yield-userop-hosted.d.ts +3 -0
- package/dist/native-yield-userop-hosted.js +71 -0
- package/dist/native-yield-userop-index.d.ts +1522 -0
- package/dist/native-yield-userop-index.js +142 -0
- package/dist/native-yield-userop-mcp-definition.d.ts +1356 -0
- package/dist/native-yield-userop-mcp-definition.js +50 -0
- package/dist/native-yield-userop-operation.d.ts +845 -0
- package/dist/native-yield-userop-operation.js +127 -0
- package/dist/native-yield-userop-policy.d.ts +23 -0
- package/dist/native-yield-userop-policy.js +25 -0
- package/dist/native-yield-userop-registration.d.ts +17 -0
- package/dist/native-yield-userop-registration.js +76 -0
- package/dist/native-yield-userop-settlement.d.ts +8 -0
- package/dist/native-yield-userop-settlement.js +79 -0
- package/dist/native-yield-userop-simulation.d.ts +18 -0
- package/dist/native-yield-userop-simulation.js +34 -0
- package/dist/native-yield-userop.d.ts +35 -0
- package/dist/native-yield-userop.js +156 -0
- package/dist/native-yield-verify.d.ts +30 -0
- package/dist/native-yield-verify.js +141 -0
- package/dist/native-yield.d.ts +17 -0
- package/dist/native-yield.js +119 -0
- package/dist/stdio-main.js +0 -0
- package/dist/stock-buy-definition.d.ts +338 -0
- package/dist/stock-buy-definition.js +127 -0
- package/dist/stock-buy-oracle.d.ts +8 -0
- package/dist/stock-buy-oracle.js +22 -0
- package/dist/stock-buy.d.ts +17 -0
- package/dist/stock-buy.js +164 -0
- package/dist/tool-definitions.d.ts +3 -1
- package/dist/tool-definitions.js +55 -2
- package/dist/vendor/lp-v3/lp-v3-atomic-contract.d.ts +445 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-contract.js +90 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-evidence.d.ts +14 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-evidence.js +82 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-index.d.ts +80 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-index.js +19 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-plan.d.ts +43 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-plan.js +112 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.d.ts +312 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.js +41 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-index.d.ts +3 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-index.js +3 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.d.ts +49 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.js +88 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read.d.ts +17 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read.js +232 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-settlement.d.ts +105 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-settlement.js +162 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.d.ts +606 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.js +59 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.d.ts +168 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.js +23 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.d.ts +32 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.js +228 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.d.ts +11 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.js +91 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.d.ts +9 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.js +124 -0
- package/dist/vendor/lp-v3/lp-v3-contract.d.ts +538 -0
- package/dist/vendor/lp-v3/lp-v3-contract.js +137 -0
- package/dist/vendor/lp-v3/lp-v3-evidence.d.ts +25 -0
- package/dist/vendor/lp-v3/lp-v3-evidence.js +168 -0
- package/dist/vendor/lp-v3/lp-v3-plan.d.ts +22 -0
- package/dist/vendor/lp-v3/lp-v3-plan.js +135 -0
- package/dist/vendor/lp-v3/types.d.ts +131 -0
- package/dist/vendor/lp-v3/types.js +6 -0
- package/dist/vendor/lp-v3/v4-math.d.ts +18 -0
- package/dist/vendor/lp-v3/v4-math.js +90 -0
- package/dist/yield-deposit-definition.d.ts +374 -0
- package/dist/yield-deposit-definition.js +123 -0
- package/dist/yield-deposit.d.ts +16 -0
- package/dist/yield-deposit.js +149 -0
- package/dist/yield-route-semantics.d.ts +27 -0
- package/dist/yield-route-semantics.js +71 -0
- package/dist/yield-withdraw-definition.d.ts +357 -0
- package/dist/yield-withdraw-definition.js +90 -0
- package/dist/yield-withdraw.d.ts +11 -0
- package/dist/yield-withdraw.js +122 -0
- package/package.json +239 -7
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/** Read-only user-paid gas estimates. Neither the estimate nor its buffer is a fee cap or authority to send. */
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { performance } from 'node:perf_hooks';
|
|
4
|
+
import { encodeAbiParameters, encodeFunctionData, getAddress, parseAbi, toHex } from 'viem';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { NATIVE_YIELD_USEROP_BATCH_ABI, NATIVE_YIELD_USEROP_ENTRY_POINT } from './native-yield-userop-definition.js';
|
|
7
|
+
const UINT256_MAX = (1n << 256n) - 1n;
|
|
8
|
+
const UINT120_MAX = (1n << 120n) - 1n;
|
|
9
|
+
const TTL_MS = 30_000;
|
|
10
|
+
const RESERVE_BPS = 2500n;
|
|
11
|
+
const MAX_RESPONSE_BYTES = 65_536;
|
|
12
|
+
const amount = z.string().max(78).regex(/^(0|[1-9][0-9]*)$/)
|
|
13
|
+
.refine(value => value.length <= 78 && /^(0|[1-9][0-9]*)$/.test(value) && BigInt(value) <= UINT256_MAX);
|
|
14
|
+
const positive = amount.refine(value => value.length <= 78 && /^(0|[1-9][0-9]*)$/.test(value) && BigInt(value) > 0n);
|
|
15
|
+
const time = z.number().int().positive().safe();
|
|
16
|
+
const address = z.string().regex(/^0x[0-9a-fA-F]{40}$/).transform((value, context) => {
|
|
17
|
+
try {
|
|
18
|
+
if (/^0x0{40}$/i.test(value))
|
|
19
|
+
throw new Error();
|
|
20
|
+
return getAddress(value).toLowerCase();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
context.addIssue({ code: 'custom', message: 'Invalid gas quote address' });
|
|
24
|
+
return z.NEVER;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const chainId = z.union([z.literal(1), z.literal(8453), z.literal(42161), z.literal(10), z.literal(137)]);
|
|
28
|
+
// Public output schemas must be representable as JSON Schema. Normalization is
|
|
29
|
+
// confined to input bindings; prepared quote addresses are already lowercase.
|
|
30
|
+
const quoteAddress = z.string().regex(/^0x[0-9a-f]{40}$/).refine(value => !/^0x0{40}$/.test(value));
|
|
31
|
+
const callsSchema = z.array(z.object({
|
|
32
|
+
to: address, data: z.string().max(262_146).regex(/^0x(?:[0-9a-fA-F]{2})*$/).transform(value => value.toLowerCase()),
|
|
33
|
+
value: amount,
|
|
34
|
+
}).strict()).min(1).max(8);
|
|
35
|
+
const bindingSchema = z.object({ account: address, chainId, entryPoint: address, calls: callsSchema }).strict();
|
|
36
|
+
const priceSchema = z.object({
|
|
37
|
+
amountAtomic: positive, decimals: z.number().int().min(0).max(77), observedAtMs: time, expiresAtMs: time,
|
|
38
|
+
}).strict().refine(value => value.expiresAtMs > value.observedAtMs);
|
|
39
|
+
const sponsorSchema = z.object({
|
|
40
|
+
paymaster: address,
|
|
41
|
+
sponsorshipPolicyId: z.string().regex(/^[a-zA-Z0-9_-]{1,128}$/),
|
|
42
|
+
}).strict();
|
|
43
|
+
const sponsoredEstimateSchema = z.object({
|
|
44
|
+
kind: z.literal('estimate'), simulation: z.literal('paymaster-stub'), entryPointVersion: z.literal('0.6'),
|
|
45
|
+
paymaster: quoteAddress, sponsorshipPolicyId: z.string().regex(/^[a-zA-Z0-9_-]{1,128}$/),
|
|
46
|
+
paymasterAndDataCommitment: z.string().regex(/^[0-9a-f]{64}$/),
|
|
47
|
+
providerEstimate: z.object({
|
|
48
|
+
blockAnchor: z.literal('unanchored'), startedAtMs: time, completedAtMs: time,
|
|
49
|
+
elapsedMs: z.number().finite().nonnegative().max(TTL_MS),
|
|
50
|
+
}).strict(),
|
|
51
|
+
gas: z.object({
|
|
52
|
+
callGasLimit: positive, verificationGasLimit: positive, preVerificationGas: positive,
|
|
53
|
+
maxFeePerGasWei: positive, maxPriorityFeePerGasWei: amount,
|
|
54
|
+
verificationGasMultiplier: z.literal('3'), estimatedPrefundWei: positive,
|
|
55
|
+
reserveWei: positive, estimatedRequiredWei: positive, reserveBps: z.literal('2500'),
|
|
56
|
+
}).strict(),
|
|
57
|
+
estimatedGasCostUsdMicros: amount.optional(),
|
|
58
|
+
}).strict();
|
|
59
|
+
function ceil(numerator, denominator) {
|
|
60
|
+
return numerator / denominator + (numerator % denominator === 0n ? 0n : 1n);
|
|
61
|
+
}
|
|
62
|
+
function max(a, b) { return a > b ? a : b; }
|
|
63
|
+
function min(a, b) { return a < b ? a : b; }
|
|
64
|
+
/** The block anchor covers balance, deposit and nonce only. The bundler estimate has no block parameter. */
|
|
65
|
+
export const ACCOUNT_GAS_QUOTE_SCHEMA = z.object({
|
|
66
|
+
version: z.literal(1), kind: z.literal('estimate'), payer: z.literal('user'),
|
|
67
|
+
account: quoteAddress, chainId, entryPoint: quoteAddress,
|
|
68
|
+
nativeAsset: z.object({ symbol: z.enum(['ETH', 'POL']), decimals: z.literal(18) }).strict(),
|
|
69
|
+
callsCommitment: z.string().regex(/^[0-9a-f]{64}$/), nonce: amount,
|
|
70
|
+
blockNumber: amount, blockHash: z.string().regex(/^0x[0-9a-f]{64}$/), blockTimestampMs: time,
|
|
71
|
+
observedAtMs: time, expiresAtMs: time,
|
|
72
|
+
simulation: z.literal('sender-balance-override'),
|
|
73
|
+
providerEstimate: z.object({
|
|
74
|
+
blockAnchor: z.literal('unanchored'), startedAtMs: time, completedAtMs: time,
|
|
75
|
+
elapsedMs: z.number().finite().nonnegative().max(TTL_MS),
|
|
76
|
+
}).strict(),
|
|
77
|
+
gas: z.object({
|
|
78
|
+
callGasLimit: positive, verificationGasLimit: positive, preVerificationGas: positive,
|
|
79
|
+
maxFeePerGasWei: positive, maxPriorityFeePerGasWei: amount,
|
|
80
|
+
estimatedPrefundWei: positive, reserveWei: positive,
|
|
81
|
+
/** Estimate plus buffer, not a maximum or an enforceable fee bound. */
|
|
82
|
+
estimatedRequiredWei: positive, reserveBps: z.literal('2500'),
|
|
83
|
+
}).strict(),
|
|
84
|
+
balance: z.object({
|
|
85
|
+
nativeWei: amount, entryPointDepositWei: amount, callValueWei: amount,
|
|
86
|
+
depositAppliedToGasWei: amount, walletGasRequirementWei: amount, nativeAvailableForGasWei: amount,
|
|
87
|
+
gasShortfallWei: amount, requiredNativeWei: amount,
|
|
88
|
+
/** Combined native funding shortfall, including call value principal. */
|
|
89
|
+
shortfallWei: amount,
|
|
90
|
+
}).strict(),
|
|
91
|
+
usdPrice: priceSchema.optional(), estimatedGasCostUsdMicros: amount.optional(),
|
|
92
|
+
/** Separate advisory sponsored simulation; never inferred from the self-pay estimate. */
|
|
93
|
+
sponsoredEstimate: sponsoredEstimateSchema.optional(),
|
|
94
|
+
}).strict().superRefine((quote, context) => {
|
|
95
|
+
const fail = () => context.addIssue({ code: 'custom', message: 'Gas quote arithmetic or observation binding is inconsistent' });
|
|
96
|
+
try {
|
|
97
|
+
const gas = quote.gas, balance = quote.balance;
|
|
98
|
+
const limits = [gas.callGasLimit, gas.verificationGasLimit, gas.preVerificationGas].map(BigInt);
|
|
99
|
+
const fee = BigInt(gas.maxFeePerGasWei), priority = BigInt(gas.maxPriorityFeePerGasWei);
|
|
100
|
+
const prefund = limits.reduce((sum, value) => sum + value, 0n) * fee;
|
|
101
|
+
const reserve = ceil(prefund * RESERVE_BPS, 10000n), buffered = prefund + reserve;
|
|
102
|
+
const deposit = min(BigInt(balance.entryPointDepositWei), buffered), walletGas = buffered - deposit;
|
|
103
|
+
const available = max(BigInt(balance.nativeWei) - BigInt(balance.callValueWei), 0n);
|
|
104
|
+
const required = BigInt(balance.callValueWei) + walletGas;
|
|
105
|
+
if ([...limits, fee, priority].some(value => value > UINT120_MAX) || priority > fee ||
|
|
106
|
+
BigInt(quote.nonce) >= (1n << 64n) || quote.nativeAsset.symbol !== (quote.chainId === 137 ? 'POL' : 'ETH') ||
|
|
107
|
+
prefund !== BigInt(gas.estimatedPrefundWei) || reserve !== BigInt(gas.reserveWei) ||
|
|
108
|
+
buffered !== BigInt(gas.estimatedRequiredWei) || deposit !== BigInt(balance.depositAppliedToGasWei) ||
|
|
109
|
+
walletGas !== BigInt(balance.walletGasRequirementWei) || available !== BigInt(balance.nativeAvailableForGasWei) ||
|
|
110
|
+
max(walletGas - available, 0n) !== BigInt(balance.gasShortfallWei) || required !== BigInt(balance.requiredNativeWei) ||
|
|
111
|
+
max(required - BigInt(balance.nativeWei), 0n) !== BigInt(balance.shortfallWei) ||
|
|
112
|
+
quote.blockTimestampMs > quote.observedAtMs || quote.expiresAtMs <= quote.observedAtMs ||
|
|
113
|
+
quote.expiresAtMs > Math.min(quote.observedAtMs + TTL_MS, quote.blockTimestampMs + TTL_MS) ||
|
|
114
|
+
quote.providerEstimate.startedAtMs < quote.observedAtMs ||
|
|
115
|
+
quote.providerEstimate.completedAtMs < quote.providerEstimate.startedAtMs ||
|
|
116
|
+
quote.providerEstimate.completedAtMs >= quote.expiresAtMs ||
|
|
117
|
+
quote.providerEstimate.startedAtMs + quote.providerEstimate.elapsedMs >= quote.expiresAtMs)
|
|
118
|
+
fail();
|
|
119
|
+
if (quote.usdPrice) {
|
|
120
|
+
const price = quote.usdPrice;
|
|
121
|
+
if (price.observedAtMs > quote.observedAtMs || quote.expiresAtMs > price.expiresAtMs ||
|
|
122
|
+
quote.estimatedGasCostUsdMicros !== ceil(buffered * BigInt(price.amountAtomic) * 1000000n, 10n ** BigInt(18 + price.decimals)).toString())
|
|
123
|
+
fail();
|
|
124
|
+
}
|
|
125
|
+
else if (quote.estimatedGasCostUsdMicros !== undefined)
|
|
126
|
+
fail();
|
|
127
|
+
if (quote.sponsoredEstimate) {
|
|
128
|
+
const sponsored = quote.sponsoredEstimate, sponsoredGas = sponsored.gas, observation = sponsored.providerEstimate;
|
|
129
|
+
const call = BigInt(sponsoredGas.callGasLimit), verification = BigInt(sponsoredGas.verificationGasLimit), pre = BigInt(sponsoredGas.preVerificationGas);
|
|
130
|
+
const sponsoredPrefund = (call + 3n * verification + pre) * fee;
|
|
131
|
+
const sponsoredReserve = ceil(sponsoredPrefund * RESERVE_BPS, 10000n), sponsoredBuffered = sponsoredPrefund + sponsoredReserve;
|
|
132
|
+
if (quote.entryPoint !== NATIVE_YIELD_USEROP_ENTRY_POINT.toLowerCase() ||
|
|
133
|
+
[call, verification, pre].some(value => value > UINT120_MAX) ||
|
|
134
|
+
sponsoredGas.maxFeePerGasWei !== gas.maxFeePerGasWei || sponsoredGas.maxPriorityFeePerGasWei !== gas.maxPriorityFeePerGasWei ||
|
|
135
|
+
sponsoredGas.estimatedPrefundWei !== sponsoredPrefund.toString() || sponsoredGas.reserveWei !== sponsoredReserve.toString() ||
|
|
136
|
+
sponsoredGas.estimatedRequiredWei !== sponsoredBuffered.toString() || observation.startedAtMs < quote.providerEstimate.completedAtMs ||
|
|
137
|
+
observation.completedAtMs < observation.startedAtMs || observation.completedAtMs >= quote.expiresAtMs ||
|
|
138
|
+
observation.startedAtMs + observation.elapsedMs >= quote.expiresAtMs)
|
|
139
|
+
fail();
|
|
140
|
+
if (quote.usdPrice) {
|
|
141
|
+
if (sponsored.estimatedGasCostUsdMicros !== ceil(sponsoredBuffered * BigInt(quote.usdPrice.amountAtomic) * 1000000n, 10n ** BigInt(18 + quote.usdPrice.decimals)).toString())
|
|
142
|
+
fail();
|
|
143
|
+
}
|
|
144
|
+
else if (sponsored.estimatedGasCostUsdMicros !== undefined)
|
|
145
|
+
fail();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
fail();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
export class AccountGasQuoteError extends Error {
|
|
153
|
+
code = 'ACCOUNT_GAS_QUOTE_UNAVAILABLE';
|
|
154
|
+
constructor(message = 'A fresh gas estimate is unavailable; refresh the review before continuing') {
|
|
155
|
+
super(message);
|
|
156
|
+
this.name = 'AccountGasQuoteError';
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function unavailable() { throw new AccountGasQuoteError(); }
|
|
160
|
+
class AccountGasQuoteClockError extends AccountGasQuoteError {
|
|
161
|
+
}
|
|
162
|
+
function invalidClock() { throw new AccountGasQuoteClockError(); }
|
|
163
|
+
function parse(schema, value) {
|
|
164
|
+
const result = schema.safeParse(value);
|
|
165
|
+
if (!result.success)
|
|
166
|
+
unavailable();
|
|
167
|
+
return result.data;
|
|
168
|
+
}
|
|
169
|
+
function uint(value, bound = UINT256_MAX) {
|
|
170
|
+
if (typeof value !== 'bigint' || value < 0n || value > bound)
|
|
171
|
+
unavailable();
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
function clockWall(value) {
|
|
175
|
+
if (!Number.isSafeInteger(value) || value <= 0)
|
|
176
|
+
invalidClock();
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
function clockMono(value) {
|
|
180
|
+
if (!Number.isFinite(value) || value < 0)
|
|
181
|
+
invalidClock();
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
export function accountGasCallsCommitment(calls) {
|
|
185
|
+
const canonical = parse(callsSchema, calls);
|
|
186
|
+
return createHash('sha256').update(JSON.stringify({ domain: 'otto-account-gas-calls-v1', calls: canonical })).digest('hex');
|
|
187
|
+
}
|
|
188
|
+
const ENTRY_POINT_ABI = parseAbi([
|
|
189
|
+
'function balanceOf(address account) view returns(uint256)',
|
|
190
|
+
'function getNonce(address sender,uint192 key) view returns(uint256)',
|
|
191
|
+
]);
|
|
192
|
+
// The sole EOA-owner wrapper has fixed ABI overhead. All 65 signature bytes are
|
|
193
|
+
// nonzero, conservatively covering signature calldata cost without signing anything.
|
|
194
|
+
const DUMMY_SIGNATURE = encodeAbiParameters([
|
|
195
|
+
{ type: 'tuple', components: [{ name: 'ownerIndex', type: 'uint8' }, { name: 'signatureData', type: 'bytes' }] },
|
|
196
|
+
], [{ ownerIndex: 0, signatureData: `0x${'11'.repeat(32)}${'22'.repeat(32)}1b` }]);
|
|
197
|
+
function providerUrl(value, expectedChainId) {
|
|
198
|
+
try {
|
|
199
|
+
const url = new URL(value);
|
|
200
|
+
const keys = [...url.searchParams.keys()];
|
|
201
|
+
if (url.protocol !== 'https:' || url.hostname !== 'api.pimlico.io' || url.port || url.username || url.password || url.hash ||
|
|
202
|
+
url.pathname !== `/v2/${expectedChainId}/rpc` || keys.length !== 1 || keys[0] !== 'apikey' || !url.searchParams.get('apikey'))
|
|
203
|
+
unavailable();
|
|
204
|
+
return url.href;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
unavailable();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function readProviderResponse(response) {
|
|
211
|
+
if (!response.ok || !response.body)
|
|
212
|
+
unavailable();
|
|
213
|
+
const reader = response.body.getReader();
|
|
214
|
+
const chunks = [];
|
|
215
|
+
let size = 0;
|
|
216
|
+
try {
|
|
217
|
+
for (;;) {
|
|
218
|
+
const chunk = await reader.read();
|
|
219
|
+
if (chunk.done)
|
|
220
|
+
break;
|
|
221
|
+
size += chunk.value.byteLength;
|
|
222
|
+
if (size > MAX_RESPONSE_BYTES) {
|
|
223
|
+
await reader.cancel();
|
|
224
|
+
unavailable();
|
|
225
|
+
}
|
|
226
|
+
chunks.push(chunk.value);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
reader.releaseLock();
|
|
231
|
+
}
|
|
232
|
+
const bytes = new Uint8Array(size);
|
|
233
|
+
let offset = 0;
|
|
234
|
+
for (const chunk of chunks) {
|
|
235
|
+
bytes.set(chunk, offset);
|
|
236
|
+
offset += chunk.byteLength;
|
|
237
|
+
}
|
|
238
|
+
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
239
|
+
}
|
|
240
|
+
const quantity = z.string().max(32).regex(/^0x[1-9a-fA-F][0-9a-fA-F]*$/)
|
|
241
|
+
.refine(value => /^0x[1-9a-fA-F][0-9a-fA-F]*$/.test(value) && BigInt(value) <= UINT120_MAX);
|
|
242
|
+
const responseSchema = z.object({ jsonrpc: z.literal('2.0'), id: z.literal(1), result: z.object({
|
|
243
|
+
callGasLimit: quantity, verificationGasLimit: quantity, preVerificationGas: quantity, verificationGas: quantity.optional(),
|
|
244
|
+
}).strict().refine(value => value.verificationGas === undefined || BigInt(value.verificationGas) === BigInt(value.verificationGasLimit)),
|
|
245
|
+
}).strict();
|
|
246
|
+
const stubResponseSchema = z.object({ jsonrpc: z.literal('2.0'), id: z.literal(1), result: z.object({
|
|
247
|
+
paymasterAndData: z.string().min(42).max(8_194).regex(/^0x(?:[0-9a-fA-F]{2})+$/),
|
|
248
|
+
// ERC-7677 metadata is bounded and ignored. It never turns a stub into send authority.
|
|
249
|
+
isFinal: z.boolean().optional(),
|
|
250
|
+
sponsor: z.object({ name: z.string().max(128), icon: z.string().max(8_192).optional() }).strict().optional(),
|
|
251
|
+
}).strict() }).strict();
|
|
252
|
+
const clockProofs = new WeakMap();
|
|
253
|
+
function freeze(value) {
|
|
254
|
+
if (typeof value === 'object' && value !== null) {
|
|
255
|
+
Object.values(value).forEach(freeze);
|
|
256
|
+
Object.freeze(value);
|
|
257
|
+
}
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
/** No signer, paymaster sponsorship, submission, account authority or account code reads. */
|
|
261
|
+
export async function prepareAccountGasQuote(input) {
|
|
262
|
+
try {
|
|
263
|
+
// Snapshot all caller-controlled bindings before the first await.
|
|
264
|
+
const binding = parse(bindingSchema, { account: input.account, chainId: input.chainId, entryPoint: input.entryPoint, calls: input.calls });
|
|
265
|
+
const price = input.usdPrice === undefined ? undefined : parse(priceSchema, input.usdPrice);
|
|
266
|
+
const sponsorResult = sponsorSchema.safeParse(input.sponsor);
|
|
267
|
+
const sponsor = sponsorResult.success ? sponsorResult.data : undefined;
|
|
268
|
+
const endpoint = providerUrl(input.paymasterUrl, binding.chainId);
|
|
269
|
+
const client = input.client, fetcher = input.fetch ?? globalThis.fetch;
|
|
270
|
+
const wall = input.nowMs ?? Date.now, mono = input.monotonicNowMs ?? (() => performance.now());
|
|
271
|
+
const startMono = clockMono(mono()), observedAtMs = clockWall(wall());
|
|
272
|
+
const [observedChain, block] = await Promise.all([client.getChainId(), client.getBlock({ blockTag: 'latest' })]);
|
|
273
|
+
if (observedChain !== binding.chainId || block.number === null || block.hash === null || !/^0x[0-9a-fA-F]{64}$/.test(block.hash))
|
|
274
|
+
unavailable();
|
|
275
|
+
const blockNumber = uint(block.number), timestamp = uint(block.timestamp);
|
|
276
|
+
if (timestamp > BigInt(Math.floor(Number.MAX_SAFE_INTEGER / 1_000)))
|
|
277
|
+
unavailable();
|
|
278
|
+
const blockTimestampMs = Number(timestamp) * 1_000;
|
|
279
|
+
if (blockTimestampMs > observedAtMs || blockTimestampMs + TTL_MS <= observedAtMs)
|
|
280
|
+
unavailable();
|
|
281
|
+
if (price && (price.observedAtMs > observedAtMs || price.expiresAtMs <= observedAtMs))
|
|
282
|
+
unavailable();
|
|
283
|
+
const expiresAtMs = Math.min(observedAtMs + TTL_MS, blockTimestampMs + TTL_MS, price?.expiresAtMs ?? Number.MAX_SAFE_INTEGER);
|
|
284
|
+
const [nativeBalance, depositBalance, operationNonce, fees] = await Promise.all([
|
|
285
|
+
client.getBalance({ address: binding.account, blockNumber }),
|
|
286
|
+
client.readContract({ address: binding.entryPoint, abi: ENTRY_POINT_ABI, functionName: 'balanceOf', args: [binding.account], blockNumber }),
|
|
287
|
+
client.readContract({ address: binding.entryPoint, abi: ENTRY_POINT_ABI, functionName: 'getNonce', args: [binding.account, 0n], blockNumber }),
|
|
288
|
+
client.estimateFeesPerGas({ chain: client.chain, type: 'eip1559' }),
|
|
289
|
+
]);
|
|
290
|
+
const native = uint(nativeBalance), deposit = uint(depositBalance), nonce = uint(operationNonce, (1n << 64n) - 1n);
|
|
291
|
+
const maxFee = uint(fees.maxFeePerGas, UINT120_MAX), priorityFee = uint(fees.maxPriorityFeePerGas, UINT120_MAX);
|
|
292
|
+
if (maxFee === 0n || priorityFee > maxFee)
|
|
293
|
+
unavailable();
|
|
294
|
+
const callValue = binding.calls.reduce((sum, call) => sum + BigInt(call.value), 0n);
|
|
295
|
+
if (callValue > UINT256_MAX)
|
|
296
|
+
unavailable();
|
|
297
|
+
const userOperation = {
|
|
298
|
+
sender: binding.account, nonce: toHex(nonce), initCode: '0x',
|
|
299
|
+
callData: encodeFunctionData({ abi: NATIVE_YIELD_USEROP_BATCH_ABI, functionName: 'executeBatch',
|
|
300
|
+
args: [binding.calls.map(call => ({ target: call.to, value: BigInt(call.value), data: call.data }))] }),
|
|
301
|
+
callGasLimit: '0x0', verificationGasLimit: '0x0', preVerificationGas: '0x0',
|
|
302
|
+
maxFeePerGas: toHex(maxFee), maxPriorityFeePerGas: toHex(priorityFee), paymasterAndData: '0x', signature: DUMMY_SIGNATURE,
|
|
303
|
+
};
|
|
304
|
+
const providerStartMono = clockMono(mono()), providerStartWall = clockWall(wall());
|
|
305
|
+
if (providerStartMono < startMono || providerStartWall < observedAtMs)
|
|
306
|
+
unavailable();
|
|
307
|
+
const providerResponse = await fetcher(endpoint, {
|
|
308
|
+
method: 'POST', headers: { 'content-type': 'application/json' }, redirect: 'error', signal: AbortSignal.timeout(10_000),
|
|
309
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_estimateUserOperationGas',
|
|
310
|
+
params: [userOperation, binding.entryPoint, { [binding.account]: { balance: toHex(UINT256_MAX) } }] }),
|
|
311
|
+
});
|
|
312
|
+
const estimate = parse(responseSchema, await readProviderResponse(providerResponse)).result;
|
|
313
|
+
const providerEndWall = clockWall(wall()), providerEndMono = clockMono(mono());
|
|
314
|
+
if (providerEndWall < providerStartWall || providerEndMono < providerStartMono)
|
|
315
|
+
unavailable();
|
|
316
|
+
let latestObservedWall = providerEndWall, latestObservedMono = providerEndMono;
|
|
317
|
+
let sponsoredEstimate;
|
|
318
|
+
if (sponsor && binding.entryPoint === NATIVE_YIELD_USEROP_ENTRY_POINT.toLowerCase()) {
|
|
319
|
+
try {
|
|
320
|
+
const sponsorStartMono = clockMono(mono()), sponsorStartWall = clockWall(wall());
|
|
321
|
+
if (sponsorStartMono < providerEndMono || sponsorStartWall < providerEndWall)
|
|
322
|
+
invalidClock();
|
|
323
|
+
latestObservedWall = sponsorStartWall;
|
|
324
|
+
latestObservedMono = sponsorStartMono;
|
|
325
|
+
// ERC-7677 stub data is for unsigned gas estimation. Never request final
|
|
326
|
+
// paymaster data or sponsorship signatures, and never expose this stub.
|
|
327
|
+
const { signature: _signature, paymasterAndData: _paymasterAndData, ...unsignedOperation } = userOperation;
|
|
328
|
+
const stubResponse = await fetcher(endpoint, {
|
|
329
|
+
method: 'POST', headers: { 'content-type': 'application/json' }, redirect: 'error', signal: AbortSignal.timeout(10_000),
|
|
330
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'pm_getPaymasterStubData',
|
|
331
|
+
params: [unsignedOperation, binding.entryPoint, toHex(binding.chainId), { sponsorshipPolicyId: sponsor.sponsorshipPolicyId }] }),
|
|
332
|
+
});
|
|
333
|
+
const stub = parse(stubResponseSchema, await readProviderResponse(stubResponse)).result.paymasterAndData.toLowerCase();
|
|
334
|
+
if (stub.slice(0, 42) !== sponsor.paymaster)
|
|
335
|
+
unavailable();
|
|
336
|
+
const stubEndWall = clockWall(wall()), stubEndMono = clockMono(mono());
|
|
337
|
+
if (stubEndWall < sponsorStartWall || stubEndMono < sponsorStartMono)
|
|
338
|
+
invalidClock();
|
|
339
|
+
latestObservedWall = stubEndWall;
|
|
340
|
+
latestObservedMono = stubEndMono;
|
|
341
|
+
if (Math.max(stubEndWall, observedAtMs + stubEndMono - startMono) >= expiresAtMs)
|
|
342
|
+
unavailable();
|
|
343
|
+
const sponsoredResponse = await fetcher(endpoint, {
|
|
344
|
+
method: 'POST', headers: { 'content-type': 'application/json' }, redirect: 'error', signal: AbortSignal.timeout(10_000),
|
|
345
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_estimateUserOperationGas',
|
|
346
|
+
params: [{ ...userOperation, paymasterAndData: stub }, binding.entryPoint, { [binding.account]: { balance: toHex(UINT256_MAX) } }] }),
|
|
347
|
+
});
|
|
348
|
+
const sponsoredGas = parse(responseSchema, await readProviderResponse(sponsoredResponse)).result;
|
|
349
|
+
const sponsorEndWall = clockWall(wall()), sponsorEndMono = clockMono(mono());
|
|
350
|
+
if (sponsorEndWall < stubEndWall || sponsorEndMono < stubEndMono)
|
|
351
|
+
invalidClock();
|
|
352
|
+
latestObservedWall = sponsorEndWall;
|
|
353
|
+
latestObservedMono = sponsorEndMono;
|
|
354
|
+
const call = BigInt(sponsoredGas.callGasLimit), verification = BigInt(sponsoredGas.verificationGasLimit), pre = BigInt(sponsoredGas.preVerificationGas);
|
|
355
|
+
const sponsoredPrefund = (call + 3n * verification + pre) * maxFee;
|
|
356
|
+
const sponsoredReserve = ceil(sponsoredPrefund * RESERVE_BPS, 10000n), sponsoredBuffered = sponsoredPrefund + sponsoredReserve;
|
|
357
|
+
sponsoredEstimate = {
|
|
358
|
+
kind: 'estimate', simulation: 'paymaster-stub', entryPointVersion: '0.6',
|
|
359
|
+
paymaster: sponsor.paymaster, sponsorshipPolicyId: sponsor.sponsorshipPolicyId,
|
|
360
|
+
paymasterAndDataCommitment: createHash('sha256').update('otto-account-gas-paymaster-stub-v1:' + stub).digest('hex'),
|
|
361
|
+
providerEstimate: { blockAnchor: 'unanchored', startedAtMs: sponsorStartWall, completedAtMs: sponsorEndWall,
|
|
362
|
+
elapsedMs: sponsorEndMono - sponsorStartMono },
|
|
363
|
+
gas: { callGasLimit: call.toString(), verificationGasLimit: verification.toString(), preVerificationGas: pre.toString(),
|
|
364
|
+
maxFeePerGasWei: maxFee.toString(), maxPriorityFeePerGasWei: priorityFee.toString(), verificationGasMultiplier: '3',
|
|
365
|
+
estimatedPrefundWei: sponsoredPrefund.toString(), reserveWei: sponsoredReserve.toString(), estimatedRequiredWei: sponsoredBuffered.toString(), reserveBps: '2500' },
|
|
366
|
+
...(price ? { estimatedGasCostUsdMicros: ceil(sponsoredBuffered * BigInt(price.amountAtomic) * 1000000n, 10n ** BigInt(18 + price.decimals)).toString() } : {}),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
if (error instanceof AccountGasQuoteClockError)
|
|
371
|
+
throw error;
|
|
372
|
+
// Sponsor availability cannot remove a still-fresh account-paid estimate.
|
|
373
|
+
// The final shared clock/anchor checks below still refuse global staleness.
|
|
374
|
+
sponsoredEstimate = undefined;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const [anchor, head, finalChain] = await Promise.all([
|
|
378
|
+
client.getBlock({ blockNumber }), client.getBlock({ blockTag: 'latest' }), client.getChainId(),
|
|
379
|
+
]);
|
|
380
|
+
const actualWallAfter = clockWall(wall()), endMono = clockMono(mono());
|
|
381
|
+
if (actualWallAfter < latestObservedWall || endMono < latestObservedMono)
|
|
382
|
+
unavailable();
|
|
383
|
+
const effectiveAfter = Math.max(actualWallAfter, observedAtMs + endMono - startMono);
|
|
384
|
+
if (effectiveAfter >= expiresAtMs || finalChain !== binding.chainId || anchor.number !== blockNumber ||
|
|
385
|
+
anchor.hash?.toLowerCase() !== block.hash.toLowerCase() || anchor.timestamp !== timestamp ||
|
|
386
|
+
head.number === null || head.hash === null || !/^0x[0-9a-fA-F]{64}$/.test(head.hash) ||
|
|
387
|
+
head.number < blockNumber || head.timestamp < timestamp || head.timestamp * 1000n > BigInt(Math.floor(effectiveAfter)) ||
|
|
388
|
+
BigInt(Math.floor(effectiveAfter)) - head.timestamp * 1000n >= BigInt(TTL_MS) ||
|
|
389
|
+
(head.number === blockNumber && head.hash.toLowerCase() !== block.hash.toLowerCase()))
|
|
390
|
+
unavailable();
|
|
391
|
+
const callGas = BigInt(estimate.callGasLimit), verificationGas = BigInt(estimate.verificationGasLimit), preVerificationGas = BigInt(estimate.preVerificationGas);
|
|
392
|
+
const prefund = (callGas + verificationGas + preVerificationGas) * maxFee;
|
|
393
|
+
const reserve = ceil(prefund * RESERVE_BPS, 10000n), buffered = prefund + reserve;
|
|
394
|
+
const appliedDeposit = min(deposit, buffered), walletGas = buffered - appliedDeposit;
|
|
395
|
+
const availableForGas = max(native - callValue, 0n), requiredNative = callValue + walletGas;
|
|
396
|
+
const quote = parse(ACCOUNT_GAS_QUOTE_SCHEMA, {
|
|
397
|
+
version: 1, kind: 'estimate', payer: 'user', account: binding.account, chainId: binding.chainId, entryPoint: binding.entryPoint,
|
|
398
|
+
nativeAsset: { symbol: binding.chainId === 137 ? 'POL' : 'ETH', decimals: 18 },
|
|
399
|
+
callsCommitment: accountGasCallsCommitment(binding.calls), nonce: nonce.toString(),
|
|
400
|
+
blockNumber: blockNumber.toString(), blockHash: block.hash.toLowerCase(), blockTimestampMs, observedAtMs, expiresAtMs,
|
|
401
|
+
simulation: 'sender-balance-override',
|
|
402
|
+
providerEstimate: { blockAnchor: 'unanchored', startedAtMs: providerStartWall, completedAtMs: providerEndWall,
|
|
403
|
+
elapsedMs: providerEndMono - providerStartMono },
|
|
404
|
+
gas: { callGasLimit: callGas.toString(), verificationGasLimit: verificationGas.toString(), preVerificationGas: preVerificationGas.toString(),
|
|
405
|
+
maxFeePerGasWei: maxFee.toString(), maxPriorityFeePerGasWei: priorityFee.toString(),
|
|
406
|
+
estimatedPrefundWei: prefund.toString(), reserveWei: reserve.toString(), estimatedRequiredWei: buffered.toString(), reserveBps: '2500' },
|
|
407
|
+
balance: { nativeWei: native.toString(), entryPointDepositWei: deposit.toString(), callValueWei: callValue.toString(),
|
|
408
|
+
depositAppliedToGasWei: appliedDeposit.toString(), walletGasRequirementWei: walletGas.toString(), nativeAvailableForGasWei: availableForGas.toString(),
|
|
409
|
+
gasShortfallWei: max(walletGas - availableForGas, 0n).toString(), requiredNativeWei: requiredNative.toString(), shortfallWei: max(requiredNative - native, 0n).toString() },
|
|
410
|
+
...(price ? { usdPrice: price, estimatedGasCostUsdMicros: ceil(buffered * BigInt(price.amountAtomic) * 1000000n, 10n ** BigInt(18 + price.decimals)).toString() } : {}),
|
|
411
|
+
...(sponsoredEstimate ? { sponsoredEstimate } : {}),
|
|
412
|
+
});
|
|
413
|
+
freeze(quote);
|
|
414
|
+
clockProofs.set(quote, { endMono, effectiveAfter, actualWallAfter, monotonicNow: mono });
|
|
415
|
+
return quote;
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
if (error instanceof AccountGasQuoteError)
|
|
419
|
+
throw error;
|
|
420
|
+
// Provider/config errors may include the API key, URL or request. Never propagate them.
|
|
421
|
+
unavailable();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/** Call synchronously at request start. Persisted copies retain wall-clock expiry but no process-local clock proof. */
|
|
425
|
+
export function assertFreshAccountGasQuote(value, input, nowMs = Date.now()) {
|
|
426
|
+
const quote = parse(ACCOUNT_GAS_QUOTE_SCHEMA, value), binding = parse(bindingSchema, input);
|
|
427
|
+
let current = clockWall(nowMs);
|
|
428
|
+
const proof = clockProofs.get(value);
|
|
429
|
+
if (proof) {
|
|
430
|
+
const monotonicNow = clockMono(proof.monotonicNow());
|
|
431
|
+
if (current < proof.actualWallAfter || monotonicNow < proof.endMono)
|
|
432
|
+
unavailable();
|
|
433
|
+
current = Math.max(current, proof.effectiveAfter + monotonicNow - proof.endMono);
|
|
434
|
+
}
|
|
435
|
+
if (quote.account !== binding.account || quote.chainId !== binding.chainId || quote.entryPoint !== binding.entryPoint ||
|
|
436
|
+
quote.callsCommitment !== accountGasCallsCommitment(binding.calls) ||
|
|
437
|
+
quote.balance.callValueWei !== binding.calls.reduce((sum, call) => sum + BigInt(call.value), 0n).toString() ||
|
|
438
|
+
current < quote.observedAtMs || current < quote.providerEstimate.completedAtMs ||
|
|
439
|
+
current < (quote.sponsoredEstimate?.providerEstimate.completedAtMs ?? quote.providerEstimate.completedAtMs) ||
|
|
440
|
+
current >= quote.expiresAtMs)
|
|
441
|
+
unavailable();
|
|
442
|
+
}
|
|
443
|
+
//# sourceMappingURL=execution-account-gas-quote.js.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/** Review-only payer selection. Quotes are estimates; this module never authorizes a send. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { type AccountGasQuote } from './execution-account-gas-quote.js';
|
|
4
|
+
import { type SponsoredGasPolicyProof } from './execution-sponsored-gas-policy.js';
|
|
5
|
+
export declare const ACCOUNT_GAS_REVIEW_SCHEMA: z.ZodObject<{
|
|
6
|
+
version: z.ZodLiteral<1>;
|
|
7
|
+
revision: z.ZodNumber;
|
|
8
|
+
payer: z.ZodEnum<{
|
|
9
|
+
account: "account";
|
|
10
|
+
sponsor: "sponsor";
|
|
11
|
+
}>;
|
|
12
|
+
sponsorStatus: z.ZodEnum<{
|
|
13
|
+
unavailable: "unavailable";
|
|
14
|
+
eligible: "eligible";
|
|
15
|
+
"above-limit": "above-limit";
|
|
16
|
+
"price-unavailable": "price-unavailable";
|
|
17
|
+
}>;
|
|
18
|
+
subsidyCapUsdMicros: z.ZodLiteral<"100000">;
|
|
19
|
+
quote: z.ZodObject<{
|
|
20
|
+
version: z.ZodLiteral<1>;
|
|
21
|
+
kind: z.ZodLiteral<"estimate">;
|
|
22
|
+
payer: z.ZodLiteral<"user">;
|
|
23
|
+
account: z.ZodString;
|
|
24
|
+
chainId: z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<8453>, z.ZodLiteral<42161>, z.ZodLiteral<10>, z.ZodLiteral<137>]>;
|
|
25
|
+
entryPoint: z.ZodString;
|
|
26
|
+
nativeAsset: z.ZodObject<{
|
|
27
|
+
symbol: z.ZodEnum<{
|
|
28
|
+
ETH: "ETH";
|
|
29
|
+
POL: "POL";
|
|
30
|
+
}>;
|
|
31
|
+
decimals: z.ZodLiteral<18>;
|
|
32
|
+
}, z.core.$strict>;
|
|
33
|
+
callsCommitment: z.ZodString;
|
|
34
|
+
nonce: z.ZodString;
|
|
35
|
+
blockNumber: z.ZodString;
|
|
36
|
+
blockHash: z.ZodString;
|
|
37
|
+
blockTimestampMs: z.ZodNumber;
|
|
38
|
+
observedAtMs: z.ZodNumber;
|
|
39
|
+
expiresAtMs: z.ZodNumber;
|
|
40
|
+
simulation: z.ZodLiteral<"sender-balance-override">;
|
|
41
|
+
providerEstimate: z.ZodObject<{
|
|
42
|
+
blockAnchor: z.ZodLiteral<"unanchored">;
|
|
43
|
+
startedAtMs: z.ZodNumber;
|
|
44
|
+
completedAtMs: z.ZodNumber;
|
|
45
|
+
elapsedMs: z.ZodNumber;
|
|
46
|
+
}, z.core.$strict>;
|
|
47
|
+
gas: z.ZodObject<{
|
|
48
|
+
callGasLimit: z.ZodString;
|
|
49
|
+
verificationGasLimit: z.ZodString;
|
|
50
|
+
preVerificationGas: z.ZodString;
|
|
51
|
+
maxFeePerGasWei: z.ZodString;
|
|
52
|
+
maxPriorityFeePerGasWei: z.ZodString;
|
|
53
|
+
estimatedPrefundWei: z.ZodString;
|
|
54
|
+
reserveWei: z.ZodString;
|
|
55
|
+
estimatedRequiredWei: z.ZodString;
|
|
56
|
+
reserveBps: z.ZodLiteral<"2500">;
|
|
57
|
+
}, z.core.$strict>;
|
|
58
|
+
balance: z.ZodObject<{
|
|
59
|
+
nativeWei: z.ZodString;
|
|
60
|
+
entryPointDepositWei: z.ZodString;
|
|
61
|
+
callValueWei: z.ZodString;
|
|
62
|
+
depositAppliedToGasWei: z.ZodString;
|
|
63
|
+
walletGasRequirementWei: z.ZodString;
|
|
64
|
+
nativeAvailableForGasWei: z.ZodString;
|
|
65
|
+
gasShortfallWei: z.ZodString;
|
|
66
|
+
requiredNativeWei: z.ZodString;
|
|
67
|
+
shortfallWei: z.ZodString;
|
|
68
|
+
}, z.core.$strict>;
|
|
69
|
+
usdPrice: z.ZodOptional<z.ZodObject<{
|
|
70
|
+
amountAtomic: z.ZodString;
|
|
71
|
+
decimals: z.ZodNumber;
|
|
72
|
+
observedAtMs: z.ZodNumber;
|
|
73
|
+
expiresAtMs: z.ZodNumber;
|
|
74
|
+
}, z.core.$strict>>;
|
|
75
|
+
estimatedGasCostUsdMicros: z.ZodOptional<z.ZodString>;
|
|
76
|
+
sponsoredEstimate: z.ZodOptional<z.ZodObject<{
|
|
77
|
+
kind: z.ZodLiteral<"estimate">;
|
|
78
|
+
simulation: z.ZodLiteral<"paymaster-stub">;
|
|
79
|
+
entryPointVersion: z.ZodLiteral<"0.6">;
|
|
80
|
+
paymaster: z.ZodString;
|
|
81
|
+
sponsorshipPolicyId: z.ZodString;
|
|
82
|
+
paymasterAndDataCommitment: z.ZodString;
|
|
83
|
+
providerEstimate: z.ZodObject<{
|
|
84
|
+
blockAnchor: z.ZodLiteral<"unanchored">;
|
|
85
|
+
startedAtMs: z.ZodNumber;
|
|
86
|
+
completedAtMs: z.ZodNumber;
|
|
87
|
+
elapsedMs: z.ZodNumber;
|
|
88
|
+
}, z.core.$strict>;
|
|
89
|
+
gas: z.ZodObject<{
|
|
90
|
+
callGasLimit: z.ZodString;
|
|
91
|
+
verificationGasLimit: z.ZodString;
|
|
92
|
+
preVerificationGas: z.ZodString;
|
|
93
|
+
maxFeePerGasWei: z.ZodString;
|
|
94
|
+
maxPriorityFeePerGasWei: z.ZodString;
|
|
95
|
+
verificationGasMultiplier: z.ZodLiteral<"3">;
|
|
96
|
+
estimatedPrefundWei: z.ZodString;
|
|
97
|
+
reserveWei: z.ZodString;
|
|
98
|
+
estimatedRequiredWei: z.ZodString;
|
|
99
|
+
reserveBps: z.ZodLiteral<"2500">;
|
|
100
|
+
}, z.core.$strict>;
|
|
101
|
+
estimatedGasCostUsdMicros: z.ZodOptional<z.ZodString>;
|
|
102
|
+
}, z.core.$strict>>;
|
|
103
|
+
}, z.core.$strict>;
|
|
104
|
+
}, z.core.$strict>;
|
|
105
|
+
export type AccountGasReview = z.infer<typeof ACCOUNT_GAS_REVIEW_SCHEMA>;
|
|
106
|
+
export type AccountGasPreference = 'auto' | 'sponsor' | 'account';
|
|
107
|
+
export declare class AccountGasReviewError extends Error {
|
|
108
|
+
readonly code: 'ACCOUNT_GAS_REVIEW_INVALID' | 'ACCOUNT_GAS_REVIEW_STALE' | 'ACCOUNT_GAS_SPONSOR_INELIGIBLE';
|
|
109
|
+
constructor(code: 'ACCOUNT_GAS_REVIEW_INVALID' | 'ACCOUNT_GAS_REVIEW_STALE' | 'ACCOUNT_GAS_SPONSOR_INELIGIBLE');
|
|
110
|
+
}
|
|
111
|
+
export declare function createAccountGasReview(input: {
|
|
112
|
+
readonly quote: AccountGasQuote;
|
|
113
|
+
readonly previousRevision?: number;
|
|
114
|
+
readonly preference: AccountGasPreference;
|
|
115
|
+
/** Already validated by the policy reader. This snapshot is eligibility evidence only. */
|
|
116
|
+
readonly sponsorPolicy?: SponsoredGasPolicyProof;
|
|
117
|
+
readonly nowMs: number;
|
|
118
|
+
}): Readonly<AccountGasReview>;
|
|
119
|
+
/** A fresh review is necessary, but caller-owned consent and live final checks remain required. */
|
|
120
|
+
export declare function accountGasReviewCanSubmit(input: AccountGasReview, nowMs: number): boolean;
|
|
121
|
+
/** Native-currency review tolerates fee movement within its displayed 25% funding cushion.
|
|
122
|
+
* This comparison is a consent threshold, not an enforceable maximum network fee. */
|
|
123
|
+
export declare function accountGasReviewNeedsRenewal(previous: AccountGasReview, fresh: AccountGasReview): boolean;
|