@morpho-org/evm-simulation 4.2.0 → 5.0.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 +109 -17
- package/lib/cjs/authorizations.d.ts +95 -0
- package/lib/cjs/authorizations.js +2 -0
- package/lib/cjs/errors.d.ts +207 -13
- package/lib/cjs/errors.js +197 -13
- package/lib/cjs/index.d.ts +10 -2
- package/lib/cjs/index.js +19 -3
- package/lib/cjs/limits.d.ts +112 -0
- package/lib/cjs/limits.js +50 -0
- package/lib/cjs/params.d.ts +25 -0
- package/lib/cjs/params.js +5 -0
- package/lib/cjs/requirements/index.d.ts +1 -0
- package/lib/cjs/requirements/index.js +5 -0
- package/lib/cjs/requirements/requirements.d.ts +64 -0
- package/lib/cjs/requirements/requirements.js +149 -0
- package/lib/cjs/result.d.ts +49 -0
- package/lib/cjs/result.js +2 -0
- package/lib/cjs/simulate/asset-changes.d.ts +7 -5
- package/lib/cjs/simulate/asset-changes.js +6 -5
- package/lib/cjs/simulate/backends/client.d.ts +16 -0
- package/lib/cjs/simulate/backends/client.js +27 -0
- package/lib/cjs/simulate/backends/eth-simulate-v1.d.ts +66 -20
- package/lib/cjs/simulate/backends/eth-simulate-v1.js +172 -98
- package/lib/cjs/simulate/backends/index.d.ts +1 -2
- package/lib/cjs/simulate/backends/index.js +3 -4
- package/lib/cjs/simulate/backends/parse-response.d.ts +69 -0
- package/lib/cjs/simulate/backends/parse-response.js +168 -0
- package/lib/cjs/simulate/backends/resolve-assets.d.ts +26 -0
- package/lib/cjs/simulate/backends/resolve-assets.js +111 -0
- package/lib/cjs/simulate/backends/resolve-pinned-block.d.ts +33 -0
- package/lib/cjs/simulate/backends/resolve-pinned-block.js +46 -0
- package/lib/cjs/simulate/context.d.ts +14 -0
- package/lib/cjs/simulate/context.js +2 -0
- package/lib/cjs/simulate/measurement-plan.d.ts +29 -0
- package/lib/cjs/simulate/measurement-plan.js +115 -0
- package/lib/cjs/simulate/parsing/transfers.d.ts +4 -3
- package/lib/cjs/simulate/parsing/transfers.js +4 -6
- package/lib/cjs/simulate/pipeline/bundles-retention.d.ts +7 -10
- package/lib/cjs/simulate/pipeline/bundles-retention.js +11 -14
- package/lib/cjs/simulate/pipeline/resolve-chain.d.ts +5 -11
- package/lib/cjs/simulate/pipeline/resolve-chain.js +5 -10
- package/lib/cjs/simulate/plan/plan-execution.d.ts +58 -0
- package/lib/cjs/simulate/plan/plan-execution.js +75 -0
- package/lib/cjs/simulate/prepare.d.ts +30 -0
- package/lib/cjs/simulate/prepare.js +80 -0
- package/lib/cjs/simulate/request/index.d.ts +1 -0
- package/lib/cjs/simulate/request/index.js +5 -0
- package/lib/cjs/simulate/request/parse-request.d.ts +44 -0
- package/lib/cjs/simulate/request/parse-request.js +759 -0
- package/lib/cjs/simulate/result.d.ts +19 -0
- package/lib/cjs/simulate/result.js +29 -0
- package/lib/cjs/simulate/run-simulation.d.ts +18 -0
- package/lib/cjs/simulate/run-simulation.js +151 -0
- package/lib/cjs/simulate/simulate.d.ts +100 -50
- package/lib/cjs/simulate/simulate.js +99 -81
- package/lib/cjs/simulate/slippage.d.ts +18 -0
- package/lib/cjs/simulate/slippage.js +41 -0
- package/lib/cjs/simulate/state/contract.d.ts +19 -0
- package/lib/cjs/simulate/state/contract.js +2 -0
- package/lib/cjs/simulate/state/read-state.d.ts +60 -0
- package/lib/cjs/simulate/state/read-state.js +148 -0
- package/lib/cjs/simulate/verify-slippage.d.ts +24 -0
- package/lib/cjs/simulate/verify-slippage.js +85 -0
- package/lib/cjs/types.d.ts +35 -95
- package/lib/esm/authorizations.d.ts +95 -0
- package/lib/esm/authorizations.js +1 -0
- package/lib/esm/errors.d.ts +207 -13
- package/lib/esm/errors.js +189 -12
- package/lib/esm/index.d.ts +10 -2
- package/lib/esm/index.js +4 -1
- package/lib/esm/limits.d.ts +112 -0
- package/lib/esm/limits.js +47 -0
- package/lib/esm/params.d.ts +25 -0
- package/lib/esm/params.js +2 -0
- package/lib/esm/requirements/index.d.ts +1 -0
- package/lib/esm/requirements/index.js +1 -0
- package/lib/esm/requirements/requirements.d.ts +64 -0
- package/lib/esm/requirements/requirements.js +146 -0
- package/lib/esm/result.d.ts +49 -0
- package/lib/esm/result.js +1 -0
- package/lib/esm/simulate/asset-changes.d.ts +7 -5
- package/lib/esm/simulate/asset-changes.js +6 -5
- package/lib/esm/simulate/backends/client.d.ts +16 -0
- package/lib/esm/simulate/backends/client.js +24 -0
- package/lib/esm/simulate/backends/eth-simulate-v1.d.ts +66 -20
- package/lib/esm/simulate/backends/eth-simulate-v1.js +170 -99
- package/lib/esm/simulate/backends/index.d.ts +1 -2
- package/lib/esm/simulate/backends/index.js +1 -2
- package/lib/esm/simulate/backends/parse-response.d.ts +69 -0
- package/lib/esm/simulate/backends/parse-response.js +165 -0
- package/lib/esm/simulate/backends/resolve-assets.d.ts +26 -0
- package/lib/esm/simulate/backends/resolve-assets.js +108 -0
- package/lib/esm/simulate/backends/resolve-pinned-block.d.ts +33 -0
- package/lib/esm/simulate/backends/resolve-pinned-block.js +43 -0
- package/lib/esm/simulate/context.d.ts +14 -0
- package/lib/esm/simulate/context.js +1 -0
- package/lib/esm/simulate/measurement-plan.d.ts +29 -0
- package/lib/esm/simulate/measurement-plan.js +112 -0
- package/lib/esm/simulate/parsing/transfers.d.ts +4 -3
- package/lib/esm/simulate/parsing/transfers.js +4 -6
- package/lib/esm/simulate/pipeline/bundles-retention.d.ts +7 -10
- package/lib/esm/simulate/pipeline/bundles-retention.js +11 -14
- package/lib/esm/simulate/pipeline/resolve-chain.d.ts +5 -11
- package/lib/esm/simulate/pipeline/resolve-chain.js +5 -10
- package/lib/esm/simulate/plan/plan-execution.d.ts +58 -0
- package/lib/esm/simulate/plan/plan-execution.js +72 -0
- package/lib/esm/simulate/prepare.d.ts +30 -0
- package/lib/esm/simulate/prepare.js +77 -0
- package/lib/esm/simulate/request/index.d.ts +1 -0
- package/lib/esm/simulate/request/index.js +1 -0
- package/lib/esm/simulate/request/parse-request.d.ts +44 -0
- package/lib/esm/simulate/request/parse-request.js +756 -0
- package/lib/esm/simulate/result.d.ts +19 -0
- package/lib/esm/simulate/result.js +26 -0
- package/lib/esm/simulate/run-simulation.d.ts +18 -0
- package/lib/esm/simulate/run-simulation.js +148 -0
- package/lib/esm/simulate/simulate.d.ts +100 -50
- package/lib/esm/simulate/simulate.js +99 -81
- package/lib/esm/simulate/slippage.d.ts +18 -0
- package/lib/esm/simulate/slippage.js +38 -0
- package/lib/esm/simulate/state/contract.d.ts +19 -0
- package/lib/esm/simulate/state/contract.js +1 -0
- package/lib/esm/simulate/state/read-state.d.ts +60 -0
- package/lib/esm/simulate/state/read-state.js +144 -0
- package/lib/esm/simulate/verify-slippage.d.ts +24 -0
- package/lib/esm/simulate/verify-slippage.js +82 -0
- package/lib/esm/types.d.ts +35 -95
- package/package.json +7 -6
- package/lib/cjs/simulate/authorizations/index.d.ts +0 -2
- package/lib/cjs/simulate/authorizations/index.js +0 -7
- package/lib/cjs/simulate/authorizations/resolve.d.ts +0 -9
- package/lib/cjs/simulate/authorizations/resolve.js +0 -29
- package/lib/cjs/simulate/authorizations/validate.d.ts +0 -6
- package/lib/cjs/simulate/authorizations/validate.js +0 -23
- package/lib/cjs/simulate/backends/tenderly-rpc.d.ts +0 -23
- package/lib/cjs/simulate/backends/tenderly-rpc.js +0 -234
- package/lib/cjs/simulate/pipeline/build-simulation-txs.d.ts +0 -17
- package/lib/cjs/simulate/pipeline/build-simulation-txs.js +0 -50
- package/lib/cjs/simulate/pipeline/execute-simulation.d.ts +0 -22
- package/lib/cjs/simulate/pipeline/execute-simulation.js +0 -82
- package/lib/cjs/simulate/pipeline/index.d.ts +0 -4
- package/lib/cjs/simulate/pipeline/index.js +0 -11
- package/lib/cjs/simulate/pipeline/validate-input.d.ts +0 -10
- package/lib/cjs/simulate/pipeline/validate-input.js +0 -55
- package/lib/esm/simulate/authorizations/index.d.ts +0 -2
- package/lib/esm/simulate/authorizations/index.js +0 -2
- package/lib/esm/simulate/authorizations/resolve.d.ts +0 -9
- package/lib/esm/simulate/authorizations/resolve.js +0 -26
- package/lib/esm/simulate/authorizations/validate.d.ts +0 -6
- package/lib/esm/simulate/authorizations/validate.js +0 -20
- package/lib/esm/simulate/backends/tenderly-rpc.d.ts +0 -23
- package/lib/esm/simulate/backends/tenderly-rpc.js +0 -231
- package/lib/esm/simulate/pipeline/build-simulation-txs.d.ts +0 -17
- package/lib/esm/simulate/pipeline/build-simulation-txs.js +0 -47
- package/lib/esm/simulate/pipeline/execute-simulation.d.ts +0 -22
- package/lib/esm/simulate/pipeline/execute-simulation.js +0 -79
- package/lib/esm/simulate/pipeline/index.d.ts +0 -4
- package/lib/esm/simulate/pipeline/index.js +0 -4
- package/lib/esm/simulate/pipeline/validate-input.d.ts +0 -10
- package/lib/esm/simulate/pipeline/validate-input.js +0 -52
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { type BlockTag } from "viem";
|
|
2
|
-
import type { RawSimulationResult, SimulationTransaction, TenderlyRpcConfig } from "../../types.js";
|
|
3
|
-
/**
|
|
4
|
-
* Simulate one or more transactions via Tenderly's Node Web3 Gateway.
|
|
5
|
-
* Dispatches to `tenderly_simulateTransaction` for a single call and
|
|
6
|
-
* `tenderly_simulateBundle` for a multi-call bundle.
|
|
7
|
-
*
|
|
8
|
-
* @param params.config - Tenderly RPC config (URL with access key embedded).
|
|
9
|
-
* @param params.transactions - Bundle to simulate, in execution order.
|
|
10
|
-
* @param params.blockNumber - Optional pinned block number or `BlockTag`. Defaults to `latest`.
|
|
11
|
-
* @param params.signal - Optional `AbortSignal` for cancellation / timeout.
|
|
12
|
-
* @returns A {@link RawSimulationResult} with one `RawCall` per input transaction.
|
|
13
|
-
* @throws {SimulationValidationError} when `transactions` is empty.
|
|
14
|
-
* @throws {SimulationRevertedError} when any simulated tx reports `status: false`.
|
|
15
|
-
* @throws {ExternalServiceError} on non-2xx response, JSON-RPC envelope error,
|
|
16
|
-
* schema-validation failure, or fetch-layer failure.
|
|
17
|
-
*/
|
|
18
|
-
export declare function simulateTenderlyRpc(params: {
|
|
19
|
-
config: TenderlyRpcConfig;
|
|
20
|
-
transactions: SimulationTransaction[];
|
|
21
|
-
blockNumber?: bigint | BlockTag;
|
|
22
|
-
signal?: AbortSignal;
|
|
23
|
-
}): Promise<RawSimulationResult>;
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { ethAddress, isAddress, isHex, maxUint256, numberToHex, } from "viem";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { ExternalServiceError, SimulationRevertedError, SimulationValidationError, } from "../../errors.js";
|
|
4
|
-
import { groupAssetChanges, normalizeAssetToken, } from "../asset-changes.js";
|
|
5
|
-
const addressSchema = z.custom((val) => typeof val === "string" && isAddress(val));
|
|
6
|
-
const hexSchema = z.custom((val) => typeof val === "string" && isHex(val));
|
|
7
|
-
const traceFrameSchema = z
|
|
8
|
-
.object({
|
|
9
|
-
output: hexSchema.optional(),
|
|
10
|
-
error: z.string().optional(),
|
|
11
|
-
errorReason: z.string().optional(),
|
|
12
|
-
})
|
|
13
|
-
.passthrough();
|
|
14
|
-
const logSchema = z
|
|
15
|
-
.object({
|
|
16
|
-
raw: z
|
|
17
|
-
.object({
|
|
18
|
-
address: addressSchema,
|
|
19
|
-
topics: z.array(hexSchema).optional(),
|
|
20
|
-
data: hexSchema.optional(),
|
|
21
|
-
})
|
|
22
|
-
.passthrough()
|
|
23
|
-
.optional(),
|
|
24
|
-
})
|
|
25
|
-
.passthrough();
|
|
26
|
-
const assetChangeSchema = z
|
|
27
|
-
.object({
|
|
28
|
-
assetInfo: z
|
|
29
|
-
.object({
|
|
30
|
-
contractAddress: addressSchema.optional(),
|
|
31
|
-
symbol: z.string().optional(),
|
|
32
|
-
decimals: z.number().optional(),
|
|
33
|
-
})
|
|
34
|
-
.passthrough(),
|
|
35
|
-
from: addressSchema.optional(),
|
|
36
|
-
to: addressSchema.optional(),
|
|
37
|
-
rawAmount: hexSchema,
|
|
38
|
-
})
|
|
39
|
-
.passthrough();
|
|
40
|
-
const simResultSchema = z
|
|
41
|
-
.object({
|
|
42
|
-
status: z.boolean(),
|
|
43
|
-
gasUsed: hexSchema,
|
|
44
|
-
logs: z.array(logSchema).optional(),
|
|
45
|
-
trace: z.array(traceFrameSchema).optional(),
|
|
46
|
-
assetChanges: z.array(assetChangeSchema).optional(),
|
|
47
|
-
error: z.string().optional(),
|
|
48
|
-
errorMessage: z.string().optional(),
|
|
49
|
-
})
|
|
50
|
-
.passthrough();
|
|
51
|
-
const rpcErrorSchema = z
|
|
52
|
-
.object({
|
|
53
|
-
code: z.number().optional(),
|
|
54
|
-
message: z.string(),
|
|
55
|
-
data: z.unknown().optional(),
|
|
56
|
-
})
|
|
57
|
-
.passthrough();
|
|
58
|
-
function rpcEnvelope(result) {
|
|
59
|
-
return z
|
|
60
|
-
.object({ result: result.optional(), error: rpcErrorSchema.optional() })
|
|
61
|
-
.passthrough();
|
|
62
|
-
}
|
|
63
|
-
const singleEnvelope = rpcEnvelope(simResultSchema);
|
|
64
|
-
const bundleEnvelope = rpcEnvelope(z.array(simResultSchema).min(1));
|
|
65
|
-
/**
|
|
66
|
-
* Simulate one or more transactions via Tenderly's Node Web3 Gateway.
|
|
67
|
-
* Dispatches to `tenderly_simulateTransaction` for a single call and
|
|
68
|
-
* `tenderly_simulateBundle` for a multi-call bundle.
|
|
69
|
-
*
|
|
70
|
-
* @param params.config - Tenderly RPC config (URL with access key embedded).
|
|
71
|
-
* @param params.transactions - Bundle to simulate, in execution order.
|
|
72
|
-
* @param params.blockNumber - Optional pinned block number or `BlockTag`. Defaults to `latest`.
|
|
73
|
-
* @param params.signal - Optional `AbortSignal` for cancellation / timeout.
|
|
74
|
-
* @returns A {@link RawSimulationResult} with one `RawCall` per input transaction.
|
|
75
|
-
* @throws {SimulationValidationError} when `transactions` is empty.
|
|
76
|
-
* @throws {SimulationRevertedError} when any simulated tx reports `status: false`.
|
|
77
|
-
* @throws {ExternalServiceError} on non-2xx response, JSON-RPC envelope error,
|
|
78
|
-
* schema-validation failure, or fetch-layer failure.
|
|
79
|
-
*/
|
|
80
|
-
export async function simulateTenderlyRpc(params) {
|
|
81
|
-
const { config, transactions, blockNumber, signal } = params;
|
|
82
|
-
const firstTx = transactions[0];
|
|
83
|
-
if (!firstTx) {
|
|
84
|
-
throw new SimulationValidationError("At least one transaction is required", []);
|
|
85
|
-
}
|
|
86
|
-
const block = encodeBlock(blockNumber);
|
|
87
|
-
// Inflate sender ETH balance to avoid false "insufficient funds for gas"
|
|
88
|
-
// reverts on wallets low on native gas token — mirrors simulateV1.
|
|
89
|
-
const stateOverrides = buildStateOverrides(firstTx.from);
|
|
90
|
-
try {
|
|
91
|
-
if (transactions.length === 1) {
|
|
92
|
-
const json = await rpcRequest({
|
|
93
|
-
rpcUrl: config.rpcUrl,
|
|
94
|
-
method: "tenderly_simulateTransaction",
|
|
95
|
-
params: [buildCall(firstTx), block, stateOverrides],
|
|
96
|
-
signal,
|
|
97
|
-
});
|
|
98
|
-
const result = unwrapResult(singleEnvelope.parse(json));
|
|
99
|
-
return {
|
|
100
|
-
calls: [toRawCall(result)],
|
|
101
|
-
assetChanges: toAssetChanges([result]),
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
const json = await rpcRequest({
|
|
105
|
-
rpcUrl: config.rpcUrl,
|
|
106
|
-
method: "tenderly_simulateBundle",
|
|
107
|
-
params: [transactions.map(buildCall), block, stateOverrides],
|
|
108
|
-
signal,
|
|
109
|
-
});
|
|
110
|
-
const results = unwrapResult(bundleEnvelope.parse(json));
|
|
111
|
-
return {
|
|
112
|
-
calls: results.map(toRawCall),
|
|
113
|
-
assetChanges: toAssetChanges(results),
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
if (error instanceof SimulationRevertedError)
|
|
118
|
-
throw error;
|
|
119
|
-
if (error instanceof ExternalServiceError)
|
|
120
|
-
throw error;
|
|
121
|
-
throw new ExternalServiceError(`Tenderly RPC error: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
async function rpcRequest(params) {
|
|
125
|
-
const { rpcUrl, method, params: rpcParams, signal } = params;
|
|
126
|
-
const response = await fetch(rpcUrl, {
|
|
127
|
-
method: "POST",
|
|
128
|
-
headers: { "Content-Type": "application/json" },
|
|
129
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params: rpcParams }),
|
|
130
|
-
signal,
|
|
131
|
-
});
|
|
132
|
-
if (!response.ok) {
|
|
133
|
-
throw new ExternalServiceError(`Tenderly RPC returned ${response.status}: ${response.statusText}`);
|
|
134
|
-
}
|
|
135
|
-
return await response.json();
|
|
136
|
-
}
|
|
137
|
-
function unwrapResult(envelope) {
|
|
138
|
-
if (envelope.error) {
|
|
139
|
-
throw new ExternalServiceError(`Tenderly RPC error: ${envelope.error.message}`);
|
|
140
|
-
}
|
|
141
|
-
if (envelope.result === undefined) {
|
|
142
|
-
throw new ExternalServiceError("Tenderly RPC returned no result");
|
|
143
|
-
}
|
|
144
|
-
return envelope.result;
|
|
145
|
-
}
|
|
146
|
-
function buildCall(tx) {
|
|
147
|
-
return {
|
|
148
|
-
from: tx.from,
|
|
149
|
-
to: tx.to,
|
|
150
|
-
input: tx.data,
|
|
151
|
-
data: tx.data,
|
|
152
|
-
value: numberToHex(tx.value ?? 0n),
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
function buildStateOverrides(sender) {
|
|
156
|
-
// Half of uint256 (not the ceiling) so the override leaves headroom for
|
|
157
|
-
// inbound native ETH — a WETH.withdraw refund or swap payout would overflow
|
|
158
|
-
// the recipient balance and revert the value transfer if the sender were
|
|
159
|
-
// pinned at maxUint256.
|
|
160
|
-
return { [sender]: { balance: numberToHex(maxUint256 / 2n) } };
|
|
161
|
-
}
|
|
162
|
-
function encodeBlock(blockNumber) {
|
|
163
|
-
if (blockNumber === undefined)
|
|
164
|
-
return "latest";
|
|
165
|
-
return typeof blockNumber === "bigint"
|
|
166
|
-
? numberToHex(blockNumber)
|
|
167
|
-
: blockNumber;
|
|
168
|
-
}
|
|
169
|
-
function toRawCall(data) {
|
|
170
|
-
if (data.status !== true) {
|
|
171
|
-
// Tenderly RPC surfaces the revert reason on the trace frame; the
|
|
172
|
-
// top-level fields are checked as a defensive fallback.
|
|
173
|
-
const traceError = data.trace?.find((f) => f.errorReason || f.error);
|
|
174
|
-
const message = traceError?.errorReason ||
|
|
175
|
-
traceError?.error ||
|
|
176
|
-
data.errorMessage ||
|
|
177
|
-
data.error ||
|
|
178
|
-
"Transaction simulation reverted";
|
|
179
|
-
throw new SimulationRevertedError(message, data);
|
|
180
|
-
}
|
|
181
|
-
const logs = [];
|
|
182
|
-
for (const log of data.logs ?? []) {
|
|
183
|
-
if (log.raw) {
|
|
184
|
-
logs.push({
|
|
185
|
-
address: log.raw.address,
|
|
186
|
-
topics: log.raw.topics ?? [],
|
|
187
|
-
data: log.raw.data ?? "0x",
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return {
|
|
192
|
-
logs,
|
|
193
|
-
status: true,
|
|
194
|
-
returnData: data.trace?.[0]?.output ?? "0x",
|
|
195
|
-
gasUsed: BigInt(data.gasUsed),
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Reduce Tenderly's per-transfer asset changes to net per-token balance changes
|
|
200
|
-
* grouped by account. Both endpoints of each transfer are accounted (sender,
|
|
201
|
-
* counterparties, and the zero address for mints/burns).
|
|
202
|
-
*/
|
|
203
|
-
function toAssetChanges(results) {
|
|
204
|
-
const entries = [];
|
|
205
|
-
for (const result of results) {
|
|
206
|
-
for (const change of result.assetChanges ?? []) {
|
|
207
|
-
const amount = BigInt(change.rawAmount);
|
|
208
|
-
const token = change.assetInfo.contractAddress
|
|
209
|
-
? normalizeAssetToken(change.assetInfo.contractAddress)
|
|
210
|
-
: ethAddress;
|
|
211
|
-
const { symbol, decimals } = change.assetInfo;
|
|
212
|
-
if (change.to)
|
|
213
|
-
entries.push({
|
|
214
|
-
account: change.to,
|
|
215
|
-
token,
|
|
216
|
-
diff: amount,
|
|
217
|
-
symbol,
|
|
218
|
-
decimals,
|
|
219
|
-
});
|
|
220
|
-
if (change.from)
|
|
221
|
-
entries.push({
|
|
222
|
-
account: change.from,
|
|
223
|
-
token,
|
|
224
|
-
diff: -amount,
|
|
225
|
-
symbol,
|
|
226
|
-
decimals,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return groupAssetChanges(entries);
|
|
231
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { SimulateParams, SimulationTransaction } from "../../types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Stage 3 of the simulate() pipeline.
|
|
4
|
-
*
|
|
5
|
-
* Prepends any resolved authorizations (e.g. ERC20 `approve` calls generated from
|
|
6
|
-
* `{ type: 'signature' }` entries) to the user's transactions. If no authorizations
|
|
7
|
-
* were supplied, returns the user's transactions unchanged.
|
|
8
|
-
*
|
|
9
|
-
* Defense-in-depth: after prepending, verifies EVERY resolved tx shares the user
|
|
10
|
-
* sender. This catches an `{ type: 'approval', transaction }` auth where the
|
|
11
|
-
* caller-provided `transaction.from` does not match the bundle sender — the
|
|
12
|
-
* `approval` variant otherwise passes through without validation. Rejecting here
|
|
13
|
-
* keeps the "all simulationTxs share one `from`" invariant the downstream
|
|
14
|
-
* backends (eth_simulateV1) assume, and prevents a malicious or buggy caller
|
|
15
|
-
* from prepending a tx on behalf of an unexpected address.
|
|
16
|
-
*/
|
|
17
|
-
export declare function buildSimulationTxs(params: SimulateParams): SimulationTransaction[];
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { getAddress } from "viem";
|
|
2
|
-
import { SimulationValidationError } from "../../errors.js";
|
|
3
|
-
import { resolveAuthorizations } from "../authorizations/index.js";
|
|
4
|
-
/**
|
|
5
|
-
* Stage 3 of the simulate() pipeline.
|
|
6
|
-
*
|
|
7
|
-
* Prepends any resolved authorizations (e.g. ERC20 `approve` calls generated from
|
|
8
|
-
* `{ type: 'signature' }` entries) to the user's transactions. If no authorizations
|
|
9
|
-
* were supplied, returns the user's transactions unchanged.
|
|
10
|
-
*
|
|
11
|
-
* Defense-in-depth: after prepending, verifies EVERY resolved tx shares the user
|
|
12
|
-
* sender. This catches an `{ type: 'approval', transaction }` auth where the
|
|
13
|
-
* caller-provided `transaction.from` does not match the bundle sender — the
|
|
14
|
-
* `approval` variant otherwise passes through without validation. Rejecting here
|
|
15
|
-
* keeps the "all simulationTxs share one `from`" invariant the downstream
|
|
16
|
-
* backends (eth_simulateV1) assume, and prevents a malicious or buggy caller
|
|
17
|
-
* from prepending a tx on behalf of an unexpected address.
|
|
18
|
-
*/
|
|
19
|
-
export function buildSimulationTxs(params) {
|
|
20
|
-
const { transactions, authorizations } = params;
|
|
21
|
-
if (!authorizations || authorizations.length === 0) {
|
|
22
|
-
return transactions;
|
|
23
|
-
}
|
|
24
|
-
const sender = transactions[0].from;
|
|
25
|
-
const resolved = resolveAuthorizations(authorizations, sender);
|
|
26
|
-
const simulationTxs = [...resolved, ...transactions];
|
|
27
|
-
const senderChecksum = getAddress(sender);
|
|
28
|
-
const mismatches = [];
|
|
29
|
-
for (let i = 0; i < simulationTxs.length; i++) {
|
|
30
|
-
const tx = simulationTxs[i];
|
|
31
|
-
let txFromChecksum;
|
|
32
|
-
try {
|
|
33
|
-
txFromChecksum = getAddress(tx.from);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
mismatches.push(`simulationTxs[${i}].from: not a valid address (${tx.from})`);
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (txFromChecksum !== senderChecksum) {
|
|
40
|
-
mismatches.push(`simulationTxs[${i}].from: expected ${senderChecksum}, got ${txFromChecksum}`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (mismatches.length > 0) {
|
|
44
|
-
throw new SimulationValidationError("Authorization transaction(s) must share the user sender", mismatches);
|
|
45
|
-
}
|
|
46
|
-
return simulationTxs;
|
|
47
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { Address, BlockTag } from "viem";
|
|
2
|
-
import type { RawSimulationResult, SimulationConfig, SimulationTransaction } from "../../types.js";
|
|
3
|
-
/**
|
|
4
|
-
* Stage 4 of the simulate() pipeline.
|
|
5
|
-
*
|
|
6
|
-
* Dispatches the transaction bundle to the available backend with a shared timeout
|
|
7
|
-
* budget:
|
|
8
|
-
* - If a Tenderly RPC URL is configured for the chain, it gets
|
|
9
|
-
* `TENDERLY_BUDGET_RATIO` (60%) of the timeout. On `ExternalServiceError`,
|
|
10
|
-
* falls back to `eth_simulateV1` with `max(remaining, FALLBACK_MIN_BUDGET_MS)`
|
|
11
|
-
* so the fallback still has a viable window when Tenderly eats its full slice.
|
|
12
|
-
* `SimulationRevertedError` (contract revert) propagates immediately without
|
|
13
|
-
* retry — a revert is a property of the bundle, not the backend.
|
|
14
|
-
* - If only `eth_simulateV1` is configured, it gets the full timeout.
|
|
15
|
-
*/
|
|
16
|
-
export declare function executeSimulation(params: {
|
|
17
|
-
config: SimulationConfig;
|
|
18
|
-
chainId: number;
|
|
19
|
-
transactions: SimulationTransaction[];
|
|
20
|
-
blockNumber?: bigint | BlockTag;
|
|
21
|
-
wNative?: Address | null;
|
|
22
|
-
}): Promise<RawSimulationResult>;
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { ExternalServiceError, UnsupportedChainError } from "../../errors.js";
|
|
2
|
-
import { simulateTenderlyRpc, simulateV1 } from "../backends/index.js";
|
|
3
|
-
import { resolveChain } from "./resolve-chain.js";
|
|
4
|
-
/** Total budget for a single `simulate()` call across both backends. */
|
|
5
|
-
const DEFAULT_TIMEOUT_MS = 5000;
|
|
6
|
-
/** Fraction of `timeoutMs` given to Tenderly before falling back to `eth_simulateV1`. */
|
|
7
|
-
const TENDERLY_BUDGET_RATIO = 0.6;
|
|
8
|
-
/**
|
|
9
|
-
* Minimum budget (ms) handed to the `eth_simulateV1` fallback when Tenderly fails.
|
|
10
|
-
*
|
|
11
|
-
* Without a floor, if Tenderly exhausts its slice and dies at the deadline, the
|
|
12
|
-
* fallback would get ~0 ms and abort immediately — turning a degraded-Tenderly
|
|
13
|
-
* scenario into a total outage even when the fallback backend is healthy. A
|
|
14
|
-
* modest floor keeps the fallback viable at the cost of the overall
|
|
15
|
-
* `timeoutMs` being treated as a soft ceiling.
|
|
16
|
-
*/
|
|
17
|
-
const FALLBACK_MIN_BUDGET_MS = 1500;
|
|
18
|
-
/**
|
|
19
|
-
* Stage 4 of the simulate() pipeline.
|
|
20
|
-
*
|
|
21
|
-
* Dispatches the transaction bundle to the available backend with a shared timeout
|
|
22
|
-
* budget:
|
|
23
|
-
* - If a Tenderly RPC URL is configured for the chain, it gets
|
|
24
|
-
* `TENDERLY_BUDGET_RATIO` (60%) of the timeout. On `ExternalServiceError`,
|
|
25
|
-
* falls back to `eth_simulateV1` with `max(remaining, FALLBACK_MIN_BUDGET_MS)`
|
|
26
|
-
* so the fallback still has a viable window when Tenderly eats its full slice.
|
|
27
|
-
* `SimulationRevertedError` (contract revert) propagates immediately without
|
|
28
|
-
* retry — a revert is a property of the bundle, not the backend.
|
|
29
|
-
* - If only `eth_simulateV1` is configured, it gets the full timeout.
|
|
30
|
-
*/
|
|
31
|
-
export async function executeSimulation(params) {
|
|
32
|
-
const { config, chainId, transactions, blockNumber, wNative } = params;
|
|
33
|
-
const chain = resolveChain(config, chainId);
|
|
34
|
-
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
35
|
-
const deadline = Date.now() + timeoutMs;
|
|
36
|
-
if (chain.tenderlyRpc) {
|
|
37
|
-
const tenderlyTimeout = Math.floor(timeoutMs * TENDERLY_BUDGET_RATIO);
|
|
38
|
-
try {
|
|
39
|
-
return await simulateTenderlyRpc({
|
|
40
|
-
config: chain.tenderlyRpc,
|
|
41
|
-
transactions,
|
|
42
|
-
blockNumber,
|
|
43
|
-
signal: AbortSignal.timeout(tenderlyTimeout),
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
if (!(error instanceof ExternalServiceError))
|
|
48
|
-
throw error;
|
|
49
|
-
config.logger?.warn("Tenderly simulation failed, attempting fallback", {
|
|
50
|
-
chainId,
|
|
51
|
-
error: error.message,
|
|
52
|
-
cause: error.cause,
|
|
53
|
-
});
|
|
54
|
-
if (!chain.simulateV1Url)
|
|
55
|
-
throw error;
|
|
56
|
-
const fallbackBudget = Math.max(deadline - Date.now(), FALLBACK_MIN_BUDGET_MS);
|
|
57
|
-
return await simulateV1({
|
|
58
|
-
rpcUrl: chain.simulateV1Url,
|
|
59
|
-
chainId,
|
|
60
|
-
transactions,
|
|
61
|
-
blockNumber,
|
|
62
|
-
wNative,
|
|
63
|
-
signal: AbortSignal.timeout(fallbackBudget),
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/* v8 ignore next: resolveChain rejects this state before executeSimulation reaches the fallback path. */
|
|
68
|
-
if (!chain.simulateV1Url) {
|
|
69
|
-
throw new UnsupportedChainError(chainId);
|
|
70
|
-
}
|
|
71
|
-
return await simulateV1({
|
|
72
|
-
rpcUrl: chain.simulateV1Url,
|
|
73
|
-
chainId,
|
|
74
|
-
transactions,
|
|
75
|
-
blockNumber,
|
|
76
|
-
wNative,
|
|
77
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
78
|
-
});
|
|
79
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { SimulateParams } from "../../types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Stage 1 of the simulate() pipeline.
|
|
4
|
-
*
|
|
5
|
-
* Throws `SimulationValidationError` with a `fieldErrors[]` list on any invalid input:
|
|
6
|
-
* empty transactions, malformed / zero-addr fields, missing `data`, negative `value`,
|
|
7
|
-
* bad `chainId`, or mixed senders (all txs in a bundle must share the same `from`).
|
|
8
|
-
* Also runs `validateAuthorizations` on the optional authorizations array.
|
|
9
|
-
*/
|
|
10
|
-
export declare function validateInput(params: SimulateParams): void;
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { isAddress, zeroAddress } from "viem";
|
|
2
|
-
import { SimulationValidationError } from "../../errors.js";
|
|
3
|
-
import { validateAuthorizations } from "../authorizations/index.js";
|
|
4
|
-
/**
|
|
5
|
-
* Stage 1 of the simulate() pipeline.
|
|
6
|
-
*
|
|
7
|
-
* Throws `SimulationValidationError` with a `fieldErrors[]` list on any invalid input:
|
|
8
|
-
* empty transactions, malformed / zero-addr fields, missing `data`, negative `value`,
|
|
9
|
-
* bad `chainId`, or mixed senders (all txs in a bundle must share the same `from`).
|
|
10
|
-
* Also runs `validateAuthorizations` on the optional authorizations array.
|
|
11
|
-
*/
|
|
12
|
-
export function validateInput(params) {
|
|
13
|
-
const errors = [];
|
|
14
|
-
if (!Number.isInteger(params.chainId) || params.chainId <= 0) {
|
|
15
|
-
errors.push(`chainId: must be a positive integer (got ${params.chainId})`);
|
|
16
|
-
}
|
|
17
|
-
const transactions = params.transactions ?? [];
|
|
18
|
-
if (transactions.length === 0) {
|
|
19
|
-
errors.push("transactions: must contain at least 1 transaction");
|
|
20
|
-
}
|
|
21
|
-
for (let i = 0; i < transactions.length; i++) {
|
|
22
|
-
const tx = transactions[i];
|
|
23
|
-
if (!tx.from || !isAddress(tx.from) || tx.from === zeroAddress) {
|
|
24
|
-
errors.push(`transactions[${i}].from: must be a valid non-zero address`);
|
|
25
|
-
}
|
|
26
|
-
if (!tx.to || !isAddress(tx.to) || tx.to === zeroAddress) {
|
|
27
|
-
errors.push(`transactions[${i}].to: must be a valid non-zero address`);
|
|
28
|
-
}
|
|
29
|
-
if (!tx.data) {
|
|
30
|
-
errors.push(`transactions[${i}].data: must be provided`);
|
|
31
|
-
}
|
|
32
|
-
if (tx.value !== undefined && tx.value < 0n) {
|
|
33
|
-
errors.push(`transactions[${i}].value: must be non-negative`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
// Same-sender check uses RAW `.from` strings (lowercased) so the invariant
|
|
37
|
-
// fires even when some tx `from`s are malformed — individual txs already
|
|
38
|
-
// flagged above, but we still want one loud signal that the bundle's
|
|
39
|
-
// senders don't match.
|
|
40
|
-
if (transactions.length > 1) {
|
|
41
|
-
const distinct = new Set(transactions.map((tx) => (tx.from ?? "").toLowerCase()));
|
|
42
|
-
if (distinct.size > 1) {
|
|
43
|
-
errors.push("transactions: all transactions must share the same from address");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
if (params.authorizations) {
|
|
47
|
-
errors.push(...validateAuthorizations(params.authorizations));
|
|
48
|
-
}
|
|
49
|
-
if (errors.length > 0) {
|
|
50
|
-
throw new SimulationValidationError("Invalid simulation input", errors);
|
|
51
|
-
}
|
|
52
|
-
}
|