Skip to content

Commit

Permalink
fix L1 gas estimation bug (#291) [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
rouzwelt authored Jan 20, 2025
1 parent 36fb1ed commit 2af8ca4
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 10 deletions.
8 changes: 3 additions & 5 deletions src/gas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export async function estimateGasCost(
const result = {
gas,
gasPrice,
l1Gas: 0n,
l1GasPrice: 0n,
l1Cost: 0n,
totalGasCost: gasPrice * gas,
Expand All @@ -36,14 +35,13 @@ export async function estimateGasCost(
if (typeof l1GasPrice !== "bigint") {
l1GasPrice = (await l1Signer_.getL1BaseFee()) as bigint;
}
const l1Gas = await l1Signer_.estimateL1Gas({
const l1Cost = await l1Signer_.estimateL1Fee({
to: tx.to,
data: tx.data,
});
result.l1Gas = l1Gas;
result.l1GasPrice = l1GasPrice;
result.l1Cost = l1Gas * l1GasPrice;
result.totalGasCost += result.l1Cost;
result.l1Cost = l1Cost;
result.totalGasCost += l1Cost;
} catch {}
}
return result;
Expand Down
42 changes: 42 additions & 0 deletions src/modes/interOrderbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,23 @@ export async function dryrun({
const estimation = await estimateGasCost(rawtx, signer, config, l1GasPrice);
l1Cost = estimation.l1Cost;
gasLimit = ethers.BigNumber.from(estimation.gas).mul(config.gasLimitMultiplier).div(100);

// include dryrun headroom gas estimation in otel logs
extendSpanAttributes(
spanAttributes,
{
gasLimit: estimation.gas.toString(),
totalCost: estimation.totalGasCost.toString(),
gasPrice: estimation.gasPrice.toString(),
...(config.isSpecialL2
? {
l1Cost: estimation.l1Cost.toString(),
l1GasPrice: estimation.l1GasPrice.toString(),
}
: {}),
},
"gasEst.headroom",
);
} catch (e) {
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
Expand Down Expand Up @@ -153,6 +170,10 @@ export async function dryrun({
// sender output which is already called above
if (config.gasCoveragePercentage !== "0") {
const headroom = (Number(config.gasCoveragePercentage) * 1.03).toFixed();
spanAttributes["gasEst.headroom.minBountyExpected"] = gasCost
.mul(headroom)
.div("100")
.toString();
task.evaluable.bytecode = await parseRainlang(
await getBountyEnsureRainlang(
ethers.utils.parseUnits(inputToEthPrice),
Expand Down Expand Up @@ -188,6 +209,23 @@ export async function dryrun({
}
rawtx.gas = gasLimit.toBigInt();
gasCost = gasLimit.mul(gasPrice).add(estimation.l1Cost);

// include dryrun final gas estimation in otel logs
extendSpanAttributes(
spanAttributes,
{
gasLimit: estimation.gas.toString(),
totalCost: estimation.totalGasCost.toString(),
gasPrice: estimation.gasPrice.toString(),
...(config.isSpecialL2
? {
l1Cost: estimation.l1Cost.toString(),
l1GasPrice: estimation.l1GasPrice.toString(),
}
: {}),
},
"gasEst.final",
);
task.evaluable.bytecode = await parseRainlang(
await getBountyEnsureRainlang(
ethers.utils.parseUnits(inputToEthPrice),
Expand All @@ -203,6 +241,10 @@ export async function dryrun({
takeOrdersConfigStruct,
task,
]);
spanAttributes["gasEst.final.minBountyExpected"] = gasCost
.mul(config.gasCoveragePercentage)
.div("100")
.toString();
} catch (e) {
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
Expand Down
42 changes: 42 additions & 0 deletions src/modes/intraOrderbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,23 @@ export async function dryrun({
const estimation = await estimateGasCost(rawtx, signer, config, l1GasPrice);
l1Cost = estimation.l1Cost;
gasLimit = ethers.BigNumber.from(estimation.gas).mul(config.gasLimitMultiplier).div(100);

// include dryrun headroom gas estimation in otel logs
extendSpanAttributes(
spanAttributes,
{
gasLimit: estimation.gas.toString(),
totalCost: estimation.totalGasCost.toString(),
gasPrice: estimation.gasPrice.toString(),
...(config.isSpecialL2
? {
l1Cost: estimation.l1Cost.toString(),
l1GasPrice: estimation.l1GasPrice.toString(),
}
: {}),
},
"gasEst.headroom",
);
} catch (e) {
// reason, code, method, transaction, error, stack, message
const isNodeError = containsNodeError(e as BaseError);
Expand Down Expand Up @@ -146,6 +163,10 @@ export async function dryrun({
// sender output which is already called above
if (config.gasCoveragePercentage !== "0") {
const headroom = (Number(config.gasCoveragePercentage) * 1.03).toFixed();
spanAttributes["gasEst.headroom.minBountyExpected"] = gasCost
.mul(headroom)
.div("100")
.toString();
task.evaluable.bytecode = await parseRainlang(
await getWithdrawEnsureRainlang(
signer.account.address,
Expand Down Expand Up @@ -190,6 +211,23 @@ export async function dryrun({
}
rawtx.gas = gasLimit.toBigInt();
gasCost = gasLimit.mul(gasPrice).add(estimation.l1Cost);

// include dryrun final gas estimation in otel logs
extendSpanAttributes(
spanAttributes,
{
gasLimit: estimation.gas.toString(),
totalCost: estimation.totalGasCost.toString(),
gasPrice: estimation.gasPrice.toString(),
...(config.isSpecialL2
? {
l1Cost: estimation.l1Cost.toString(),
l1GasPrice: estimation.l1GasPrice.toString(),
}
: {}),
},
"gasEst.final",
);
task.evaluable.bytecode = await parseRainlang(
await getWithdrawEnsureRainlang(
signer.account.address,
Expand All @@ -214,6 +252,10 @@ export async function dryrun({
rawtx.data = obInterface.encodeFunctionData("multicall", [
[clear2Calldata, withdrawInputCalldata, withdrawOutputCalldata],
]);
spanAttributes["gasEst.final.minBountyExpected"] = gasCost
.mul(config.gasCoveragePercentage)
.div("100")
.toString();
} catch (e) {
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
Expand Down
42 changes: 42 additions & 0 deletions src/modes/routeProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,23 @@ export async function dryrun({
gasLimit = ethers.BigNumber.from(estimation.gas)
.mul(config.gasLimitMultiplier)
.div(100);

// include dryrun headroom gas estimation in otel logs
extendSpanAttributes(
spanAttributes,
{
gasLimit: estimation.gas.toString(),
totalCost: estimation.totalGasCost.toString(),
gasPrice: estimation.gasPrice.toString(),
...(config.isSpecialL2
? {
l1Cost: estimation.l1Cost.toString(),
l1GasPrice: estimation.l1GasPrice.toString(),
}
: {}),
},
"gasEst.headroom",
);
} catch (e) {
// reason, code, method, transaction, error, stack, message
const isNodeError = containsNodeError(e as BaseError);
Expand Down Expand Up @@ -219,6 +236,10 @@ export async function dryrun({
// sender output which is already called above
if (config.gasCoveragePercentage !== "0") {
const headroom = (Number(config.gasCoveragePercentage) * 1.03).toFixed();
spanAttributes["gasEst.headroom.minBountyExpected"] = gasCost
.mul(headroom)
.div("100")
.toString();
task.evaluable.bytecode = await parseRainlang(
await getBountyEnsureRainlang(
ethers.utils.parseUnits(ethPrice),
Expand Down Expand Up @@ -254,6 +275,23 @@ export async function dryrun({
}
rawtx.gas = gasLimit.toBigInt();
gasCost = gasLimit.mul(gasPrice).add(estimation.l1Cost);

// include dryrun final gas estimation in otel logs
extendSpanAttributes(
spanAttributes,
{
gasLimit: estimation.gas.toString(),
totalCost: estimation.totalGasCost.toString(),
gasPrice: estimation.gasPrice.toString(),
...(config.isSpecialL2
? {
l1Cost: estimation.l1Cost.toString(),
l1GasPrice: estimation.l1GasPrice.toString(),
}
: {}),
},
"gasEst.final",
);
task.evaluable.bytecode = await parseRainlang(
await getBountyEnsureRainlang(
ethers.utils.parseUnits(ethPrice),
Expand All @@ -269,6 +307,10 @@ export async function dryrun({
takeOrdersConfigStruct,
task,
]);
spanAttributes["gasEst.final.minBountyExpected"] = gasCost
.mul(config.gasCoveragePercentage)
.div("100")
.toString();
} catch (e) {
const isNodeError = containsNodeError(e as BaseError);
const errMsg = errorSnapshot("", e);
Expand Down
36 changes: 36 additions & 0 deletions test/findOpp.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ describe("Test find opp", async function () {
marketPrice: formatUnits(getCurrentPrice(vaultBalance)),
route: expectedRouteVisual,
clearModePick: "rp4",
"gasEst.final.gasLimit": gasLimitEstimation.toString(),
"gasEst.final.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.final.gasPrice": gasPrice.toString(),
"gasEst.final.minBountyExpected": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasLimit": gasLimitEstimation.toString(),
"gasEst.headroom.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasPrice": gasPrice.toString(),
"gasEst.headroom.minBountyExpected": gasLimitEstimation
.mul(gasPrice)
.mul(103)
.div(100)
.toString(),
},
};
assert.deepEqual(result, expected);
Expand Down Expand Up @@ -237,6 +249,18 @@ describe("Test find opp", async function () {
foundOpp: true,
maxInput: vaultBalance.toString(),
clearModePick: "inter",
"gasEst.final.gasLimit": gasLimitEstimation.toString(),
"gasEst.final.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.final.gasPrice": gasPrice.toString(),
"gasEst.final.minBountyExpected": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasLimit": gasLimitEstimation.toString(),
"gasEst.headroom.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasPrice": gasPrice.toString(),
"gasEst.headroom.minBountyExpected": gasLimitEstimation
.mul(gasPrice)
.mul(103)
.div(100)
.toString(),
},
};
assert.deepEqual(result, expected);
Expand Down Expand Up @@ -338,6 +362,18 @@ describe("Test find opp", async function () {
oppBlockNumber,
foundOpp: true,
clearModePick: "intra",
"gasEst.final.gasLimit": gasLimitEstimation.toString(),
"gasEst.final.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.final.gasPrice": gasPrice.toString(),
"gasEst.final.minBountyExpected": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasLimit": gasLimitEstimation.toString(),
"gasEst.headroom.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasPrice": gasPrice.toString(),
"gasEst.headroom.minBountyExpected": gasLimitEstimation
.mul(gasPrice)
.mul(103)
.div(100)
.toString(),
},
};
assert.deepEqual(result, expected);
Expand Down
8 changes: 3 additions & 5 deletions test/gas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe("Test gas", async function () {
// mock L1 signer for L2 client
const l1Signer = {
getL1BaseFee: async () => 20n,
estimateL1Gas: async () => 5n,
estimateL1Fee: async () => 5n,
};
// mock normal L1 signer
const signer = {
Expand All @@ -32,10 +32,9 @@ describe("Test gas", async function () {
const expected1 = {
gas: 55n,
gasPrice: 2n,
l1Gas: 5n,
l1GasPrice: 20n,
l1Cost: 20n * 5n,
totalGasCost: 2n * 55n + 20n * 5n,
l1Cost: 5n,
totalGasCost: 2n * 55n + 5n,
};
assert.deepEqual(result1, expected1);

Expand All @@ -45,7 +44,6 @@ describe("Test gas", async function () {
const expected2 = {
gas: 55n,
gasPrice: 2n,
l1Gas: 0n,
l1GasPrice: 0n,
l1Cost: 0n,
totalGasCost: 2n * 55n,
Expand Down
24 changes: 24 additions & 0 deletions test/mode-interOrderbook.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ describe("Test inter-orderbook dryrun", async function () {
oppBlockNumber,
foundOpp: true,
maxInput: vaultBalance.toString(),
"gasEst.final.gasLimit": gasLimitEstimation.toString(),
"gasEst.final.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.final.gasPrice": gasPrice.toString(),
"gasEst.final.minBountyExpected": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasLimit": gasLimitEstimation.toString(),
"gasEst.headroom.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasPrice": gasPrice.toString(),
"gasEst.headroom.minBountyExpected": gasLimitEstimation
.mul(gasPrice)
.mul(103)
.div(100)
.toString(),
},
};
assert.deepEqual(result, expected);
Expand Down Expand Up @@ -263,6 +275,18 @@ describe("Test inter-orderbook find opp", async function () {
oppBlockNumber,
foundOpp: true,
maxInput: vaultBalance.toString(),
"gasEst.final.gasLimit": gasLimitEstimation.toString(),
"gasEst.final.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.final.gasPrice": gasPrice.toString(),
"gasEst.final.minBountyExpected": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasLimit": gasLimitEstimation.toString(),
"gasEst.headroom.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasPrice": gasPrice.toString(),
"gasEst.headroom.minBountyExpected": gasLimitEstimation
.mul(gasPrice)
.mul(103)
.div(100)
.toString(),
},
};
assert.deepEqual(result, expected);
Expand Down
24 changes: 24 additions & 0 deletions test/mode-intraOrderbook.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ describe("Test intra-orderbook dryrun", async function () {
spanAttributes: {
oppBlockNumber,
foundOpp: true,
"gasEst.final.gasLimit": gasLimitEstimation.toString(),
"gasEst.final.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.final.gasPrice": gasPrice.toString(),
"gasEst.final.minBountyExpected": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasLimit": gasLimitEstimation.toString(),
"gasEst.headroom.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasPrice": gasPrice.toString(),
"gasEst.headroom.minBountyExpected": gasLimitEstimation
.mul(gasPrice)
.mul(103)
.div(100)
.toString(),
},
};
assert.deepEqual(result, expected);
Expand Down Expand Up @@ -263,6 +275,18 @@ describe("Test intra-orderbook find opp", async function () {
spanAttributes: {
oppBlockNumber,
foundOpp: true,
"gasEst.final.gasLimit": gasLimitEstimation.toString(),
"gasEst.final.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.final.gasPrice": gasPrice.toString(),
"gasEst.final.minBountyExpected": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasLimit": gasLimitEstimation.toString(),
"gasEst.headroom.totalCost": gasLimitEstimation.mul(gasPrice).toString(),
"gasEst.headroom.gasPrice": gasPrice.toString(),
"gasEst.headroom.minBountyExpected": gasLimitEstimation
.mul(gasPrice)
.mul(103)
.div(100)
.toString(),
},
};
assert.deepEqual(result, expected);
Expand Down
Loading

0 comments on commit 2af8ca4

Please sign in to comment.