Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

estimate gnosis safe message with all gas left in the block #1224

Merged
merged 4 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions app/ts/background/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { getActiveAddressEntry, getAddressBookEntriesForVisualiser, identifyAddr
import { getActiveAddress, sendPopupMessageToOpenWindows } from './backgroundUtils.js'
import { DistributiveOmit, assertNever, assertUnreachable, modifyObject } from '../utils/typescript.js'
import { EthereumClientService } from '../simulation/services/EthereumClientService.js'
import { appendTransaction, calculateRealizedEffectiveGasPrice, copySimulationState, fixNonceErrorsIfNeeded, getAddressToMakeRich, getBaseFeeAdjustedTransactions, getNonceFixedSimulatedTransactions, getTokenBalancesAfter, getWebsiteCreatedEthereumUnsignedTransactions, mockSignTransaction, setSimulationTransactionsAndSignedMessages, simulateEstimateGas } from '../simulation/services/SimulationModeEthereumClientService.js'
import { appendTransaction, calculateRealizedEffectiveGasPrice, copySimulationState, fixNonceErrorsIfNeeded, getAddressToMakeRich, getBaseFeeAdjustedTransactions, getNonceFixedSimulatedTransactions, getTokenBalancesAfter, getWebsiteCreatedEthereumUnsignedTransactions, mockSignTransaction, setSimulationTransactionsAndSignedMessages, simulationGasLeft } from '../simulation/services/SimulationModeEthereumClientService.js'
import { Semaphore } from '../utils/semaphore.js'
import { JsonRpcResponseError, handleUnexpectedError, isFailedToFetchError, isNewBlockAbort, printError } from '../utils/errors.js'
import { formSimulatedAndVisualizedTransaction } from '../components/formVisualizerResults.js'
Expand Down Expand Up @@ -102,7 +102,7 @@ export const simulateGovernanceContractExecution = async (pendingTransaction: Pe

if (tokenBalancesAfter[0] === undefined) return returnError('Could not compute token balances')

const governanceContractSimulationState: SimulationState = {
const governanceContractSimulationState: SimulationState = {
addressToMakeRich: undefined,
simulatedTransactions: [{
preSimulationTransaction: {
Expand Down Expand Up @@ -145,8 +145,7 @@ export const simulateGnosisSafeMetaTransaction = async (gnosisSafeMessage: Visua
nonce: 0n,
chainId: ethereumClientService.getChainId(),
}
const gasLimit = gnosisSafeMessage.message.message.baseGas !== 0n ? { gas: gnosisSafeMessage.message.message.baseGas } : await simulateEstimateGas(ethereumClientService, undefined, simulationState, transactionWithoutGas)
if ('error' in gasLimit) return returnError(gasLimit.error.message)
const gasLimit = gnosisSafeMessage.message.message.baseGas !== 0n ? { gas: gnosisSafeMessage.message.message.baseGas } : { gas: simulationGasLeft(simulationState, await ethereumClientService.getBlock(undefined)) }
const transaction = { ...transactionWithoutGas, gas: gasLimit.gas }
const metaTransaction: WebsiteCreatedEthereumUnsignedTransaction = {
website: gnosisSafeMessage.website,
Expand Down
3 changes: 2 additions & 1 deletion app/ts/simulation/compoundGovernanceFaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ export const simulateCompoundGovernanceExecution = async (ethereumClientService:
]
const parentBlock = await ethereumClientService.getBlock(undefined)
if (parentBlock === null) throw new Error('The latest block is null')
const governanceContractCalls = (await ethereumClientService.simulateTransactionsAndSignatures(calls, [], parentBlock.number, undefined)).calls
const governanceContractCalls = (await ethereumClientService.simulateTransactionsAndSignatures([calls], [], parentBlock.number, undefined))[0]?.calls
if (governanceContractCalls === undefined) throw new Error('simulateTransactionsAndSignatures returned zero length aray')
for (const call of governanceContractCalls) {
if (call.status !== 'success') throw new Error('Failed to retrieve governance contracts information')
}
Expand Down
43 changes: 24 additions & 19 deletions app/ts/simulation/services/EthereumClientService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,12 @@ export class EthereumClientService {
return EthSimulateV1Result.parse(unvalidatedResult)
}

public readonly simulateTransactionsAndSignatures = async (transactions: readonly OptionalEthereumUnsignedTransaction[], signatures: readonly SignatureWithFakeSignerAddress[], blockNumber: bigint, requestAbortController: AbortController | undefined, extraAccountOverrides: StateOverrides = {}) => {
const transactionsWithRemoveZeroPricedOnes = transactions.map((transaction) => {
public readonly simulateTransactionsAndSignatures = async (transactionsInBlocks: readonly OptionalEthereumUnsignedTransaction[][], signatures: readonly SignatureWithFakeSignerAddress[], blockNumber: bigint, requestAbortController: AbortController | undefined, extraAccountOverrides: StateOverrides = {}) => {
const transactionsWithRemoveZeroPricedOnes = transactionsInBlocks.map((block) => block.map((transaction) => {
if (transaction.type !== '1559') return transaction
const { maxFeePerGas, ...transactionWithoutMaxFee } = transaction
return { ...transactionWithoutMaxFee, ...maxFeePerGas === 0n ? {} : { maxFeePerGas } }
})
}))
const ecRecoverMovedToAddress = 0x123456n
const ecRecoverAddress = 1n
const parentBlock = await this.getBlock(requestAbortController, blockNumber)
Expand All @@ -233,16 +233,22 @@ export class EthereumClientService {
return acc
}, {} as { [key: string]: bigint } )

const query = [{
calls: transactionsWithRemoveZeroPricedOnes,
blockOverride: {
number: parentBlock.number + 1n,
prevRandao: 0x1n,
time: new Date(parentBlock.timestamp.getTime() + 12 * 1000),
gasLimit: parentBlock.gasLimit,
feeRecipient: parentBlock.miner,
baseFeePerGas: parentBlock.baseFeePerGas === undefined ? 15000000n : parentBlock.baseFeePerGas
},
const getBlockOverrides = (index: number) => ({
number: parentBlock.number + 1n + BigInt(index),
prevRandao: 0x1n,
time: new Date(parentBlock.timestamp.getTime() + (index + 1) * 12 * 1000),
gasLimit: parentBlock.gasLimit,
feeRecipient: parentBlock.miner,
baseFeePerGas: parentBlock.baseFeePerGas === undefined ? 15000000n : parentBlock.baseFeePerGas
})

const [firstBlocksTransactions, ...restofTheBlocks] = transactionsWithRemoveZeroPricedOnes
if (firstBlocksTransactions === undefined) throw new Error('No blocks specified for simulateTransactionsAndSignatures')

// add stateOverrides only to the first block (ecrecover overrides, make me rich and such)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

State overrides carry over to subsequent blocks?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, they modify state and state stays modified

const firstBlocksCalls = {
calls: firstBlocksTransactions,
blockOverride: getBlockOverrides(0),
stateOverrides: {
...signatures.length > 0 ? {
[addressString(ecRecoverAddress)]: {
Expand All @@ -253,12 +259,11 @@ export class EthereumClientService {
} : {},
...extraAccountOverrides,
}
}]
const ethSimulateResults = await this.ethSimulateV1(query, parentBlock.number, requestAbortController)
if (ethSimulateResults.length !== 1) throw new Error('Ran Eth Simulate for one block but did not get one block')
const singleMulticalResult = ethSimulateResults[0]
if (singleMulticalResult === undefined) throw new Error('Eth Simualte result was undefined')
return singleMulticalResult
}
const blockStateCalls = [firstBlocksCalls, ...restofTheBlocks.map((calls, index) => ({ calls, blockOverride: getBlockOverrides(index + 1) }))]
const ethSimulateResults = await this.ethSimulateV1(blockStateCalls, parentBlock.number, requestAbortController)
if (ethSimulateResults.length !== transactionsWithRemoveZeroPricedOnes.length) throw new Error(`Ran Eth Simulate for ${ transactionsWithRemoveZeroPricedOnes.length } blocks but got ${ ethSimulateResults.length } blocks`)
return ethSimulateResults
}

public readonly web3ClientVersion = async (requestAbortController: AbortController | undefined) => {
Expand Down
Loading
Loading