Skip to content

Commit

Permalink
fix(core): increase overridden gas limit on local and forked networks
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-goldman committed Feb 20, 2024
1 parent 77c81a4 commit a77d5a2
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/red-spoons-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sphinx-labs/core': patch
---

Increase overridden gas limit on local and forked networks
29 changes: 15 additions & 14 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,20 +168,21 @@ export const getGasPriceOverrides = async (
executionMode === ExecutionMode.Platform) &&
process.env.SPHINX_INTERNAL__DISABLE_HARDCODED_GAS_LIMIT !== 'true'
) {
// Hard-code the gas limit to be the 3/4 of the block gas limit. This is an optimization that
// significantly speeds up deployments because it removes the need for EthersJS to call
// `eth_estimateGas`, which is a very slow operation for large transactions. We only make this
// optimization in situations where we can safely assume that the caller has an unlimited amount
// of ETH. We don't override this on live networks because the signer is the user's wallet,
// which may have a limited amount of ETH. We set it to 3/4 of the block gas limit for two
// reasons:
// 1. This value is considerably higher than the max batch size for `EXECUTE` Merkle leaves,
// which ensures that we don't accidentally underfund the transaction.
// 2. This value is considerably lower than the block gas limit. If, instead, we set this value
// equal to the current block gas limit, a situation could occur where the block gas limit
// decreases slightly after we set this value, which would cause an error due to the fact
// that the transaction's gas limit exceeds the block gas limit.
overridden.gasLimit = (BigInt(3) * block.gasLimit) / BigInt(4)
// Get the max batch gas limit on the current network.
const maxGasLimit = getMaxGasLimit(block.gasLimit, network.chainId)
// Hard-code the gas limit to be midway between the max batch gas limit and the block gas limit.
// This is an optimization that significantly speeds up deployments because it removes the need
// for EthersJS to call `eth_estimateGas`, which is a very slow operation for large
// transactions. We only make this optimization in situations where we can safely assume that
// the caller has an unlimited amount of ETH. We don't override this on live networks because
// the signer is the user's wallet, which may have a limited amount of ETH.
//
// We set this value higher than the max batch gas limit to ensure that we don't accidentally
// underfund the transaction. If we set this value equal to the block gas limit, a situation
// could occur where the block gas limit decreases slightly after we set this value, which would
// cause an error due to the fact that the transaction's gas limit exceeds the block gas limit.
// This occurred when simulating a deployment on Polygon.
overridden.gasLimit = (maxGasLimit + block.gasLimit) / BigInt(2)
return overridden
}

Expand Down
48 changes: 32 additions & 16 deletions packages/plugins/src/foundry/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
Create2ActionInput,
ActionInputType,
fetchNameForNetwork,
getMaxGasLimit,
prettyFunctionCall,
} from '@sphinx-labs/core'
import { AbiCoder, ConstructorFragment, ethers } from 'ethers'
import {
Expand Down Expand Up @@ -144,25 +146,12 @@ export const makeNetworkConfig = (
gasEstimates,
} = deploymentInfo

// Each Merkle leaf must have a gas amount that's at most 80% of the block gas limit. This ensures
// that it's possible to execute the transaction on-chain. Specifically, there must be enough gas
// to execute the Sphinx Module's logic, which isn't included in the gas estimate of the Merkle
// leaf. The 80% was chosen arbitrarily.
const maxAllowedGasPerLeaf = (BigInt(8) * BigInt(blockGasLimit)) / BigInt(10)

const parsedActionInputs: Array<ActionInput> = []
const unlabeledContracts: NetworkConfig['unlabeledContracts'] = []
for (let i = 0; i < accountAccesses.length; i++) {
const { root, nested } = accountAccesses[i]
const gas = gasEstimates[i].toString()

if (BigInt(gas) > maxAllowedGasPerLeaf) {
const networkName = fetchNameForNetwork(BigInt(chainId))
throw new Error(
`Estimated gas for a transaction is too close to the block gas limit on ${networkName}.`
)
}

const { parsedContracts, unlabeled } = parseNestedContractDeployments(
nested,
configArtifacts
Expand All @@ -173,6 +162,7 @@ export const makeNetworkConfig = (
// index of 0.
const executeActionIndex = i + 1

let actionInput: ActionInput
if (root.kind === AccountAccessKind.Create) {
const initCodeWithArgs = root.data
const address = root.account
Expand Down Expand Up @@ -219,7 +209,7 @@ export const makeNetworkConfig = (
to: getCreateCallAddress(),
txData: encodeCreateCall(root.value, initCodeWithArgs),
}
parsedActionInputs.push(action)
actionInput = action
} else if (isCreate2AccountAccess(root, nested)) {
const { create2Address, initCodeWithArgs } =
decodeDeterministicDeploymentProxyData(root.data)
Expand Down Expand Up @@ -250,7 +240,7 @@ export const makeNetworkConfig = (
to: DETERMINISTIC_DEPLOYMENT_PROXY_ADDRESS,
txData: root.data,
}
parsedActionInputs.push(action)
actionInput = action
} else if (root.kind === AccountAccessKind.Call) {
const to = root.account

Expand Down Expand Up @@ -281,8 +271,34 @@ export const makeNetworkConfig = (
to,
txData: root.data,
}
actionInput = callInput
} else {
throw new Error(`Invalid action input. Should never happen.`)
}

parsedActionInputs.push(actionInput)

parsedActionInputs.push(callInput)
// Check if the estimated gas exceeds the max batch gas limit. It's not necessary to check this
// here because the simulation will throw an error if it can't find a valid batch size. However,
// we check this here anyways so that we can display an error to the user earlier in the
// process. If the estimated gas is less than the max batch gas limit but it can't fit into a
// batch, the simulation will throw an error.
const maxGasLimit = getMaxGasLimit(BigInt(blockGasLimit), BigInt(chainId))
if (BigInt(gas) > maxGasLimit) {
const networkName = fetchNameForNetwork(BigInt(chainId))
const { referenceName, address, functionName, variables } =
actionInput.decodedAction
throw new Error(
`Estimated gas for the following transaction is too high to be executed by Sphinx on ${networkName}:\n` +
prettyFunctionCall(
referenceName,
address,
functionName,
variables,
5,
3
)
)
}
}

Expand Down

0 comments on commit a77d5a2

Please sign in to comment.