You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Provide a brief description of the functionality you're trying to implement and the issue you are running into.
I'm trying to port a test of a contract that by design artificially burns through a part of the entire gas available in the block. This is a safety measure throttling the usage of a finite off-chain resource, so it's crucial. I'm getting a bunch of problems, so I wrote a short test trying to make sense of what zkSync is doing:
contractGasInfo {
function gasLeft() publicviewreturns (uint256) {
returngasleft();
}
function gasLimit() publicviewreturns (uint256) {
returnblock.gaslimit;
}
}
contractGasTestisTest, TestExt, GasInfo {
function testGas() public {
GasInfo gasInfo =newGasInfo();
console.log("Gas limit default VM local ", block.gaslimit);
console.log("Gas left default VM local ", gasleft());
console.log("Gas limit default VM self call ", this.gasLimit());
console.log("Gas left default VM self call ", this.gasLeft());
console.log("Gas limit default VM external call ", gasInfo.gasLimit());
console.log("Gas left default VM external call ", gasInfo.gasLeft());
vmExt.zkVm(true);
console.log("Gas limit zkVM local ", block.gaslimit);
console.log("Gas left zkVM local ", gasleft());
console.log("Gas limit zkVM self call ", this.gasLimit());
console.log("Gas left zkVM self call ", this.gasLeft());
console.log("Gas limit zkVM external call ", gasInfo.gasLimit());
console.log("Gas left zkVM external call ", gasInfo.gasLeft());
vmExt.zkVm(false);
console.log("Gas limit EVM local ", block.gaslimit);
console.log("Gas left EVM local ", gasleft());
console.log("Gas limit EVM self call ", this.gasLimit());
console.log("Gas left EVM self call ", this.gasLeft());
console.log("Gas limit EVM external call ", gasInfo.gasLimit());
console.log("Gas left EVM external call ", gasInfo.gasLeft());
}
}
The output:
[FAIL: EvmError: Revert] testGas() (gas: 1652275)
Logs:
Gas limit default VM local 1073741824
Gas left default VM local 1072520839
Gas limit default VM self call 1073741824
Gas left default VM self call 1055760156
Gas limit default VM external call 1125899906842624
Gas left default VM external call 77421921
Gas limit zkVM local 1073741824
Gas left zkVM local 1072296644
Gas limit zkVM self call 1073741824
Gas left zkVM self call 1055539458
Gas limit zkVM external call 1125899906842624
Gas left zkVM external call 77421921
Gas limit EVM local 1073741824
Gas left EVM local 1072072439
Gas limit EVM self call 1073741824
Gas left EVM self call 1055318752
Traces:
[1652275] GasTest::testGas()
├─ [1164234] → new GasInfo@0xF9E9ba9Ed9B96AB918c74B21dD0f1D5f2ac38a30
│ └─ ← [Return] 800 bytes of code
├─ [0] console::log("Gas limit default VM local ", 1073741824 [1.073e9]) [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Gas left default VM local ", 1072520839 [1.072e9]) [staticcall]
│ └─ ← [Stop]
├─ [256] GasTest::gasLimit() [staticcall]
│ └─ ← [Return] 1073741824 [1.073e9]
├─ [0] console::log("Gas limit default VM self call ", 1073741824 [1.073e9]) [staticcall]
│ └─ ← [Stop]
├─ [280] GasTest::gasLeft() [staticcall]
│ └─ ← [Return] 1055760156 [1.055e9]
├─ [0] console::log("Gas left default VM self call ", 1055760156 [1.055e9]) [staticcall]
│ └─ ← [Stop]
├─ [108862] GasInfo::gasLimit() [staticcall]
│ └─ ← [Return] 1125899906842624 [1.125e15]
├─ [0] console::log("Gas limit default VM external call ", 1125899906842624 [1.125e15]) [staticcall]
│ └─ ← [Stop]
├─ [108302] GasInfo::gasLeft() [staticcall]
│ └─ ← [Return] 77421921 [7.742e7]
├─ [0] console::log("Gas left default VM external call ", 77421921 [7.742e7]) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::zkVm(true) [staticcall]
│ └─ ← [Return]
├─ [0] console::log("Gas limit zkVM local ", 1073741824 [1.073e9]) [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Gas left zkVM local ", 1072296644 [1.072e9]) [staticcall]
│ └─ ← [Stop]
├─ [256] GasTest::gasLimit() [staticcall]
│ └─ ← [Return] 1073741824 [1.073e9]
├─ [0] console::log("Gas limit zkVM self call ", 1073741824 [1.073e9]) [staticcall]
│ └─ ← [Stop]
├─ [280] GasTest::gasLeft() [staticcall]
│ └─ ← [Return] 1055539458 [1.055e9]
├─ [0] console::log("Gas left zkVM self call ", 1055539458 [1.055e9]) [staticcall]
│ └─ ← [Stop]
├─ [108862] GasInfo::gasLimit() [staticcall]
│ └─ ← [Return] 1125899906842624 [1.125e15]
├─ [0] console::log("Gas limit zkVM external call ", 1125899906842624 [1.125e15]) [staticcall]
│ └─ ← [Stop]
├─ [108302] GasInfo::gasLeft() [staticcall]
│ └─ ← [Return] 77421921 [7.742e7]
├─ [0] console::log("Gas left zkVM external call ", 77421921 [7.742e7]) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::zkVm(false) [staticcall]
│ └─ ← [Return]
├─ [0] console::log("Gas limit EVM local ", 1073741824 [1.073e9]) [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Gas left EVM local ", 1072072439 [1.072e9]) [staticcall]
│ └─ ← [Stop]
├─ [256] GasTest::gasLimit() [staticcall]
│ └─ ← [Return] 1073741824 [1.073e9]
├─ [0] console::log("Gas limit EVM self call ", 1073741824 [1.073e9]) [staticcall]
│ └─ ← [Stop]
├─ [280] GasTest::gasLeft() [staticcall]
│ └─ ← [Return] 1055318752 [1.055e9]
├─ [0] console::log("Gas left EVM self call ", 1055318752 [1.055e9]) [staticcall]
│ └─ ← [Stop]
├─ [0] GasInfo::gasLimit() [staticcall]
│ └─ ← [Stop]
└─ ← [Revert] EvmError: Revert
I don't quite understand what's going on here, I have a few questions:
Which contracts are living in which contexts? Why does an external call to the test contract itself doesn't switch the context to zkVM? Why the gasInfo contract doesn't exist in the EVM context? Is staying in the EVM context useful for anything at all?
Why is the block gas limit so high in the zkVM context? Why is the actually available gas so low in comparison? Does this behavior exist only in the local environment or on the actual blockchain too? My contract needs to burn through a fraction of the gas available in the block, so that's a very important aspect.
Why does the zkVM context reset the gas usage after each context switch? How can I measure gas usage of my contracts?
In Foundry, Solidity is used as a "scripting language" to run some test/script code that results in calls made to the actual Etherum network (or a simulation of that), the same way it is done elsewhere using any other programming language. Given that yields EVM bytecode, there is some overlap between what happens in the script and what happens in the blockchain, the test/script itself is a transaction made to a local vm instance that triggers the execution of the test/script code but this is only because that's the only way the code will get executed. Furthermore, this also allows to modify what's happening inside the EVM instance from the Solidity code itself (e.g: print EVM state to stdout, apply cheatcodes). We keep the EVM to execute the test/script because it lets us leverage a lot of the implementation of the foundry features, without needing to reimplement them on zkVM. This means that internally, an evm instance is created, the test/script deployed on it and a transaction made to the evm instace in order to execute it. Now what the user wants to do when running on the zkVM context is interact with the Era protocol, so we keep a zk state (just the state in the db, no zkVM yet) that simulates the blockhain and when on the script there's a statement that has the intent of modifying/querying the state (a call/create, some specific cheatcodes), we intercept it and do the proper modifications of that state. In particular a call/create will have us spawn an instance of a zkVM every time, run the call/create transaction and commit those changes back to the state. So in summary, when on zkVM context, anything that does not intend to modify/query EraVM state is run on the EVM (the "scripting" part), everything that intends to modify or query EraVM state (the "blockchain" part) will impact or get the values from to the zkVM context. More info in the "Overview" section on the book.
I am not sure what zkVM returns with block.gaslimit (maybe someone else here can clarify), that value seems to be the bootloader's gas limit (a bootloader run makes for a batch which includes several blocks), the cap you get however is because zkVM limits the amount of gas that can be used for a single transaction execution to 80_000_000 (this is both locally and in the actual blockhain). You can use more to publish factory deps but there's a limit as well, that can also be clarified better by someone involved in the protocol design but you can probably not use execution alone to burn through an arbitrary amount of gas in a single transaction.
The gas is reset because each call done in the zkVM involves a new transaction alltogether, spawning a new vm and running a new bootloader program (for zksync context, the only persitent thing is the state). When returning a result back to the EVM we return the gas consumed during the call/create (not just execution gas, but all underlying gas costs), gas used in each transaction can be approximated like that, there's also forge test --gas-report --zksync which might give useful info. There's the caveat that gas values on zkVM vary with gasPerPubdata which is sensible to the network values and varies from block to block, see the docs. There's some explanation about it in the book but how the specific gas values are set in foundry is a bit out of date.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Team or Project
Drips
Environment
Testnet
Select the Dev Tool you are using
Foundry
Provide the version of the tool (if applicable)
forge 0.0.2 (ddf8b75 2025-01-03T00:25:18.327459215Z)
Provide a brief description of the functionality you're trying to implement and the issue you are running into.
I'm trying to port a test of a contract that by design artificially burns through a part of the entire gas available in the block. This is a safety measure throttling the usage of a finite off-chain resource, so it's crucial. I'm getting a bunch of problems, so I wrote a short test trying to make sense of what zkSync is doing:
The output:
I don't quite understand what's going on here, I have a few questions:
gasInfo
contract doesn't exist in the EVM context? Is staying in the EVM context useful for anything at all?Repo Link (Optional)
No response
Additional Details
No response
Beta Was this translation helpful? Give feedback.
All reactions