Skip to content

Commit e310bbc

Browse files
chore: enforced the funds before contract deployment
Ticket: WIN-7265
1 parent c5477ef commit e310bbc

File tree

5 files changed

+171
-9
lines changed

5 files changed

+171
-9
lines changed

.github/workflows/update_transfer_gas_limit.yml

Whitespace-only changes.

deployUtils.ts

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,70 @@ export const logger = {
1515
step: (msg: string) => console.log(`\n--- ${msg} ---`)
1616
};
1717

18+
/**
19+
* Checks if the deployer has sufficient balance for the deployment.
20+
* Requires 1.5x the estimated gas cost to provide a safety buffer.
21+
*/
22+
export async function checkSufficientBalance(
23+
deployerAddress: string,
24+
estimatedGasCost: bigint,
25+
contractName: string = 'contract'
26+
): Promise<void> {
27+
const balance = await ethers.provider.getBalance(deployerAddress);
28+
const requiredAmount = (estimatedGasCost * 150n) / 100n; // 1.5x buffer
29+
30+
logger.info(`💰 Balance check for ${contractName} deployment:`);
31+
logger.info(` Deployer balance: ${ethers.formatEther(balance)} ETH`);
32+
logger.info(` Estimated gas cost: ${ethers.formatEther(estimatedGasCost)} ETH`);
33+
logger.info(` Required (1.5x buffer): ${ethers.formatEther(requiredAmount)} ETH`);
34+
35+
if (balance < requiredAmount) {
36+
const shortfall = requiredAmount - balance;
37+
logger.error(`Insufficient funds for ${contractName} deployment!`);
38+
logger.error(` Shortfall: ${ethers.formatEther(shortfall)} ETH`);
39+
logger.error(` Please fund the deployer account: ${deployerAddress}`);
40+
throw new Error(`Insufficient funds: need ${ethers.formatEther(requiredAmount)} ETH, have ${ethers.formatEther(balance)} ETH`);
41+
}
42+
43+
logger.success(`✅ Sufficient balance confirmed for ${contractName} deployment`);
44+
}
45+
46+
/**
47+
* Checks balance before individual contract deployment.
48+
* Estimates gas for a typical contract deployment transaction.
49+
*/
50+
export async function checkIndividualContractBalance(
51+
deployerAddress: string,
52+
contractName: string,
53+
gasOverrides?: any
54+
): Promise<void> {
55+
try {
56+
// Get current fee data
57+
const feeData = await ethers.provider.getFeeData();
58+
59+
// Estimate typical contract deployment gas (fallback if we can't estimate precisely)
60+
const estimatedGas = 500000n; // 500k gas - reasonable estimate for contract deployment
61+
62+
// Calculate gas price
63+
let gasPrice: bigint;
64+
if (gasOverrides?.gasPrice) {
65+
gasPrice = gasOverrides.gasPrice;
66+
} else if (gasOverrides?.maxFeePerGas) {
67+
gasPrice = gasOverrides.maxFeePerGas;
68+
} else {
69+
gasPrice = feeData.gasPrice || 1_000_000_000n; // 1 gwei fallback
70+
}
71+
72+
const estimatedCost = estimatedGas * gasPrice;
73+
74+
// Check balance with 1.5x buffer
75+
await checkSufficientBalance(deployerAddress, estimatedCost, contractName);
76+
} catch (error) {
77+
logger.warn(`⚠️ Could not perform precise balance check for ${contractName}: ${(error as Error).message}`);
78+
logger.info(`🔄 Proceeding with deployment - will fail fast if insufficient funds`);
79+
}
80+
}
81+
1882
export type DeploymentAddresses = {
1983
walletImplementation?: string;
2084
walletFactory?: string;
@@ -31,7 +95,8 @@ export async function deployIfNeededAtNonce(
3195
expectedNonce: number,
3296
deployerAddress: string,
3397
contractName: string,
34-
deployFn: () => Promise<string>
98+
deployFn: () => Promise<string>,
99+
gasOverrides?: any
35100
): Promise<string> {
36101
const predictedAddress = getCreateAddress({
37102
from: deployerAddress,
@@ -83,12 +148,15 @@ export async function deployIfNeededAtNonce(
83148
throw new Error(errorMsg);
84149
}
85150

86-
// 5. Deploy
151+
// 5. Balance check before deployment
152+
await checkIndividualContractBalance(deployerAddress, contractName, gasOverrides);
153+
154+
// 6. Deploy
87155
logger.info(`🚀 Deploying ${contractName} at nonce ${expectedNonce}...`);
88156
return await deployFn();
89157
}
90158

91-
async function isContractDeployed(address: string) {
159+
export async function isContractDeployed(address: string) {
92160
const code = await ethers.provider.getCode(address);
93161
return code && code !== '0x';
94162
}

scripts/deploy.ts

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import {
55
waitAndVerify,
66
loadOutput,
77
saveOutput,
8-
DeploymentAddresses
8+
DeploymentAddresses,
9+
checkSufficientBalance,
10+
isContractDeployed
911
} from '../deployUtils';
1012
import { setupBigBlocksForV4Deployment } from './enableBigBlocks';
1113
import { isBigBlocksSupported } from '../config/bigBlocksConfig';
@@ -39,6 +41,69 @@ async function main() {
3941
`🚀 Deployer: ${deployerAddress} (nonce: ${currentNonce}) on chain ${chainId}`
4042
);
4143

44+
// Pre-deployment balance check - estimate total cost for all contracts
45+
console.log('\n--- Checking deployer balance ---');
46+
47+
// Estimate gas costs for all potential deployments
48+
let totalEstimatedCost = 0n;
49+
50+
// Only estimate if we need to deploy (not already deployed)
51+
if (!output.walletImplementation || !(await isContractDeployed(output.walletImplementation))) {
52+
const WalletSimple = await ethers.getContractFactory(chainConfig.walletImplementationContractName);
53+
const walletGas = await deployer.estimateGas({
54+
...await WalletSimple.getDeployTransaction(...[], gasOverrides),
55+
from: deployerAddress
56+
});
57+
totalEstimatedCost += walletGas;
58+
}
59+
60+
if (!output.walletFactory || !(await isContractDeployed(output.walletFactory))) {
61+
const WalletFactory = await ethers.getContractFactory(chainConfig.walletFactoryContractName);
62+
const factoryGas = await deployer.estimateGas({
63+
...await WalletFactory.getDeployTransaction(deployerAddress, gasOverrides), // Use deployer address as placeholder
64+
from: deployerAddress
65+
});
66+
totalEstimatedCost += factoryGas;
67+
}
68+
69+
if (!output.forwarderImplementation || !(await isContractDeployed(output.forwarderImplementation))) {
70+
const ForwarderV4 = await ethers.getContractFactory(chainConfig.forwarderContractName);
71+
const forwarderGas = await deployer.estimateGas({
72+
...await ForwarderV4.getDeployTransaction(gasOverrides),
73+
from: deployerAddress
74+
});
75+
totalEstimatedCost += forwarderGas;
76+
}
77+
78+
if (!output.forwarderFactory || !(await isContractDeployed(output.forwarderFactory))) {
79+
const ForwarderFactory = await ethers.getContractFactory(chainConfig.forwarderFactoryContractName);
80+
const forwarderFactoryGas = await deployer.estimateGas({
81+
...await ForwarderFactory.getDeployTransaction(deployerAddress, gasOverrides), // Use deployer address as placeholder
82+
from: deployerAddress
83+
});
84+
totalEstimatedCost += forwarderFactoryGas;
85+
}
86+
87+
if (totalEstimatedCost > 0n) {
88+
// Get gas price for cost calculation
89+
const feeData = await ethers.provider.getFeeData();
90+
let gasPrice: bigint;
91+
92+
if (gasOverrides?.gasPrice) {
93+
gasPrice = gasOverrides.gasPrice;
94+
} else if (gasOverrides?.maxFeePerGas) {
95+
gasPrice = gasOverrides.maxFeePerGas;
96+
} else {
97+
gasPrice = feeData.gasPrice || 1_000_000_000n; // 1 gwei fallback
98+
}
99+
100+
const estimatedCost = totalEstimatedCost * gasPrice;
101+
102+
await checkSufficientBalance(deployerAddress, estimatedCost, 'all V4 contracts');
103+
} else {
104+
console.log('✅ All contracts already deployed, skipping balance check');
105+
}
106+
42107
// Deploy Wallet Implementation
43108
const walletAddress = await deployIfNeededAtNonce(
44109
output.walletImplementation,
@@ -69,7 +134,8 @@ async function main() {
69134
output.walletImplementation = contract.target as string;
70135
saveOutput(output);
71136
return contract.target as string;
72-
}
137+
},
138+
gasOverrides
73139
);
74140

75141
// Deploy Wallet Factory
@@ -96,7 +162,8 @@ async function main() {
96162
output.walletFactory = contract.target as string;
97163
saveOutput(output);
98164
return contract.target as string;
99-
}
165+
},
166+
gasOverrides
100167
);
101168

102169
// Deploy Forwarder
@@ -118,7 +185,8 @@ async function main() {
118185
output.forwarderImplementation = contract.target as string;
119186
saveOutput(output);
120187
return contract.target as string;
121-
}
188+
},
189+
gasOverrides
122190
);
123191

124192
// Deploy Forwarder Factory
@@ -148,7 +216,8 @@ async function main() {
148216
output.forwarderFactory = contract.target as string;
149217
saveOutput(output);
150218
return contract.target as string;
151-
}
219+
},
220+
gasOverrides
152221
);
153222

154223
console.log(`🎉 All contracts deployed and verified!`);

scripts/deployBatcherContract.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import hre, { ethers } from 'hardhat';
22
import { Contract } from 'ethers';
3-
import { logger, waitAndVerify } from '../deployUtils';
3+
import { logger, waitAndVerify, checkSufficientBalance } from '../deployUtils';
44
import fs from 'fs';
55
import { setupBigBlocksForBatcherDeployment } from './enableBigBlocks';
66
import { isBigBlocksSupported } from '../config/bigBlocksConfig';
@@ -187,6 +187,31 @@ async function main() {
187187
logger.info(`Using default gas parameters for this network.`);
188188
}
189189

190+
// --- 3.5. Balance Check ---
191+
logger.step('3.5. Checking deployer balance before deployment...');
192+
193+
// Estimate gas cost for the deployment
194+
const gasEstimate = await batcherDeployer.estimateGas({
195+
...deployTxReq,
196+
from: address,
197+
...gasOverrides
198+
});
199+
200+
// Calculate total cost
201+
let gasPrice: bigint;
202+
if (gasOverrides?.gasPrice) {
203+
gasPrice = gasOverrides.gasPrice;
204+
} else if (gasOverrides?.maxFeePerGas) {
205+
gasPrice = gasOverrides.maxFeePerGas;
206+
} else {
207+
gasPrice = feeData.gasPrice ?? 1_000_000_000n; // 1 gwei fallback
208+
}
209+
210+
const estimatedCost = gasEstimate * gasPrice;
211+
212+
// Check if we have 1.5x the required amount
213+
await checkSufficientBalance(address, estimatedCost, 'Batcher');
214+
190215
let batcher: Contract;
191216
if (gasOverrides) {
192217
batcher = (await Batcher.deploy(

scripts/deployBatcherContractSomnia.ts

Whitespace-only changes.

0 commit comments

Comments
 (0)