zksync-web3
Advanced tools
Comparing version 0.14.3 to 0.14.4-beta.0
@@ -19,2 +19,3 @@ import { BigNumber, BigNumberish, BytesLike, ethers } from 'ethers'; | ||
getBalanceL1(token?: Address, blockTag?: ethers.providers.BlockTag): Promise<BigNumber>; | ||
getAllowanceL1(token: Address, bridgeAddress?: Address, blockTag?: ethers.providers.BlockTag): Promise<BigNumber>; | ||
l2TokenAddress(token: Address): Promise<string>; | ||
@@ -61,2 +62,9 @@ approveERC20(token: Address, amount: BigNumberish, overrides?: ethers.Overrides & { | ||
}): Promise<any>; | ||
getFullRequiredDepositFee(transaction: { | ||
token: Address; | ||
to?: Address; | ||
bridgeAddress?: Address; | ||
gasPerPubdataByte?: BigNumberish; | ||
overrides?: ethers.PayableOverrides; | ||
}): Promise<[BigNumber, BigNumber]>; | ||
_getWithdrawalLog(withdrawalHash: BytesLike, index?: number): Promise<{ | ||
@@ -63,0 +71,0 @@ log: import("./types").Log; |
@@ -38,2 +38,7 @@ "use strict"; | ||
} | ||
async getAllowanceL1(token, bridgeAddress, blockTag) { | ||
const erc20contract = typechain_1.IERC20MetadataFactory.connect(token, this._providerL1()); | ||
bridgeAddress !== null && bridgeAddress !== void 0 ? bridgeAddress : (bridgeAddress = (await this.getL1BridgeContracts()).erc20.address); | ||
return await erc20contract.allowance(await this.getAddress(), bridgeAddress, { blockTag }); | ||
} | ||
async l2TokenAddress(token) { | ||
@@ -71,5 +76,10 @@ if (token == utils_1.ETH_ADDRESS) { | ||
async deposit(transaction) { | ||
var _a; | ||
var _a, _b, _c, _d; | ||
var _e; | ||
const depositTx = await this.getDepositTx(transaction); | ||
if (transaction.token == utils_1.ETH_ADDRESS) { | ||
const baseGasLimit = await this.estimateGasRequestExecute(depositTx); | ||
const gasLimit = (0, utils_1.scaleGasLimit)(baseGasLimit); | ||
(_a = depositTx.overrides) !== null && _a !== void 0 ? _a : (depositTx.overrides = {}); | ||
(_b = (_e = depositTx.overrides).gasLimit) !== null && _b !== void 0 ? _b : (_e.gasLimit = gasLimit); | ||
return this.requestExecute(depositTx); | ||
@@ -80,8 +90,15 @@ } | ||
if (transaction.approveERC20) { | ||
const approveTx = await this.approveERC20(transaction.token, transaction.amount, { | ||
bridgeAddress: (_a = transaction.bridgeAddress) !== null && _a !== void 0 ? _a : bridgeContracts.erc20.address, | ||
...transaction.approveOverrides | ||
}); | ||
await approveTx.wait(); | ||
// We only request the allowance if the current one is not enough. | ||
const allowance = await this.getAllowanceL1(transaction.token); | ||
if (allowance.lt(transaction.amount)) { | ||
const approveTx = await this.approveERC20(transaction.token, transaction.amount, { | ||
bridgeAddress: (_c = transaction.bridgeAddress) !== null && _c !== void 0 ? _c : bridgeContracts.erc20.address, | ||
...transaction.approveOverrides | ||
}); | ||
await approveTx.wait(); | ||
} | ||
} | ||
const baseGasLimit = await this._providerL1().estimateGas(depositTx); | ||
const gasLimit = (0, utils_1.scaleGasLimit)(baseGasLimit); | ||
(_d = depositTx.gasLimit) !== null && _d !== void 0 ? _d : (depositTx.gasLimit = gasLimit); | ||
return await this._providerL2().getPriorityOpResponse(await this._signerL1().sendTransaction(depositTx)); | ||
@@ -92,11 +109,13 @@ } | ||
const depositTx = await this.getDepositTx(transaction); | ||
let baseGasLimit; | ||
if (transaction.token == utils_1.ETH_ADDRESS) { | ||
return await this.estimateGasRequestExecute(depositTx); | ||
baseGasLimit = await this.estimateGasRequestExecute(depositTx); | ||
} | ||
else { | ||
return await this._providerL1().estimateGas(depositTx); | ||
baseGasLimit = await this._providerL1().estimateGas(depositTx); | ||
} | ||
return (0, utils_1.scaleGasLimit)(baseGasLimit); | ||
} | ||
async getDepositTx(transaction) { | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
var _a, _b, _c, _d, _e, _f, _g, _h; | ||
const bridgeContracts = await this.getL1BridgeContracts(); | ||
@@ -113,8 +132,7 @@ if (transaction.bridgeAddress) { | ||
const { to, token, amount, operatorTip, overrides } = tx; | ||
await insertGasPrice(this._providerL1(), overrides); | ||
const gasPriceForEstimation = overrides.maxFeePerGas || overrides.gasPrice; | ||
(_f = overrides.gasPrice) !== null && _f !== void 0 ? _f : (overrides.gasPrice = await this._providerL1().getGasPrice()); | ||
const zksyncContract = await this.getMainContract(); | ||
const baseCost = await zksyncContract.l2TransactionBaseCost(await gasPriceForEstimation, tx.l2GasLimit, tx.gasPerPubdataByte); | ||
const baseCost = await zksyncContract.l2TransactionBaseCost(await overrides.gasPrice, tx.l2GasLimit, tx.gasPerPubdataByte); | ||
if (token == utils_1.ETH_ADDRESS) { | ||
(_f = overrides.value) !== null && _f !== void 0 ? _f : (overrides.value = baseCost.add(operatorTip).add(amount)); | ||
(_g = overrides.value) !== null && _g !== void 0 ? _g : (overrides.value = baseCost.add(operatorTip).add(amount)); | ||
return { | ||
@@ -138,10 +156,51 @@ contractAddress: to, | ||
]; | ||
(_g = overrides.value) !== null && _g !== void 0 ? _g : (overrides.value = baseCost.add(operatorTip)); | ||
(_h = overrides.value) !== null && _h !== void 0 ? _h : (overrides.value = baseCost.add(operatorTip)); | ||
await (0, utils_1.checkBaseCost)(baseCost, overrides.value); | ||
// TODO: compatibility layer: using the old API which uses msg.sender as the | ||
// refund recipient, to make the SDK compatible with the old contracts. | ||
// const contract = bridgeContracts.erc20 as ethers.Contract; | ||
return await bridgeContracts.erc20.populateTransaction.deposit(...args, overrides); | ||
} | ||
} | ||
// Retrieves the full needed ETH fee for the deposit. | ||
// Returns the L1 fee and the L2 fee. | ||
async getFullRequiredDepositFee(transaction) { | ||
var _a, _b, _c, _d; | ||
var _e; | ||
// It is assumed that the L2 fee for the transaction does not depend on its value. | ||
const dummyAmount = '1'; | ||
const { ...tx } = transaction; | ||
const zksyncContract = await this.getMainContract(); | ||
(_a = tx.overrides) !== null && _a !== void 0 ? _a : (tx.overrides = {}); | ||
(_b = (_e = tx.overrides).gasPrice) !== null && _b !== void 0 ? _b : (_e.gasPrice = await this._providerL1().getGasPrice()); | ||
(_c = tx.to) !== null && _c !== void 0 ? _c : (tx.to = await this.getAddress()); | ||
(_d = tx.gasPerPubdataByte) !== null && _d !== void 0 ? _d : (tx.gasPerPubdataByte = utils_1.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT); | ||
const l2GasLimit = await (0, utils_1.estimateDefaultBridgeDepositL2Gas)(this._providerL1(), this._providerL2(), tx.token, dummyAmount, tx.to, await this.getAddress(), tx.gasPerPubdataByte); | ||
const baseCost = await zksyncContract.l2TransactionBaseCost(await tx.overrides.gasPrice, l2GasLimit, tx.gasPerPubdataByte); | ||
const selfBalanceETH = await this.getBalanceL1(); | ||
// We could 0 in, because the final fee will anyway be bigger than | ||
if (baseCost.gte(selfBalanceETH)) { | ||
const recommendedETHBalance = ethers_1.BigNumber.from(utils_1.L1_RECOMMENDED_MIN_DEPOSIT_GAS_LIMIT) | ||
.mul(await tx.overrides.gasPrice) | ||
.add(baseCost); | ||
const formattedRecommendedBalance = ethers_1.ethers.utils.formatEther(recommendedETHBalance); | ||
throw new Error(`Not enough balance for deposit. Under the provided gas price, the recommended balance to perform a deposit is ${formattedRecommendedBalance} ETH`); | ||
} | ||
// For ETH token the value that the user passes to the estimation is the one which has the | ||
// value for the L2 comission substracted. | ||
let amountForEstimate; | ||
if ((0, utils_1.isETH)(tx.token)) { | ||
amountForEstimate = ethers_1.BigNumber.from(dummyAmount); | ||
} | ||
else { | ||
amountForEstimate = await this.getBalanceL1(tx.token); | ||
if ((await this.getAllowanceL1(tx.token)) < amountForEstimate) { | ||
throw new Error('Not enough allowance to cover the deposit'); | ||
} | ||
} | ||
const l1GasLimit = await this.estimateGasDeposit({ | ||
...tx, | ||
amount: amountForEstimate, | ||
l2GasLimit | ||
}); | ||
const l1Fee = (0, utils_1.scaleGasLimit)(l1GasLimit).mul(await tx.overrides.gasPrice); | ||
return [l1Fee, baseCost]; | ||
} | ||
async _getWithdrawalLog(withdrawalHash, index = 0) { | ||
@@ -236,3 +295,3 @@ const hash = ethers_1.ethers.utils.hexlify(withdrawalHash); | ||
async getRequestExecuteTx(transaction) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h; | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j; | ||
const zksyncContract = await this.getMainContract(); | ||
@@ -248,10 +307,9 @@ const { ...tx } = transaction; | ||
const { contractAddress, l2Value, calldata, l2GasLimit, factoryDeps, operatorTip, overrides, gasPerPubdataByte, refundRecipient } = tx; | ||
await insertGasPrice(this._providerL1(), overrides); | ||
const gasPriceForEstimation = overrides.maxFeePerGas || overrides.gasPrice; | ||
(_h = overrides.gasPrice) !== null && _h !== void 0 ? _h : (overrides.gasPrice = await this._providerL1().getGasPrice()); | ||
const baseCost = await this.getBaseCost({ | ||
gasPrice: await gasPriceForEstimation, | ||
gasPrice: await overrides.gasPrice, | ||
gasPerPubdataByte, | ||
gasLimit: l2GasLimit | ||
}); | ||
(_h = overrides.value) !== null && _h !== void 0 ? _h : (overrides.value = baseCost.add(operatorTip).add(l2Value)); | ||
(_j = overrides.value) !== null && _j !== void 0 ? _j : (overrides.value = baseCost.add(operatorTip).add(l2Value)); | ||
await (0, utils_1.checkBaseCost)(baseCost, overrides.value); | ||
@@ -309,15 +367,1 @@ return await zksyncContract.populateTransaction.requestL2Transaction(contractAddress, l2Value, calldata, l2GasLimit, utils_1.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT, factoryDeps, refundRecipient, overrides); | ||
exports.AdapterL2 = AdapterL2; | ||
/// @dev This method checks if the overrides contain a gasPrice (or maxFeePerGas), if not it will insert | ||
/// the maxFeePerGas | ||
async function insertGasPrice(l1Provider, overrides) { | ||
if (!overrides.gasPrice && !overrides.maxFeePerGas) { | ||
const l1FeeData = await l1Provider.getFeeData(); | ||
// Sometimes baseFeePerGas is not available, so we use gasPrice instead. | ||
const baseFee = l1FeeData.lastBaseFeePerGas || l1FeeData.gasPrice; | ||
// ethers.js by default uses multiplcation by 2, but since the price for the L2 part | ||
// will depend on the L1 part, doubling base fee is typically too much. | ||
const maxFeePerGas = baseFee.mul(3).div(2).add(l1FeeData.maxPriorityFeePerGas); | ||
overrides.maxFeePerGas = maxFeePerGas; | ||
overrides.maxPriorityFeePerGas = l1FeeData.maxPriorityFeePerGas; | ||
} | ||
} |
@@ -81,2 +81,3 @@ import { ethers } from 'ethers'; | ||
getBalanceL1(token?: string, blockTag?: ethers.providers.BlockTag): Promise<ethers.BigNumber>; | ||
getAllowanceL1(token: string, bridgeAddress?: string, blockTag?: ethers.providers.BlockTag): Promise<ethers.BigNumber>; | ||
l2TokenAddress(token: string): Promise<string>; | ||
@@ -123,2 +124,9 @@ approveERC20(token: string, amount: ethers.BigNumberish, overrides?: ethers.Overrides & { | ||
}): Promise<any>; | ||
getFullRequiredDepositFee(transaction: { | ||
token: string; | ||
to?: string; | ||
bridgeAddress?: string; | ||
gasPerPubdataByte?: ethers.BigNumberish; | ||
overrides?: ethers.PayableOverrides; | ||
}): Promise<[ethers.BigNumber, ethers.BigNumber]>; | ||
_getWithdrawalLog(withdrawalHash: ethers.utils.BytesLike, index?: number): Promise<{ | ||
@@ -125,0 +133,0 @@ log: import("./types").Log; |
@@ -24,2 +24,5 @@ import { utils, ethers, BigNumber, BigNumberish, BytesLike } from 'ethers'; | ||
export declare const MAX_BYTECODE_LEN_BYTES: number; | ||
export declare const L1_FEE_ESTIMATION_COEF_NUMERATOR: ethers.BigNumber; | ||
export declare const L1_FEE_ESTIMATION_COEF_DENOMINATOR: ethers.BigNumber; | ||
export declare const L1_RECOMMENDED_MIN_DEPOSIT_GAS_LIMIT = 400000; | ||
export declare const DEFAULT_GAS_PER_PUBDATA_LIMIT = 50000; | ||
@@ -48,1 +51,2 @@ export declare const REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = 800; | ||
export declare function estimateDefaultBridgeDepositL2Gas(providerL1: ethers.providers.Provider, providerL2: Provider, token: Address, amount: BigNumberish, to: Address, from?: Address, gasPerPubdataByte?: BigNumberish): Promise<BigNumber>; | ||
export declare function scaleGasLimit(gasLimit: BigNumber): BigNumber; |
@@ -17,3 +17,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.estimateDefaultBridgeDepositL2Gas = exports.isTypedDataSignatureCorrect = exports.isMessageSignatureCorrect = exports.getERC20BridgeCalldata = exports.undoL1ToL2Alias = exports.applyL1ToL2Alias = exports.getL2HashFromPriorityOp = exports.parseTransaction = exports.hashBytecode = exports.serialize = exports.checkBaseCost = exports.createAddress = exports.create2Address = exports.getDeployedContracts = exports.getHashedL2ToL1Msg = exports.layer1TxDefaults = exports.sleep = exports.isETH = exports.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = exports.DEFAULT_GAS_PER_PUBDATA_LIMIT = exports.MAX_BYTECODE_LEN_BYTES = exports.PRIORITY_OPERATION_L2_TX_TYPE = exports.EIP712_TX_TYPE = exports.EIP1271_MAGIC_VALUE = exports.L1_TO_L2_ALIAS_OFFSET = exports.L2_ETH_TOKEN_ADDRESS = exports.L1_MESSENGER_ADDRESS = exports.CONTRACT_DEPLOYER_ADDRESS = exports.BOOTLOADER_FORMAL_ADDRESS = exports.L2_BRIDGE_ABI = exports.L1_BRIDGE_ABI = exports.IERC1271 = exports.IERC20 = exports.L1_MESSENGER = exports.CONTRACT_DEPLOYER = exports.ZKSYNC_MAIN_ABI = exports.ETH_ADDRESS = void 0; | ||
exports.scaleGasLimit = exports.estimateDefaultBridgeDepositL2Gas = exports.isTypedDataSignatureCorrect = exports.isMessageSignatureCorrect = exports.getERC20BridgeCalldata = exports.undoL1ToL2Alias = exports.applyL1ToL2Alias = exports.getL2HashFromPriorityOp = exports.parseTransaction = exports.hashBytecode = exports.serialize = exports.checkBaseCost = exports.createAddress = exports.create2Address = exports.getDeployedContracts = exports.getHashedL2ToL1Msg = exports.layer1TxDefaults = exports.sleep = exports.isETH = exports.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT = exports.DEFAULT_GAS_PER_PUBDATA_LIMIT = exports.L1_RECOMMENDED_MIN_DEPOSIT_GAS_LIMIT = exports.L1_FEE_ESTIMATION_COEF_DENOMINATOR = exports.L1_FEE_ESTIMATION_COEF_NUMERATOR = exports.MAX_BYTECODE_LEN_BYTES = exports.PRIORITY_OPERATION_L2_TX_TYPE = exports.EIP712_TX_TYPE = exports.EIP1271_MAGIC_VALUE = exports.L1_TO_L2_ALIAS_OFFSET = exports.L2_ETH_TOKEN_ADDRESS = exports.L1_MESSENGER_ADDRESS = exports.CONTRACT_DEPLOYER_ADDRESS = exports.BOOTLOADER_FORMAL_ADDRESS = exports.L2_BRIDGE_ABI = exports.L1_BRIDGE_ABI = exports.IERC1271 = exports.IERC20 = exports.L1_MESSENGER = exports.CONTRACT_DEPLOYER = exports.ZKSYNC_MAIN_ABI = exports.ETH_ADDRESS = void 0; | ||
const ethers_1 = require("ethers"); | ||
@@ -42,2 +42,8 @@ const types_1 = require("./types"); | ||
exports.MAX_BYTECODE_LEN_BYTES = ((1 << 16) - 1) * 32; | ||
// Currently, for some reason the SDK may return slightly smaller value than required for the L1->L2 | ||
// transaction. We will use a coefficient to ensure that the transaction will be accepted. | ||
exports.L1_FEE_ESTIMATION_COEF_NUMERATOR = ethers_1.BigNumber.from(12); | ||
exports.L1_FEE_ESTIMATION_COEF_DENOMINATOR = ethers_1.BigNumber.from(10); | ||
// This gas limit will be used for displaying the error messages when the users do not have enough fee. | ||
exports.L1_RECOMMENDED_MIN_DEPOSIT_GAS_LIMIT = 400000; | ||
// The large L2 gas per pubdata to sign. This gas is enough to ensure that | ||
@@ -435,1 +441,5 @@ // any reasonable limit will be accepted. Note, that the operator is NOT required to | ||
exports.estimateDefaultBridgeDepositL2Gas = estimateDefaultBridgeDepositL2Gas; | ||
function scaleGasLimit(gasLimit) { | ||
return gasLimit.mul(exports.L1_FEE_ESTIMATION_COEF_NUMERATOR).div(exports.L1_FEE_ESTIMATION_COEF_DENOMINATOR); | ||
} | ||
exports.scaleGasLimit = scaleGasLimit; |
@@ -42,2 +42,3 @@ import { EIP712Signer } from './signer'; | ||
getBalanceL1(token?: string, blockTag?: ethers.providers.BlockTag): Promise<ethers.BigNumber>; | ||
getAllowanceL1(token: string, bridgeAddress?: string, blockTag?: ethers.providers.BlockTag): Promise<ethers.BigNumber>; | ||
l2TokenAddress(token: string): Promise<string>; | ||
@@ -84,2 +85,9 @@ approveERC20(token: string, amount: ethers.BigNumberish, overrides?: ethers.Overrides & { | ||
}): Promise<any>; | ||
getFullRequiredDepositFee(transaction: { | ||
token: string; | ||
to?: string; | ||
bridgeAddress?: string; | ||
gasPerPubdataByte?: ethers.BigNumberish; | ||
overrides?: ethers.PayableOverrides; | ||
}): Promise<[ethers.BigNumber, ethers.BigNumber]>; | ||
_getWithdrawalLog(withdrawalHash: ethers.utils.BytesLike, index?: number): Promise<{ | ||
@@ -86,0 +94,0 @@ log: import("./types").Log; |
{ | ||
"name": "zksync-web3", | ||
"version": "0.14.3", | ||
"version": "0.14.4-beta.0", | ||
"main": "build/src/index.js", | ||
@@ -5,0 +5,0 @@ "types": "build/src/index.d.ts", |
@@ -15,3 +15,5 @@ import { BigNumber, BigNumberish, BytesLike, ethers } from 'ethers'; | ||
undoL1ToL2Alias, | ||
estimateDefaultBridgeDepositL2Gas | ||
estimateDefaultBridgeDepositL2Gas, | ||
L1_RECOMMENDED_MIN_DEPOSIT_GAS_LIMIT, | ||
scaleGasLimit | ||
} from './utils'; | ||
@@ -60,2 +62,12 @@ | ||
async getAllowanceL1( | ||
token: Address, | ||
bridgeAddress?: Address, | ||
blockTag?: ethers.providers.BlockTag | ||
): Promise<BigNumber> { | ||
const erc20contract = IERC20MetadataFactory.connect(token, this._providerL1()); | ||
bridgeAddress ??= (await this.getL1BridgeContracts()).erc20.address; | ||
return await erc20contract.allowance(await this.getAddress(), bridgeAddress, { blockTag }); | ||
} | ||
async l2TokenAddress(token: Address) { | ||
@@ -123,3 +135,10 @@ if (token == ETH_ADDRESS) { | ||
const depositTx = await this.getDepositTx(transaction); | ||
if (transaction.token == ETH_ADDRESS) { | ||
const baseGasLimit = await this.estimateGasRequestExecute(depositTx); | ||
const gasLimit = scaleGasLimit(baseGasLimit); | ||
depositTx.overrides ??= {}; | ||
depositTx.overrides.gasLimit ??= gasLimit; | ||
return this.requestExecute(depositTx); | ||
@@ -129,8 +148,18 @@ } else { | ||
if (transaction.approveERC20) { | ||
const approveTx = await this.approveERC20(transaction.token, transaction.amount, { | ||
bridgeAddress: transaction.bridgeAddress ?? bridgeContracts.erc20.address, | ||
...transaction.approveOverrides | ||
}); | ||
await approveTx.wait(); | ||
// We only request the allowance if the current one is not enough. | ||
const allowance = await this.getAllowanceL1(transaction.token); | ||
if (allowance.lt(transaction.amount)) { | ||
const approveTx = await this.approveERC20(transaction.token, transaction.amount, { | ||
bridgeAddress: transaction.bridgeAddress ?? bridgeContracts.erc20.address, | ||
...transaction.approveOverrides | ||
}); | ||
await approveTx.wait(); | ||
} | ||
} | ||
const baseGasLimit = await this._providerL1().estimateGas(depositTx); | ||
const gasLimit = scaleGasLimit(baseGasLimit); | ||
depositTx.gasLimit ??= gasLimit; | ||
return await this._providerL2().getPriorityOpResponse( | ||
@@ -153,7 +182,11 @@ await this._signerL1().sendTransaction(depositTx) | ||
const depositTx = await this.getDepositTx(transaction); | ||
let baseGasLimit: BigNumber; | ||
if (transaction.token == ETH_ADDRESS) { | ||
return await this.estimateGasRequestExecute(depositTx); | ||
baseGasLimit = await this.estimateGasRequestExecute(depositTx); | ||
} else { | ||
return await this._providerL1().estimateGas(depositTx); | ||
baseGasLimit = await this._providerL1().estimateGas(depositTx); | ||
} | ||
return scaleGasLimit(baseGasLimit); | ||
} | ||
@@ -192,10 +225,8 @@ | ||
const { to, token, amount, operatorTip, overrides } = tx; | ||
overrides.gasPrice ??= await this._providerL1().getGasPrice(); | ||
await insertGasPrice(this._providerL1(), overrides); | ||
const gasPriceForEstimation = overrides.maxFeePerGas || overrides.gasPrice; | ||
const zksyncContract = await this.getMainContract(); | ||
const baseCost = await zksyncContract.l2TransactionBaseCost( | ||
await gasPriceForEstimation, | ||
await overrides.gasPrice, | ||
tx.l2GasLimit, | ||
@@ -229,5 +260,2 @@ tx.gasPerPubdataByte | ||
// TODO: compatibility layer: using the old API which uses msg.sender as the | ||
// refund recipient, to make the SDK compatible with the old contracts. | ||
// const contract = bridgeContracts.erc20 as ethers.Contract; | ||
return await bridgeContracts.erc20.populateTransaction.deposit(...args, overrides); | ||
@@ -237,2 +265,75 @@ } | ||
// Retrieves the full needed ETH fee for the deposit. | ||
// Returns the L1 fee and the L2 fee. | ||
async getFullRequiredDepositFee(transaction: { | ||
token: Address; | ||
to?: Address; | ||
bridgeAddress?: Address; | ||
gasPerPubdataByte?: BigNumberish; | ||
overrides?: ethers.PayableOverrides; | ||
}): Promise<[BigNumber, BigNumber]> { | ||
// It is assumed that the L2 fee for the transaction does not depend on its value. | ||
const dummyAmount = '1'; | ||
const { ...tx } = transaction; | ||
const zksyncContract = await this.getMainContract(); | ||
tx.overrides ??= {}; | ||
tx.overrides.gasPrice ??= await this._providerL1().getGasPrice(); | ||
tx.to ??= await this.getAddress(); | ||
tx.gasPerPubdataByte ??= REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT; | ||
const l2GasLimit = await estimateDefaultBridgeDepositL2Gas( | ||
this._providerL1(), | ||
this._providerL2(), | ||
tx.token, | ||
dummyAmount, | ||
tx.to, | ||
await this.getAddress(), | ||
tx.gasPerPubdataByte | ||
); | ||
const baseCost = await zksyncContract.l2TransactionBaseCost( | ||
await tx.overrides.gasPrice, | ||
l2GasLimit, | ||
tx.gasPerPubdataByte | ||
); | ||
const selfBalanceETH = await this.getBalanceL1(); | ||
// We could 0 in, because the final fee will anyway be bigger than | ||
if (baseCost.gte(selfBalanceETH)) { | ||
const recommendedETHBalance = BigNumber.from(L1_RECOMMENDED_MIN_DEPOSIT_GAS_LIMIT) | ||
.mul(await tx.overrides!.gasPrice!) | ||
.add(baseCost); | ||
const formattedRecommendedBalance = ethers.utils.formatEther(recommendedETHBalance); | ||
throw new Error( | ||
`Not enough balance for deposit. Under the provided gas price, the recommended balance to perform a deposit is ${formattedRecommendedBalance} ETH` | ||
); | ||
} | ||
// For ETH token the value that the user passes to the estimation is the one which has the | ||
// value for the L2 comission substracted. | ||
let amountForEstimate: BigNumber; | ||
if (isETH(tx.token)) { | ||
amountForEstimate = BigNumber.from(dummyAmount); | ||
} else { | ||
amountForEstimate = await this.getBalanceL1(tx.token); | ||
if ((await this.getAllowanceL1(tx.token)) < amountForEstimate) { | ||
throw new Error('Not enough allowance to cover the deposit'); | ||
} | ||
} | ||
const l1GasLimit = await this.estimateGasDeposit({ | ||
...tx, | ||
amount: amountForEstimate, | ||
l2GasLimit | ||
}); | ||
const l1Fee = scaleGasLimit(l1GasLimit).mul(await tx.overrides.gasPrice); | ||
return [l1Fee, baseCost]; | ||
} | ||
async _getWithdrawalLog(withdrawalHash: BytesLike, index: number = 0) { | ||
@@ -432,8 +533,6 @@ const hash = ethers.utils.hexlify(withdrawalHash); | ||
} = tx; | ||
overrides.gasPrice ??= await this._providerL1().getGasPrice(); | ||
await insertGasPrice(this._providerL1(), overrides); | ||
const gasPriceForEstimation = overrides.maxFeePerGas || overrides.gasPrice; | ||
const baseCost = await this.getBaseCost({ | ||
gasPrice: await gasPriceForEstimation, | ||
gasPrice: await overrides.gasPrice, | ||
gasPerPubdataByte, | ||
@@ -522,19 +621,1 @@ gasLimit: l2GasLimit | ||
} | ||
/// @dev This method checks if the overrides contain a gasPrice (or maxFeePerGas), if not it will insert | ||
/// the maxFeePerGas | ||
async function insertGasPrice(l1Provider: ethers.providers.Provider, overrides: ethers.PayableOverrides) { | ||
if (!overrides.gasPrice && !overrides.maxFeePerGas) { | ||
const l1FeeData = await l1Provider.getFeeData(); | ||
// Sometimes baseFeePerGas is not available, so we use gasPrice instead. | ||
const baseFee = l1FeeData.lastBaseFeePerGas || l1FeeData.gasPrice; | ||
// ethers.js by default uses multiplcation by 2, but since the price for the L2 part | ||
// will depend on the L1 part, doubling base fee is typically too much. | ||
const maxFeePerGas = baseFee.mul(3).div(2).add(l1FeeData.maxPriorityFeePerGas); | ||
overrides.maxFeePerGas = maxFeePerGas; | ||
overrides.maxPriorityFeePerGas = l1FeeData.maxPriorityFeePerGas; | ||
} | ||
} |
@@ -44,2 +44,10 @@ import { utils, ethers, BigNumber, BigNumberish, BytesLike } from 'ethers'; | ||
// Currently, for some reason the SDK may return slightly smaller value than required for the L1->L2 | ||
// transaction. We will use a coefficient to ensure that the transaction will be accepted. | ||
export const L1_FEE_ESTIMATION_COEF_NUMERATOR = BigNumber.from(12); | ||
export const L1_FEE_ESTIMATION_COEF_DENOMINATOR = BigNumber.from(10); | ||
// This gas limit will be used for displaying the error messages when the users do not have enough fee. | ||
export const L1_RECOMMENDED_MIN_DEPOSIT_GAS_LIMIT = 400000; | ||
// The large L2 gas per pubdata to sign. This gas is enough to ensure that | ||
@@ -538,1 +546,5 @@ // any reasonable limit will be accepted. Note, that the operator is NOT required to | ||
} | ||
export function scaleGasLimit(gasLimit: BigNumber): BigNumber { | ||
return gasLimit.mul(L1_FEE_ESTIMATION_COEF_NUMERATOR).div(L1_FEE_ESTIMATION_COEF_DENOMINATOR); | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
885245
28161