@morpho-org/evm-simulation 1.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/LICENSE +21 -0
- package/README.md +77 -0
- package/lib/cjs/errors.d.ts +50 -0
- package/lib/cjs/errors.js +72 -0
- package/lib/cjs/index.d.ts +4 -0
- package/lib/cjs/index.js +17 -0
- package/lib/cjs/package.json +1 -0
- package/lib/cjs/screen-addresses/index.d.ts +1 -0
- package/lib/cjs/screen-addresses/index.js +5 -0
- package/lib/cjs/screen-addresses/sanctioned-addresses.d.ts +11 -0
- package/lib/cjs/screen-addresses/sanctioned-addresses.js +77 -0
- package/lib/cjs/screen-addresses/screen-addresses.d.ts +34 -0
- package/lib/cjs/screen-addresses/screen-addresses.js +152 -0
- package/lib/cjs/simulate/authorizations/index.d.ts +2 -0
- package/lib/cjs/simulate/authorizations/index.js +7 -0
- package/lib/cjs/simulate/authorizations/resolve.d.ts +9 -0
- package/lib/cjs/simulate/authorizations/resolve.js +29 -0
- package/lib/cjs/simulate/authorizations/validate.d.ts +6 -0
- package/lib/cjs/simulate/authorizations/validate.js +23 -0
- package/lib/cjs/simulate/backends/eth-simulate-v1.d.ts +15 -0
- package/lib/cjs/simulate/backends/eth-simulate-v1.js +76 -0
- package/lib/cjs/simulate/backends/index.d.ts +2 -0
- package/lib/cjs/simulate/backends/index.js +7 -0
- package/lib/cjs/simulate/backends/tenderly-rest.d.ts +20 -0
- package/lib/cjs/simulate/backends/tenderly-rest.js +269 -0
- package/lib/cjs/simulate/index.d.ts +1 -0
- package/lib/cjs/simulate/index.js +5 -0
- package/lib/cjs/simulate/parsing/index.d.ts +1 -0
- package/lib/cjs/simulate/parsing/index.js +5 -0
- package/lib/cjs/simulate/parsing/transfers.d.ts +27 -0
- package/lib/cjs/simulate/parsing/transfers.js +153 -0
- package/lib/cjs/simulate/pipeline/build-simulation-txs.d.ts +17 -0
- package/lib/cjs/simulate/pipeline/build-simulation-txs.js +50 -0
- package/lib/cjs/simulate/pipeline/bundler-retention.d.ts +19 -0
- package/lib/cjs/simulate/pipeline/bundler-retention.js +90 -0
- package/lib/cjs/simulate/pipeline/execute-simulation.d.ts +25 -0
- package/lib/cjs/simulate/pipeline/execute-simulation.js +87 -0
- package/lib/cjs/simulate/pipeline/index.d.ts +4 -0
- package/lib/cjs/simulate/pipeline/index.js +11 -0
- package/lib/cjs/simulate/pipeline/resolve-chain.d.ts +14 -0
- package/lib/cjs/simulate/pipeline/resolve-chain.js +19 -0
- package/lib/cjs/simulate/pipeline/validate-input.d.ts +10 -0
- package/lib/cjs/simulate/pipeline/validate-input.js +55 -0
- package/lib/cjs/simulate/simulate.d.ts +26 -0
- package/lib/cjs/simulate/simulate.js +46 -0
- package/lib/cjs/types.d.ts +131 -0
- package/lib/cjs/types.js +2 -0
- package/lib/esm/errors.d.ts +50 -0
- package/lib/esm/errors.js +62 -0
- package/lib/esm/index.d.ts +4 -0
- package/lib/esm/index.js +5 -0
- package/lib/esm/package.json +1 -0
- package/lib/esm/screen-addresses/index.d.ts +1 -0
- package/lib/esm/screen-addresses/index.js +1 -0
- package/lib/esm/screen-addresses/sanctioned-addresses.d.ts +11 -0
- package/lib/esm/screen-addresses/sanctioned-addresses.js +74 -0
- package/lib/esm/screen-addresses/screen-addresses.d.ts +34 -0
- package/lib/esm/screen-addresses/screen-addresses.js +149 -0
- package/lib/esm/simulate/authorizations/index.d.ts +2 -0
- package/lib/esm/simulate/authorizations/index.js +2 -0
- package/lib/esm/simulate/authorizations/resolve.d.ts +9 -0
- package/lib/esm/simulate/authorizations/resolve.js +26 -0
- package/lib/esm/simulate/authorizations/validate.d.ts +6 -0
- package/lib/esm/simulate/authorizations/validate.js +20 -0
- package/lib/esm/simulate/backends/eth-simulate-v1.d.ts +15 -0
- package/lib/esm/simulate/backends/eth-simulate-v1.js +73 -0
- package/lib/esm/simulate/backends/index.d.ts +2 -0
- package/lib/esm/simulate/backends/index.js +2 -0
- package/lib/esm/simulate/backends/tenderly-rest.d.ts +20 -0
- package/lib/esm/simulate/backends/tenderly-rest.js +266 -0
- package/lib/esm/simulate/index.d.ts +1 -0
- package/lib/esm/simulate/index.js +1 -0
- package/lib/esm/simulate/parsing/index.d.ts +1 -0
- package/lib/esm/simulate/parsing/index.js +1 -0
- package/lib/esm/simulate/parsing/transfers.d.ts +27 -0
- package/lib/esm/simulate/parsing/transfers.js +149 -0
- package/lib/esm/simulate/pipeline/build-simulation-txs.d.ts +17 -0
- package/lib/esm/simulate/pipeline/build-simulation-txs.js +47 -0
- package/lib/esm/simulate/pipeline/bundler-retention.d.ts +19 -0
- package/lib/esm/simulate/pipeline/bundler-retention.js +87 -0
- package/lib/esm/simulate/pipeline/execute-simulation.d.ts +25 -0
- package/lib/esm/simulate/pipeline/execute-simulation.js +84 -0
- package/lib/esm/simulate/pipeline/index.d.ts +4 -0
- package/lib/esm/simulate/pipeline/index.js +4 -0
- package/lib/esm/simulate/pipeline/resolve-chain.d.ts +14 -0
- package/lib/esm/simulate/pipeline/resolve-chain.js +16 -0
- package/lib/esm/simulate/pipeline/validate-input.d.ts +10 -0
- package/lib/esm/simulate/pipeline/validate-input.js +52 -0
- package/lib/esm/simulate/simulate.d.ts +26 -0
- package/lib/esm/simulate/simulate.js +43 -0
- package/lib/esm/types.d.ts +131 -0
- package/lib/esm/types.js +1 -0
- package/package.json +49 -0
- package/src/index.ts +27 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Morpho Association
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @morpho-org/evm-simulation
|
|
2
|
+
|
|
3
|
+
EVM transaction simulation engine for Morpho — bundle execution preview,
|
|
4
|
+
transfer parsing, and compliance screening.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
Add to the consuming app's `package.json`:
|
|
9
|
+
|
|
10
|
+
```jsonc
|
|
11
|
+
{
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@morpho-org/evm-simulation": "workspace:*"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import {
|
|
22
|
+
simulate,
|
|
23
|
+
screenAddresses,
|
|
24
|
+
type SimulationConfig,
|
|
25
|
+
SimulationRevertedError
|
|
26
|
+
} from "@morpho-org/evm-simulation";
|
|
27
|
+
|
|
28
|
+
const config: SimulationConfig = {
|
|
29
|
+
tenderlyRest: {
|
|
30
|
+
apiBaseUrl: "https://api.tenderly.co",
|
|
31
|
+
accessToken: process.env.TENDERLY_ACCESS_TOKEN!,
|
|
32
|
+
accountSlug: "my-account",
|
|
33
|
+
projectSlug: "my-project",
|
|
34
|
+
supportedChainIds: new Set([1]),
|
|
35
|
+
},
|
|
36
|
+
chains: new Map([[1, { simulateV1Url: process.env.MAINNET_RPC_URL }]]),
|
|
37
|
+
timeoutMs: 5000,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// Pass { shareable: true } when you want a shareable Tenderly URL (user-facing preview).
|
|
42
|
+
// Omit it for server-side verification — the same function, same return shape.
|
|
43
|
+
const { transfers, simulationTxs, tenderlyUrl } = await simulate(
|
|
44
|
+
config,
|
|
45
|
+
{
|
|
46
|
+
chainId: 1,
|
|
47
|
+
transactions: [{ from: user, to: vault, data: encodedDeposit }],
|
|
48
|
+
authorizations: [{ type: "signature", token: usdc, spender: vault }],
|
|
49
|
+
},
|
|
50
|
+
{ shareable: true }
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// For server-side flows, pipe into screenAddresses:
|
|
54
|
+
await screenAddresses({ simulationTxs, transfers });
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (err instanceof SimulationRevertedError) {
|
|
57
|
+
// show err.reason to the user
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## API surface
|
|
64
|
+
|
|
65
|
+
All symbols below are re-exported from the package root.
|
|
66
|
+
|
|
67
|
+
- `simulate(config, params, options?)` — run a bundle through the simulation pipeline. Pass `{ shareable: true }` for a shareable Tenderly URL; omit for server-side verification.
|
|
68
|
+
- `screenAddresses({ simulationTxs, transfers, chainalysisApiKey? })` — static sanctioned-set + optional Chainalysis screening.
|
|
69
|
+
- Config types: `SimulationConfig`, `TenderlyRestConfig`, `ChainSimulationConfig`, `SimulationLogger`.
|
|
70
|
+
- Data types: `SimulationTransaction`, `SimulationAuthorization`, `Transfer`, `SimulateParams`, `SimulationResult`.
|
|
71
|
+
- Errors: `SimulationPackageError` (abstract base — `instanceof` it to catch any package error), `SimulationRevertedError`, `BlacklistViolationError`, `AddressScreeningError`, `ExternalServiceError`, `SimulationValidationError`, `UnsupportedChainError`.
|
|
72
|
+
|
|
73
|
+
## Deeper docs
|
|
74
|
+
|
|
75
|
+
See [`CLAUDE.md`](./CLAUDE.md) in this directory for the execution flow diagram,
|
|
76
|
+
backend tradeoffs, authorizations model, screening design, error-handling table,
|
|
77
|
+
and recipes for adding a chain or a new backend.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error for all simulation package errors.
|
|
3
|
+
* Transport-agnostic — no HTTP status codes.
|
|
4
|
+
* Consumers use try/catch + instanceof to handle specific error types.
|
|
5
|
+
*/
|
|
6
|
+
export declare abstract class SimulationPackageError extends Error {
|
|
7
|
+
abstract readonly code: string;
|
|
8
|
+
constructor(message: string, options?: ErrorOptions);
|
|
9
|
+
}
|
|
10
|
+
/** Transaction would revert on-chain. Not bypassable. */
|
|
11
|
+
export declare class SimulationRevertedError extends SimulationPackageError {
|
|
12
|
+
readonly reason: string | undefined;
|
|
13
|
+
readonly details?: unknown | undefined;
|
|
14
|
+
readonly code = "SIMULATION_REVERTED";
|
|
15
|
+
constructor(reason: string | undefined, details?: unknown | undefined);
|
|
16
|
+
}
|
|
17
|
+
interface RetainedAsset {
|
|
18
|
+
address: string | undefined;
|
|
19
|
+
token: string | undefined;
|
|
20
|
+
netRetained: string;
|
|
21
|
+
}
|
|
22
|
+
/** Funds would flow to bundler3 contract addresses. Never bypassable. */
|
|
23
|
+
export declare class BlacklistViolationError extends SimulationPackageError {
|
|
24
|
+
readonly assetChanges?: RetainedAsset[] | undefined;
|
|
25
|
+
readonly code = "BLACKLIST_ERROR";
|
|
26
|
+
constructor(message: string, assetChanges?: RetainedAsset[] | undefined);
|
|
27
|
+
}
|
|
28
|
+
/** Sanctioned or compromised address detected. Never bypassable. */
|
|
29
|
+
export declare class AddressScreeningError extends SimulationPackageError {
|
|
30
|
+
readonly addresses: string[];
|
|
31
|
+
readonly code = "SCREENING_ERROR";
|
|
32
|
+
constructor(message: string, addresses: string[]);
|
|
33
|
+
}
|
|
34
|
+
/** Tenderly or RPC service is down. Bypassable — user can proceed. */
|
|
35
|
+
export declare class ExternalServiceError extends SimulationPackageError {
|
|
36
|
+
readonly code = "EXTERNAL_SERVICE_ERROR";
|
|
37
|
+
}
|
|
38
|
+
/** Bad input to the simulation functions. Not bypassable. */
|
|
39
|
+
export declare class SimulationValidationError extends SimulationPackageError {
|
|
40
|
+
readonly fieldErrors?: string[] | undefined;
|
|
41
|
+
readonly code = "VALIDATION_ERROR";
|
|
42
|
+
constructor(message: string, fieldErrors?: string[] | undefined);
|
|
43
|
+
}
|
|
44
|
+
/** Chain ID not configured for any simulation method. Not bypassable. */
|
|
45
|
+
export declare class UnsupportedChainError extends SimulationPackageError {
|
|
46
|
+
readonly chainId: number;
|
|
47
|
+
readonly code = "UNSUPPORTED_CHAIN";
|
|
48
|
+
constructor(chainId: number);
|
|
49
|
+
}
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnsupportedChainError = exports.SimulationValidationError = exports.ExternalServiceError = exports.AddressScreeningError = exports.BlacklistViolationError = exports.SimulationRevertedError = exports.SimulationPackageError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Base error for all simulation package errors.
|
|
6
|
+
* Transport-agnostic — no HTTP status codes.
|
|
7
|
+
* Consumers use try/catch + instanceof to handle specific error types.
|
|
8
|
+
*/
|
|
9
|
+
class SimulationPackageError extends Error {
|
|
10
|
+
constructor(message, options) {
|
|
11
|
+
super(message, options);
|
|
12
|
+
this.name = this.constructor.name;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.SimulationPackageError = SimulationPackageError;
|
|
16
|
+
/** Transaction would revert on-chain. Not bypassable. */
|
|
17
|
+
class SimulationRevertedError extends SimulationPackageError {
|
|
18
|
+
reason;
|
|
19
|
+
details;
|
|
20
|
+
code = "SIMULATION_REVERTED";
|
|
21
|
+
constructor(reason, details) {
|
|
22
|
+
super(reason ?? "Transaction simulation reverted");
|
|
23
|
+
this.reason = reason;
|
|
24
|
+
this.details = details;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.SimulationRevertedError = SimulationRevertedError;
|
|
28
|
+
/** Funds would flow to bundler3 contract addresses. Never bypassable. */
|
|
29
|
+
class BlacklistViolationError extends SimulationPackageError {
|
|
30
|
+
assetChanges;
|
|
31
|
+
code = "BLACKLIST_ERROR";
|
|
32
|
+
constructor(message, assetChanges) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.assetChanges = assetChanges;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.BlacklistViolationError = BlacklistViolationError;
|
|
38
|
+
/** Sanctioned or compromised address detected. Never bypassable. */
|
|
39
|
+
class AddressScreeningError extends SimulationPackageError {
|
|
40
|
+
addresses;
|
|
41
|
+
code = "SCREENING_ERROR";
|
|
42
|
+
constructor(message, addresses) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.addresses = addresses;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.AddressScreeningError = AddressScreeningError;
|
|
48
|
+
/** Tenderly or RPC service is down. Bypassable — user can proceed. */
|
|
49
|
+
class ExternalServiceError extends SimulationPackageError {
|
|
50
|
+
code = "EXTERNAL_SERVICE_ERROR";
|
|
51
|
+
}
|
|
52
|
+
exports.ExternalServiceError = ExternalServiceError;
|
|
53
|
+
/** Bad input to the simulation functions. Not bypassable. */
|
|
54
|
+
class SimulationValidationError extends SimulationPackageError {
|
|
55
|
+
fieldErrors;
|
|
56
|
+
code = "VALIDATION_ERROR";
|
|
57
|
+
constructor(message, fieldErrors) {
|
|
58
|
+
super(message);
|
|
59
|
+
this.fieldErrors = fieldErrors;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.SimulationValidationError = SimulationValidationError;
|
|
63
|
+
/** Chain ID not configured for any simulation method. Not bypassable. */
|
|
64
|
+
class UnsupportedChainError extends SimulationPackageError {
|
|
65
|
+
chainId;
|
|
66
|
+
code = "UNSUPPORTED_CHAIN";
|
|
67
|
+
constructor(chainId) {
|
|
68
|
+
super(`Chain ${chainId} is not configured for simulation`);
|
|
69
|
+
this.chainId = chainId;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.UnsupportedChainError = UnsupportedChainError;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { screenAddresses } from "./screen-addresses/index.js";
|
|
2
|
+
export { simulate } from "./simulate/index.js";
|
|
3
|
+
export type { ChainSimulationConfig, SimulateParams, SimulationAuthorization, SimulationConfig, SimulationLogger, SimulationResult, SimulationTransaction, TenderlyRestConfig, Transfer, } from "./types.js";
|
|
4
|
+
export { SimulationPackageError, SimulationRevertedError, BlacklistViolationError, AddressScreeningError, ExternalServiceError, SimulationValidationError, UnsupportedChainError, } from "./errors.js";
|
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnsupportedChainError = exports.SimulationValidationError = exports.ExternalServiceError = exports.AddressScreeningError = exports.BlacklistViolationError = exports.SimulationRevertedError = exports.SimulationPackageError = exports.simulate = exports.screenAddresses = void 0;
|
|
4
|
+
// Public fns (via feature-folder barrels)
|
|
5
|
+
var index_js_1 = require("./screen-addresses/index.js");
|
|
6
|
+
Object.defineProperty(exports, "screenAddresses", { enumerable: true, get: function () { return index_js_1.screenAddresses; } });
|
|
7
|
+
var index_js_2 = require("./simulate/index.js");
|
|
8
|
+
Object.defineProperty(exports, "simulate", { enumerable: true, get: function () { return index_js_2.simulate; } });
|
|
9
|
+
// Errors (for instanceof checks by consumers)
|
|
10
|
+
var errors_js_1 = require("./errors.js");
|
|
11
|
+
Object.defineProperty(exports, "SimulationPackageError", { enumerable: true, get: function () { return errors_js_1.SimulationPackageError; } });
|
|
12
|
+
Object.defineProperty(exports, "SimulationRevertedError", { enumerable: true, get: function () { return errors_js_1.SimulationRevertedError; } });
|
|
13
|
+
Object.defineProperty(exports, "BlacklistViolationError", { enumerable: true, get: function () { return errors_js_1.BlacklistViolationError; } });
|
|
14
|
+
Object.defineProperty(exports, "AddressScreeningError", { enumerable: true, get: function () { return errors_js_1.AddressScreeningError; } });
|
|
15
|
+
Object.defineProperty(exports, "ExternalServiceError", { enumerable: true, get: function () { return errors_js_1.ExternalServiceError; } });
|
|
16
|
+
Object.defineProperty(exports, "SimulationValidationError", { enumerable: true, get: function () { return errors_js_1.SimulationValidationError; } });
|
|
17
|
+
Object.defineProperty(exports, "UnsupportedChainError", { enumerable: true, get: function () { return errors_js_1.UnsupportedChainError; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { screenAddresses } from "./screen-addresses.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.screenAddresses = void 0;
|
|
4
|
+
var screen_addresses_js_1 = require("./screen-addresses.js");
|
|
5
|
+
Object.defineProperty(exports, "screenAddresses", { enumerable: true, get: function () { return screen_addresses_js_1.screenAddresses; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static list of sanctioned/compromised addresses.
|
|
3
|
+
* Ported from offchain-services-server/src/functions/screen/blacklist.ts.
|
|
4
|
+
* These addresses are checked before calling the Chainalysis API (fast, no network).
|
|
5
|
+
*
|
|
6
|
+
* **Invariant** (enforced at module load, see below): every entry is a
|
|
7
|
+
* well-formed lowercased EVM address. `screenAddresses` does
|
|
8
|
+
* `sanctionedAddresses.has(addr.toLowerCase())` — a mixed-case entry would
|
|
9
|
+
* never match and would silently bypass the sanctioned check.
|
|
10
|
+
*/
|
|
11
|
+
export declare const sanctionedAddresses: Set<string>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanctionedAddresses = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Static list of sanctioned/compromised addresses.
|
|
6
|
+
* Ported from offchain-services-server/src/functions/screen/blacklist.ts.
|
|
7
|
+
* These addresses are checked before calling the Chainalysis API (fast, no network).
|
|
8
|
+
*
|
|
9
|
+
* **Invariant** (enforced at module load, see below): every entry is a
|
|
10
|
+
* well-formed lowercased EVM address. `screenAddresses` does
|
|
11
|
+
* `sanctionedAddresses.has(addr.toLowerCase())` — a mixed-case entry would
|
|
12
|
+
* never match and would silently bypass the sanctioned check.
|
|
13
|
+
*/
|
|
14
|
+
exports.sanctionedAddresses = new Set([
|
|
15
|
+
// Bybit hack addresses
|
|
16
|
+
"0x47666Fab8bd0Ac7003bce3f5C3585383F09486E2",
|
|
17
|
+
"0xA4B2Fd68593B6F34E51cB9eDB66E71c1B4Ab449e",
|
|
18
|
+
"0x36ed3C0213565530C35115d93A80F9c04d94E4Cb",
|
|
19
|
+
"0x1542368a03ad1f03d96D51B414f4738961Cf4443",
|
|
20
|
+
"0xdD90071D52F20e85c89802e5Dc1eC0A7B6475f92",
|
|
21
|
+
"0x40e98FeEEbaD7Ddb0F0534Ccaa617427eA10187e",
|
|
22
|
+
"0x30a822CDD2782D2B2A12a08526452e885978FA1D",
|
|
23
|
+
"0x6d46bd3AfF100f23C194e5312f93507978a6DC91",
|
|
24
|
+
"0x660BfcEa3A5FAF823e8f8bF57dd558db034dea1d",
|
|
25
|
+
"0x140c9Ab92347734641b1A7c124ffDeE58c20C3E3",
|
|
26
|
+
"0x8c7235e1A6EeF91b980D0FcA083347FBb7EE1806",
|
|
27
|
+
"0xb172F7e99452446f18FF49A71bfEeCf0873003b4",
|
|
28
|
+
"0xCd7eC020121Ead6f99855cbB972dF502dB5bC63a",
|
|
29
|
+
"0x0e8C1E2881F35Ef20343264862A242FB749d6b35",
|
|
30
|
+
"0x2290937A4498C96eFfb87b8371a33D108F8D433f",
|
|
31
|
+
"0x1bb0970508316DC735329752a4581E0a4bAbc6B4",
|
|
32
|
+
"0xf0a16603289eAF35F64077Ba3681af41194a1c09",
|
|
33
|
+
"0x5Af75eAB6BEC227657fA3E749a8BFd55f02e4b1D",
|
|
34
|
+
"0xB4a862A81aBB2f952FcA4C6f5510962e18c7f1A2",
|
|
35
|
+
"0x959c4CA19c4532C97A657D82d97acCBAb70e6fb4",
|
|
36
|
+
"0xB72334cB9D0b614D30C4c60e2bd12fF5Ed03c305",
|
|
37
|
+
"0xFc926659Dd8808f6e3e0a8d61B20B871F3Fa6465",
|
|
38
|
+
"0xfa3FcCCB897079fD83bfBA690E7D47Eb402d6c49",
|
|
39
|
+
"0x51E9d833Ecae4E8D9D8Be17300AEE6D3398C135D",
|
|
40
|
+
"0x1eB27f136BFe7947f80d6ceE3Cf0bfDf92b45e57",
|
|
41
|
+
"0x83Ef5E80faD88288F770152875Ab0bb16641a09E",
|
|
42
|
+
"0x52207Ec7B1b43AA5DB116931a904371ae2C1619e",
|
|
43
|
+
"0x23Db729908137cb60852f2936D2b5c6De0e1c887",
|
|
44
|
+
"0xAF620E6d32B1c67f3396EF5d2F7d7642Dc2e6CE9",
|
|
45
|
+
"0xCd1a4A457cA8b0931c3BF81Df3CFa227ADBdb6E9",
|
|
46
|
+
"0x96244D83DC15d36847C35209bBDc5bdDE9bEc3D8",
|
|
47
|
+
"0x09278b36863bE4cCd3d0c22d643E8062D7a11377",
|
|
48
|
+
"0x3A21F4E6Bbe527D347ca7c157F4233c935779847",
|
|
49
|
+
"0xBC3e5e8C10897a81b63933348f53f2e052F89a7E",
|
|
50
|
+
"0x9271EDdda0F0f2bB7b1A0c712bdF8dbD0A38d1Ab",
|
|
51
|
+
"0x4C198B3B5F3a4b1Aa706daC73D826c2B795ccd67",
|
|
52
|
+
"0x684d4b58Dc32af786BF6D572A792fF7A883428B9",
|
|
53
|
+
"0xD3C611AeD139107DEC2294032da3913BC26507fb",
|
|
54
|
+
"0x9eF42873Ae015AA3da0c4354AeF94a18D2B3407b",
|
|
55
|
+
"0xbdE2Cc5375fa9E0383309A2cA31213f2D6cabcbd",
|
|
56
|
+
"0xe69753Ddfbedbd249E703EB374452E78dae1ae49",
|
|
57
|
+
"0xE9bc552fdFa54b30296d95F147e3e0280FF7f7e6",
|
|
58
|
+
"0xBCA02B395747D62626a65016F2e64A20bd254A39",
|
|
59
|
+
"0xF302572594a68aA8F951faE64ED3aE7DA41c72Be",
|
|
60
|
+
"0x21032176B43d9f7E9410fB37290a78f4fEd6044C",
|
|
61
|
+
"0xD5b58Cf7813c1eDC412367b97876bD400ea5c489",
|
|
62
|
+
"0xA5A023E052243b7cce34Cbd4ba20180e8Dea6Ad6",
|
|
63
|
+
"0x723a7084028421994d4a7829108D63aB44658315",
|
|
64
|
+
"0x1512fcb09463A61862B73ec09B9b354aF1790268",
|
|
65
|
+
"0xEB0bAA3A556586192590CAD296b1e48dF62a8549",
|
|
66
|
+
"0xf03AfB1c6A11A7E370920ad42e6eE735dBedF0b1",
|
|
67
|
+
"0x55CCa2f5eB07907696afe4b9Db5102bcE5feB734",
|
|
68
|
+
].map((a) => a.toLowerCase()));
|
|
69
|
+
// Runtime invariant check at module load. Catches a future edit that adds an
|
|
70
|
+
// entry via `sanctionedAddresses.add(...)` bypassing the `.map(toLowerCase)` or
|
|
71
|
+
// inserts a malformed string. Throwing at load is the loudest signal — the
|
|
72
|
+
// package won't import if the dataset is invalid.
|
|
73
|
+
for (const entry of exports.sanctionedAddresses) {
|
|
74
|
+
if (!/^0x[0-9a-f]{40}$/.test(entry)) {
|
|
75
|
+
throw new Error(`sanctioned-addresses.ts: entry "${entry}" is not a well-formed lowercased EVM address. Every entry must match /^0x[0-9a-f]{40}$/ so lowercase lookup matches.`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SimulationLogger, SimulationTransaction, Transfer } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Screen all relevant addresses from a simulation for sanctioned/compromised addresses.
|
|
4
|
+
*
|
|
5
|
+
* Screening targets (deduped, lowercased, zero-addr removed):
|
|
6
|
+
* - Transfer senders and recipients from simulation result
|
|
7
|
+
* - `from` and `to` of every tx in `simulationTxs` (catches native-value flows)
|
|
8
|
+
*
|
|
9
|
+
* Checks run in order:
|
|
10
|
+
* 1. Static {@link sanctionedAddresses} set (fast, no network, always on).
|
|
11
|
+
* 2. Chainalysis Entity API — one lookup per address, lookups across addresses
|
|
12
|
+
* run in parallel. Only fires if `chainalysisApiKey` is provided.
|
|
13
|
+
* **Fail-open on transport errors before registration** (network / timeout /
|
|
14
|
+
* non-ok POST): the address is treated as clean, a warn is logged, and the
|
|
15
|
+
* simulation proceeds. This is the documented posture — a Chainalysis
|
|
16
|
+
* outage must not block all simulations.
|
|
17
|
+
* **Fail-closed on partial registration**: if the registration POST succeeded
|
|
18
|
+
* but the subsequent lookup GET fails, the address is treated as Severe.
|
|
19
|
+
* The data already persisted server-side, so failing open at this point
|
|
20
|
+
* would let a pass slip through based on a half-completed check.
|
|
21
|
+
* **Fail-closed on schema drift**: if Chainalysis returns an unknown risk
|
|
22
|
+
* tier or a body that doesn't parse, the address is treated as Severe so a
|
|
23
|
+
* future stricter tier isn't silently downgraded.
|
|
24
|
+
*
|
|
25
|
+
* Throws `AddressScreeningError` on any Severe hit. Intended to be called after
|
|
26
|
+
* `simulate`, passing the returned `simulationTxs` and `transfers`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function screenAddresses(params: {
|
|
29
|
+
simulationTxs: SimulationTransaction[];
|
|
30
|
+
transfers: Transfer[];
|
|
31
|
+
chainalysisApiKey?: string;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
logger?: SimulationLogger;
|
|
34
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.screenAddresses = screenAddresses;
|
|
4
|
+
const viem_1 = require("viem");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const errors_js_1 = require("../errors.js");
|
|
7
|
+
const sanctioned_addresses_js_1 = require("./sanctioned-addresses.js");
|
|
8
|
+
const CHAINALYSIS_API_URL = "https://api.chainalysis.com/api/risk/v2/entities";
|
|
9
|
+
const CHAINALYSIS_TIMEOUT_MS = 5_000;
|
|
10
|
+
/**
|
|
11
|
+
* Known tiers whose presence means NOT severe. Any other value (unknown future
|
|
12
|
+
* tier, malformed field) is treated as Severe so a stricter tier Chainalysis
|
|
13
|
+
* might introduce later isn't silently downgraded to clean.
|
|
14
|
+
*/
|
|
15
|
+
const NON_SEVERE_TIERS = new Set(["High", "Medium", "Low"]);
|
|
16
|
+
const chainalysisEntitySchema = zod_1.z
|
|
17
|
+
.object({
|
|
18
|
+
risk: zod_1.z.string().nullable(),
|
|
19
|
+
})
|
|
20
|
+
.passthrough();
|
|
21
|
+
/**
|
|
22
|
+
* Screen all relevant addresses from a simulation for sanctioned/compromised addresses.
|
|
23
|
+
*
|
|
24
|
+
* Screening targets (deduped, lowercased, zero-addr removed):
|
|
25
|
+
* - Transfer senders and recipients from simulation result
|
|
26
|
+
* - `from` and `to` of every tx in `simulationTxs` (catches native-value flows)
|
|
27
|
+
*
|
|
28
|
+
* Checks run in order:
|
|
29
|
+
* 1. Static {@link sanctionedAddresses} set (fast, no network, always on).
|
|
30
|
+
* 2. Chainalysis Entity API — one lookup per address, lookups across addresses
|
|
31
|
+
* run in parallel. Only fires if `chainalysisApiKey` is provided.
|
|
32
|
+
* **Fail-open on transport errors before registration** (network / timeout /
|
|
33
|
+
* non-ok POST): the address is treated as clean, a warn is logged, and the
|
|
34
|
+
* simulation proceeds. This is the documented posture — a Chainalysis
|
|
35
|
+
* outage must not block all simulations.
|
|
36
|
+
* **Fail-closed on partial registration**: if the registration POST succeeded
|
|
37
|
+
* but the subsequent lookup GET fails, the address is treated as Severe.
|
|
38
|
+
* The data already persisted server-side, so failing open at this point
|
|
39
|
+
* would let a pass slip through based on a half-completed check.
|
|
40
|
+
* **Fail-closed on schema drift**: if Chainalysis returns an unknown risk
|
|
41
|
+
* tier or a body that doesn't parse, the address is treated as Severe so a
|
|
42
|
+
* future stricter tier isn't silently downgraded.
|
|
43
|
+
*
|
|
44
|
+
* Throws `AddressScreeningError` on any Severe hit. Intended to be called after
|
|
45
|
+
* `simulate`, passing the returned `simulationTxs` and `transfers`.
|
|
46
|
+
*/
|
|
47
|
+
async function screenAddresses(params) {
|
|
48
|
+
const { simulationTxs, transfers, chainalysisApiKey, signal, logger } = params;
|
|
49
|
+
const addressesToScreen = new Set();
|
|
50
|
+
for (const transfer of transfers) {
|
|
51
|
+
addressesToScreen.add(transfer.from.toLowerCase());
|
|
52
|
+
addressesToScreen.add(transfer.to.toLowerCase());
|
|
53
|
+
}
|
|
54
|
+
for (const tx of simulationTxs) {
|
|
55
|
+
addressesToScreen.add(tx.from.toLowerCase());
|
|
56
|
+
addressesToScreen.add(tx.to.toLowerCase());
|
|
57
|
+
}
|
|
58
|
+
addressesToScreen.delete(viem_1.zeroAddress);
|
|
59
|
+
// 1. Static sanctioned list — always on, no network.
|
|
60
|
+
const sanctioned = [];
|
|
61
|
+
for (const addr of addressesToScreen) {
|
|
62
|
+
if (sanctioned_addresses_js_1.sanctionedAddresses.has(addr))
|
|
63
|
+
sanctioned.push((0, viem_1.getAddress)(addr));
|
|
64
|
+
}
|
|
65
|
+
if (sanctioned.length > 0) {
|
|
66
|
+
throw new errors_js_1.AddressScreeningError("Transaction involves sanctioned addresses", sanctioned);
|
|
67
|
+
}
|
|
68
|
+
// 2. Chainalysis (optional).
|
|
69
|
+
if (!chainalysisApiKey) {
|
|
70
|
+
logger?.warn("Chainalysis screening skipped: no API key configured");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const results = await Promise.allSettled([...addressesToScreen].map(async (addr) => {
|
|
74
|
+
const verdict = await screenWithChainalysis(addr, chainalysisApiKey, signal);
|
|
75
|
+
return { addr, verdict };
|
|
76
|
+
}));
|
|
77
|
+
const flagged = [];
|
|
78
|
+
for (const r of results) {
|
|
79
|
+
if (r.status === "fulfilled") {
|
|
80
|
+
if (r.value.verdict === "severe")
|
|
81
|
+
flagged.push((0, viem_1.getAddress)(r.value.addr));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Transport fail-open: registration POST couldn't even complete, so the
|
|
85
|
+
// address was never registered with Chainalysis and the package's
|
|
86
|
+
// fail-open posture applies.
|
|
87
|
+
logger?.warn("Chainalysis screening failed (fail-open)", {
|
|
88
|
+
error: r.reason instanceof Error ? r.reason.message : String(r.reason),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (flagged.length > 0) {
|
|
93
|
+
throw new errors_js_1.AddressScreeningError("Transaction involves addresses flagged as Severe risk", flagged);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Run the Chainalysis Entity flow for one address.
|
|
98
|
+
*
|
|
99
|
+
* Returns `'severe'` or `'clean'`. Throws only if the initial registration POST
|
|
100
|
+
* fails before any server-side side effect — then the outer handler treats the
|
|
101
|
+
* rejection as fail-open. After registration succeeds, every error path returns
|
|
102
|
+
* `'severe'` so the check fails closed.
|
|
103
|
+
*
|
|
104
|
+
* A SINGLE deadline covers both the POST and the GET, keeping the per-address
|
|
105
|
+
* wall-time budget at `CHAINALYSIS_TIMEOUT_MS` (5 s) rather than doubling it.
|
|
106
|
+
*/
|
|
107
|
+
async function screenWithChainalysis(address, apiKey, parentSignal) {
|
|
108
|
+
const headers = {
|
|
109
|
+
"Content-Type": "application/json",
|
|
110
|
+
token: apiKey,
|
|
111
|
+
};
|
|
112
|
+
const deadline = AbortSignal.timeout(CHAINALYSIS_TIMEOUT_MS);
|
|
113
|
+
const signal = parentSignal
|
|
114
|
+
? AbortSignal.any([parentSignal, deadline])
|
|
115
|
+
: deadline;
|
|
116
|
+
// POST to register. Pre-registration failure → fail-open via thrown Error.
|
|
117
|
+
const registration = await fetch(CHAINALYSIS_API_URL, {
|
|
118
|
+
headers,
|
|
119
|
+
method: "POST",
|
|
120
|
+
body: JSON.stringify({ address }),
|
|
121
|
+
signal,
|
|
122
|
+
});
|
|
123
|
+
if (!registration.ok) {
|
|
124
|
+
throw new Error(`Chainalysis registration failed: ${registration.status} ${registration.statusText}`);
|
|
125
|
+
}
|
|
126
|
+
// From here on we've already told Chainalysis about the address — fail closed.
|
|
127
|
+
let response;
|
|
128
|
+
try {
|
|
129
|
+
response = await fetch(`${CHAINALYSIS_API_URL}/${encodeURIComponent(address)}`, {
|
|
130
|
+
method: "GET",
|
|
131
|
+
headers,
|
|
132
|
+
signal,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return "severe";
|
|
137
|
+
}
|
|
138
|
+
if (!response.ok)
|
|
139
|
+
return "severe";
|
|
140
|
+
let parsed;
|
|
141
|
+
try {
|
|
142
|
+
parsed = chainalysisEntitySchema.parse(await response.json());
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return "severe";
|
|
146
|
+
}
|
|
147
|
+
if (parsed.risk === null)
|
|
148
|
+
return "clean";
|
|
149
|
+
if (NON_SEVERE_TIERS.has(parsed.risk))
|
|
150
|
+
return "clean";
|
|
151
|
+
return "severe";
|
|
152
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateAuthorizations = exports.resolveAuthorizations = void 0;
|
|
4
|
+
var resolve_js_1 = require("./resolve.js");
|
|
5
|
+
Object.defineProperty(exports, "resolveAuthorizations", { enumerable: true, get: function () { return resolve_js_1.resolveAuthorizations; } });
|
|
6
|
+
var validate_js_1 = require("./validate.js");
|
|
7
|
+
Object.defineProperty(exports, "validateAuthorizations", { enumerable: true, get: function () { return validate_js_1.validateAuthorizations; } });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Address } from "viem";
|
|
2
|
+
import type { SimulationAuthorization, SimulationTransaction } from "../../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve SimulationAuthorization[] into SimulationTransaction[].
|
|
5
|
+
*
|
|
6
|
+
* - "approval" → use authorization.transaction as-is
|
|
7
|
+
* - "signature" → encode approve(spender, amount) on the token contract
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveAuthorizations(authorizations: SimulationAuthorization[], sender: Address): SimulationTransaction[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveAuthorizations = resolveAuthorizations;
|
|
4
|
+
const viem_1 = require("viem");
|
|
5
|
+
/**
|
|
6
|
+
* Resolve SimulationAuthorization[] into SimulationTransaction[].
|
|
7
|
+
*
|
|
8
|
+
* - "approval" → use authorization.transaction as-is
|
|
9
|
+
* - "signature" → encode approve(spender, amount) on the token contract
|
|
10
|
+
*/
|
|
11
|
+
function resolveAuthorizations(authorizations, sender) {
|
|
12
|
+
return authorizations.map((auth) => {
|
|
13
|
+
if (auth.type === "approval") {
|
|
14
|
+
return auth.transaction;
|
|
15
|
+
}
|
|
16
|
+
const amount = auth.amount ?? viem_1.maxUint256;
|
|
17
|
+
const data = (0, viem_1.encodeFunctionData)({
|
|
18
|
+
abi: viem_1.erc20Abi,
|
|
19
|
+
functionName: "approve",
|
|
20
|
+
args: [auth.spender, amount],
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
from: sender,
|
|
24
|
+
to: auth.token,
|
|
25
|
+
data,
|
|
26
|
+
value: 0n,
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SimulationAuthorization } from "../../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validate all signature authorizations have non-zero token and spender.
|
|
4
|
+
* Returns an array of error strings (empty if valid).
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateAuthorizations(authorizations: SimulationAuthorization[]): string[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateAuthorizations = validateAuthorizations;
|
|
4
|
+
const viem_1 = require("viem");
|
|
5
|
+
/**
|
|
6
|
+
* Validate all signature authorizations have non-zero token and spender.
|
|
7
|
+
* Returns an array of error strings (empty if valid).
|
|
8
|
+
*/
|
|
9
|
+
function validateAuthorizations(authorizations) {
|
|
10
|
+
const errors = [];
|
|
11
|
+
for (let i = 0; i < authorizations.length; i++) {
|
|
12
|
+
const auth = authorizations[i];
|
|
13
|
+
if (auth.type === "signature") {
|
|
14
|
+
if (!auth.token || auth.token === viem_1.zeroAddress) {
|
|
15
|
+
errors.push(`authorizations[${i}]: signature authorization has zero-address token`);
|
|
16
|
+
}
|
|
17
|
+
if (!auth.spender || auth.spender === viem_1.zeroAddress) {
|
|
18
|
+
errors.push(`authorizations[${i}]: signature authorization has zero-address spender`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return errors;
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type BlockTag } from "viem";
|
|
2
|
+
import type { RawSimulationResult, SimulationTransaction } from "../../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Simulate transactions using eth_simulateV1 (viem simulateCalls).
|
|
5
|
+
*
|
|
6
|
+
* Uses stateOverride to set the user's ETH balance high enough to avoid
|
|
7
|
+
* false "insufficient balance for gas" reverts.
|
|
8
|
+
*/
|
|
9
|
+
export declare function simulateV1(params: {
|
|
10
|
+
rpcUrl: string;
|
|
11
|
+
chainId: number;
|
|
12
|
+
transactions: SimulationTransaction[];
|
|
13
|
+
blockNumber?: bigint | BlockTag;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}): Promise<RawSimulationResult>;
|