import 'dotenv/config'; import { ICache } from './dex-helper/icache'; interface StateObject { storage?: Record; balance?: string; nonce?: number; code?: string; } export type StateOverride = Record; interface TokenStorageSlots { balanceSlot: string; allowanceSlot: string; isVyper?: boolean; isSolady?: boolean; partition?: string; stateProxy?: string; additionalOverrides?: StateOverride; } interface FoundSlot { slot: string; isVyper?: boolean; isSolady?: boolean; partition?: string; stateProxy?: string; } interface SimulateTransactionRequest { from: string | null; to: string | null; value?: string; data: string; chainId: number; timestamp?: number; blockNumber?: number; stateOverride?: StateOverride; } interface SimulatedTransactionCall { hash: string; contract_name: string; function_name: string; function_pc: number; function_op: string; function_file_index: number; function_code_start: number; function_line_number: number; function_code_length: number; absolute_position: number; caller_pc: number; caller_op: string; call_type: string; address: string; from: string; from_balance: string; to: string; to_balance: string; value: string | null; caller: { address: string; balance: string; }; block_timestamp: string; gas: number; gas_used: number; intrinsic_gas: number; storage_address: string; input: string; output: string; storage_slot: string[] | undefined; calls: SimulatedTransactionCall[] | null; } type SimulatedTransactionCallWithParent = SimulatedTransactionCall & { parentCall: SimulatedTransactionCall | null; }; interface Simulation { id: string; project_id: string; owner_id: string; network_id: string; block_number: number; transaction_index: number; from: string; to: string; input: string; gas: number; gas_price: string; gas_used: number; value: string; method: string; status: boolean; } interface SimulatedTransactionDetails { transaction: { hash: string; block_hash: string; block_number: number; from: string; gas: number; gas_price: number; gas_fee_cap: number; gas_tip_cap: number; cumulative_gas_used: number; gas_used: number; effective_gas_price: number; input: string; nonce: number; to: string; index: number; value: string; access_list: null; status: boolean; transaction_info: { call_trace: SimulatedTransactionCall; }; call_trace: SimulatedTransactionCall[]; }; } export type SimulatedTransaction = Pick; export type SimulationResult = { transaction: SimulatedTransaction; simulation: Simulation; }; export declare class TenderlySimulator { static readonly DEFAULT_OWNER = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; static readonly DEFAULT_SPENDER = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; static readonly SLOT_VERIFICATION_AMOUNT = 123456789012345678901234567n; static readonly SOLADY_BALANCE_SLOT_SEED = "0x87a211a2"; static readonly SOLADY_ALLOWANCE_SLOT_SEED = "0x7f5e9f20"; static readonly B20_BALANCES_SLOT = "0xc78b71fee795ddd74aff64ea9b2474194c938c3196430e10bb5f01ed48434004"; static readonly B20_ALLOWANCES_SLOT = "0xc78b71fee795ddd74aff64ea9b2474194c938c3196430e10bb5f01ed48434005"; private static instance; private constructor(); static getInstance(cache?: ICache, tenderlyToken?: string, tenderlyProject?: string, tenderlyAccountId?: string): TenderlySimulator; simulateTransaction(request: SimulateTransactionRequest, forceSimulationAPI?: boolean): Promise; private simulateWithTenderlySimulationAPI; private simulateWithTenderlyVNet; getSimulatedTransactionDetails(id: string): Promise; getSLOADCalls: (callTrace: SimulatedTransactionCall, parentCall?: SimulatedTransactionCall | null) => SimulatedTransactionCallWithParent[]; /** * * @param balanceOfSlot storage slot of `balanceOf` mapping (Solady slot seed if `isSolady`) * @param owner account's address * @param isVyper `true` if contract is written in Vyper * @param isSolady `true` if contract uses Solady ERC20 storage layout * @param partition partition key if contract uses partitioned storage layout */ calculateAddressBalanceSlot(balanceOfSlot: string, owner: string, isVyper?: boolean, isSolady?: boolean, partition?: string): string; /** * * @param balanceOfSlot storage slot of `balanceOf` mapping * @param owner account's address */ calculateSolidityAddressBalanceSlot(balanceOfSlot: string, owner: string): string; /** * * @param balanceOfSlot storage slot of `balanceOf` mapping * @param owner account's address */ calculateVyperAddressBalanceSlot(balanceOfSlot: string, owner: string): string; /** * Solady ERC20 stores balances at `keccak256(owner ++ seed)` where the seed * is the 12-byte-padded `_BALANCE_SLOT_SEED` library constant * @param balanceSlotSeed Solady balance slot seed * @param owner account's address */ calculateSoladyAddressBalanceSlot(balanceSlotSeed: string, owner: string): string; /** * Partitioned layout (e.g. ViciERC20's `balances[partition][owner]`): * balance is stored at `keccak256(owner ++ keccak256(partition ++ slot))` * @param balanceOfSlot storage slot of the partitioned balances mapping * @param owner account's address * @param partition partition key (item/token id) as a 32-byte value */ calculatePartitionedAddressBalanceSlot(balanceOfSlot: string, owner: string, partition: string): string; /** * * @param allowanceSlot storage slot of `allowance` mapping (Solady slot seed if `isSolady`) * @param owner account's address * @param spender spender's address * @param isVyper `true` if contract is written in Vyper * @param isSolady `true` if contract uses Solady ERC20 storage layout * @param partition partition key if contract uses partitioned storage layout */ calculateAddressAllowanceSlot(allowanceSlot: string, owner: string, spender: string, isVyper?: boolean, isSolady?: boolean, partition?: string): string; /** * * @param allowanceSlot storage slot of `allowance` mapping * @param owner account's address * @param spender spender's address */ calculateSolidityAddressAllowanceSlot(allowanceSlot: string, owner: string, spender: string): string; /** * * @param allowanceSlot storage slot of `allowance` mapping * @param owner account's address * @param spender spender's address */ calculateVyperAddressAllowanceSlot(allowanceSlot: string, owner: string, spender: string): string; /** * Solady ERC20 stores allowances at `keccak256(owner ++ seed ++ spender)` * where the seed is the 12-byte-padded `_ALLOWANCE_SLOT_SEED` library constant * @param allowanceSlotSeed Solady allowance slot seed * @param owner account's address * @param spender spender's address */ calculateSoladyAddressAllowanceSlot(allowanceSlotSeed: string, owner: string, spender: string): string; /** * Partitioned layout (e.g. ViciERC20's `allowances[owner][partition][spender]`): * allowance is stored at * `keccak256(spender ++ keccak256(partition ++ keccak256(owner ++ slot)))` * @param allowanceSlot storage slot of the partitioned allowances mapping * @param owner account's address * @param spender spender's address * @param partition partition key (item/token id) as a 32-byte value */ calculatePartitionedAddressAllowanceSlot(allowanceSlot: string, owner: string, spender: string, partition: string): string; buildBalanceOfSimulationRequest(chainId: number, token: string, owner: string): SimulateTransactionRequest; buildAllowanceSimulationRequest(chainId: number, token: string, owner: string, spender: string): SimulateTransactionRequest; private decodeUintOutput; /** * Simulates the given read call with the state override applied and checks * that the override actually controls the returned value: the call must * succeed and return a non-zero value different from the baseline. * With `allowScaled` an exact match with `writtenValue` is not required — * tokens with derived balances (e.g. stETH shares, aToken scaled balances) * return a scaled value. Without it, only an exact match passes */ private simulateAndCheckOutput; /** * Verifies a candidate `balanceOf` mapping slot by overriding the derived * slot with a distinctive value and checking that `balanceOf` reflects it * @param chainId token chain id * @param token token address * @param foundSlot candidate slot to verify * @param baselineValue `balanceOf` result without the override applied */ verifyTokenBalanceSlot(chainId: number, token: string, foundSlot: FoundSlot, baselineValue?: bigint): Promise; /** * Verifies a candidate `allowance` mapping slot by overriding the derived * slot with a distinctive value and checking that `allowance` reflects it * @param chainId token chain id * @param token token address * @param foundSlot candidate slot to verify * @param baselineValue `allowance` result without the override applied */ verifyTokenAllowanceSlot(chainId: number, token: string, foundSlot: FoundSlot, baselineValue?: bigint): Promise; /** * Builds a `FoundSlot` from a matched SLOAD. `storage_address` is where the * read actually happened: if it differs from the token, the state lives on * an external contract (state proxy) and overrides must target it */ private buildFoundSlot; /** * Finds the slot of the `balanceOf` mapping in given token contract's storage. * Supports `Solidity` and `Vyper` contracts. * Found slots are verified with an additional simulation before being returned * @param chainId token chain id * @param token token address */ findTokenBalanceOfSlot(chainId: number, token: string): Promise; /** * Finds the slot of the `allowance` mapping in given token contract's storage. * Supports `Solidity` and `Vyper` contracts. * Found slots are verified with an additional simulation before being returned * @param chainId token chain id * @param token token address */ findTokenAllowanceSlot(chainId: number, token: string): Promise; /** * Returns storage slots for the given token contract. * @param chainId Token chain ID * @param token Token address. Doesn't have to be normalized */ getTokenStorageSlots(chainId: number, token: string): Promise; /** * Adds native balance override to an existing `StateOverride` object * @param stateOverride object to add the override to * @param account address to be given the balance * @param amount token amount in wei */ addBalanceOverride(stateOverride: StateOverride, account: string, amount: bigint): void; /** * Adds token balance override to an existing `StateOverride` object * @param stateOverride object to add the override to * @param chainId token chain ID * @param token token address * @param account address to be given the balance * @param amount token amount in wei */ addTokenBalanceOverride(stateOverride: StateOverride, chainId: number, token: string, account: string, amount: bigint): Promise; /** * Adds token allowance override to an existing `StateOverride` object * @param stateOverride object to add the override to * @param chainId token chain ID * @param token token address * @param account owner address * @param spender spender address * @param amount token amount in wei */ addAllowanceOverride(stateOverride: StateOverride, chainId: number, token: string, account: string, spender: string, amount: bigint): Promise; } export {};