Skip to content

Commit

Permalink
fix(pg): set simulation account balance to avoid running out of funds
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-goldman committed Jan 5, 2024
1 parent f46d9c0 commit b7614c6
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 12 deletions.
6 changes: 6 additions & 0 deletions .changeset/tender-foxes-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sphinx-labs/plugins': patch
'@sphinx-labs/core': patch
---

Set simulation account balance to avoid running out of funds
4 changes: 2 additions & 2 deletions packages/core/src/actions/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
import {
addSphinxWalletsToGnosisSafeOwners,
findLeafWithProof,
fundAccount,
fundAccountMaxBalance,
getGasPriceOverrides,
getMaxGasLimit,
getReadableActions,
Expand Down Expand Up @@ -574,7 +574,7 @@ export const runEntireDeploymentProcess = async (
executionMode === ExecutionMode.LocalNetworkCLI ||
executionMode === ExecutionMode.Platform
) {
await fundAccount(signer.address, provider)
await fundAccountMaxBalance(signer.address, provider)
await setManagedServiceRelayer(signer.address, provider)

estimateGas = estimateGasViaManagedService
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/languages/solidity/predeploys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
isLiveNetwork,
getImpersonatedSigner,
getSphinxWalletPrivateKey,
fundAccount,
fundAccountMaxBalance,
} from '../../utils'
import { SphinxJsonRpcProvider } from '../../provider'
import { RELAYER_ROLE } from '../../constants'
Expand All @@ -43,7 +43,7 @@ export const ensureSphinxAndGnosisSafeDeployed = async (
// own keys, but it provides a better separation of concerns.
const firstSphinxPrivateKey = getSphinxWalletPrivateKey(0)
const wallet = new ethers.Wallet(firstSphinxPrivateKey, provider)
await fundAccount(wallet.address, provider)
await fundAccountMaxBalance(wallet.address, provider)

await deploySphinxSystem(provider, wallet, relayers, logger)
} else if (!(await allSphinxAndGnosisSafeContractsDeployed(provider))) {
Expand Down
19 changes: 15 additions & 4 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ export const addSphinxWalletsToGnosisSafeOwners = async (

// Set the balance of the Gnosis Safe. This ensures that it has enough funds to submit the
// transactions.
await fundAccount(safeAddress, provider)
await fundAccountMaxBalance(safeAddress, provider)

const ownerThreshold: bigint = await safe.getThreshold()

Expand Down Expand Up @@ -1070,7 +1070,7 @@ export const removeSphinxWalletsFromGnosisSafeOwners = async (

// Set the balance of the Gnosis Safe. This ensures that it has enough funds to submit the
// transactions.
await fundAccount(safeAddress, provider)
await fundAccountMaxBalance(safeAddress, provider)

const ownerThreshold = Number(await safe.getThreshold())

Expand Down Expand Up @@ -1266,11 +1266,22 @@ export const signMerkleRoot = async (
return signature
}

export const fundAccount = async (
/**
* This function sets an account's balance to the maximum possible amount on local networks such as
* Hardhat or Anvil. The main purpose of setting an account's balance to the maximum amount is to
* prevent the possibility that a relayer runs out of funds when running a simulation of a network
* like Arbitrum Sepolia, which has an extremely high block gas limit. Running out of funds is a
* concern when simulating this type of network because we set each transaction's `gasLimit` equal
* to the block gas limit. This is an optimization that allows us to avoid making `eth_estimateGas`
* RPC calls, which can be expensive on local networks for large transactions. However, this makes
* the transactions extremely expensive, which is why we set the account's balance to be extremely
* high.
*/
export const fundAccountMaxBalance = async (
address: string,
provider: SphinxJsonRpcProvider | HardhatEthersProvider
) => {
await setBalance(address, ethers.toBeHex(ethers.parseEther('100')), provider)
await setBalance(address, ethers.toBeHex(ethers.MaxUint256), provider)
}

export const setBalance = async (
Expand Down
5 changes: 5 additions & 0 deletions packages/plugins/src/hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const chainId = process.env.SPHINX_INTERNAL__CHAIN_ID
if (!chainId) {
throw new Error(`Could not find chain ID.`)
}
const blockGasLimit = process.env.SPHINX_INTERNAL__BLOCK_GAS_LIMIT
if (!blockGasLimit) {
throw new Error(`Could not find block gas limit.`)
}

subtask('sphinxSimulateDeployment', simulateDeploymentSubtask)

Expand All @@ -27,6 +31,7 @@ module.exports = {
forking: {
url: forkUrl,
},
blockGasLimit: Number(blockGasLimit),
},
},
}
17 changes: 13 additions & 4 deletions packages/plugins/src/hardhat/simulate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
MerkleRootStatus,
SphinxJsonRpcProvider,
isLiveNetwork,
getNetworkNameForChainId,
} from '@sphinx-labs/core'
import { ethers } from 'ethers'
import {
Expand Down Expand Up @@ -66,9 +67,18 @@ export const simulate = async (
const rootPluginPath =
process.env.DEV_FILE_PATH ?? join('node_modules', '@sphinx-labs', 'plugins')

const provider = new SphinxJsonRpcProvider(rpcUrl)

const block = await provider.getBlock('latest')
// Narrow the TypeScript type.
if (!block) {
throw new Error(`Could not find block. Should never happen.`)
}

const envVars = {
SPHINX_INTERNAL__FORK_URL: rpcUrl,
SPHINX_INTERNAL__CHAIN_ID: chainId,
SPHINX_INTERNAL__BLOCK_GAS_LIMIT: block.gasLimit.toString(),
// We must set the Hardhat config using an environment variable so that Hardhat recognizes the
// Hardhat config when we import the HRE in the child process.
HARDHAT_CONFIG: join(rootPluginPath, 'dist', 'hardhat.config.js'),
Expand All @@ -79,13 +89,11 @@ export const simulate = async (
chainId,
}

const provider = new SphinxJsonRpcProvider(rpcUrl)

if (!(await isLiveNetwork(provider))) {
// Fast forward 1000 blocks. This is necessary to prevent the following edge case that occurs
// when running the simulation against a vanilla Anvil node:
// 1. We deploy the Gnosis Safe and Sphinx contracts.
// 2. We create the Hardhat fork, which uses a block that's many confirmations behind the latest
// 2. We create the Hardhat fork, which uses a block that's multiple confirmations behind the latest
// block. This is Hardhat's default behavior, which is meant to protect against chain reorgs
// on forks of live networks.
// 3. The simulation fails because some of the contracts deployed in step 1 don't exist on the
Expand Down Expand Up @@ -128,7 +136,8 @@ export const simulate = async (
)

if (code !== 0) {
throw new Error(`Simulation failed: ${stderr}`)
const networkName = getNetworkNameForChainId(BigInt(chainId))
throw new Error(`Simulation failed for ${networkName}: ${stderr}`)
}

const receipts = JSON.parse(stdout).receipts
Expand Down

0 comments on commit b7614c6

Please sign in to comment.