-
Notifications
You must be signed in to change notification settings - Fork 622
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Difficulties Managing Gas Allocation for Chained Cross-Contract Calls #11977
Comments
Thanks for posting this issue in such great detail @rm-Umar I don't have a good solution for you, but hopefully I can explain why the gas allocation works the way it does and encourage you that you are on the right track to get this working. First of all we need to distinguish "gas burnt" from "gas used". The values you are quoting which are much lower than the attached gas are the "burnt" values. "Gas burnt" represents how much gas was actually spent on computation (burned) during the execution of that receipt. However, in the case that the receipt generated a new promise, this gas burnt will be lower than the "gas used". "Gas used" represents the total amount of gas that the receipt did something with -- including passing on to another receipt. In your You can use the nearblocks explorer to see this overestimation more clearly. Here is the link for your Perhaps you knew all this already and you are still wondering why Near works in this way. The key point is that all outgoing receipts from an execution, including callbacks, are generated immediately and they have their gas allocated to them at the time they are created. This design gives the guarantee that a callback will have a specified amount of gas regardless of what happens to the execution of the receipt the callback is waiting for. This is important because contracts are often designed under the assumption that its callback will always execute, even if the call itself fails. For example this gives the contract a chance to do error handling on the failed call. Of course, in theory it would be possible to have both a guaranteed minimum and also get any unused gas from the prior call. But this is not implemented in the Near protocol, and I think it would be a hard feature to use even if it was implemented. As you point out in your "callback hell" section, if you just pass the remaining gas along to the next call then you can get into a situation where you run out of gas and the chain stops without any opportunity for error handling. Similarly, if a contract relied on its prior call using less gas than it was given then there is a chance its callback would run out of gas if that call did actually use all its gas. The only way to guarantee a callback runs to completion is to give it enough gas upfront. This last point is exactly what leads to so much overestimation in how much gas is attached to promises. Developers must code defensively and attach as much gas as the worst case execution requires. So even though it might look ridiculous that Hopefully that makes it clear why this (obviously suboptimal) situation exists. Now, what can be done about it? I've heard rumours of an idea that we could loosen the 300 Tgas restriction such that it only applies to burnt gas instead of used gas. Then it would be possible to create much longer receipt chains. For example, you could attach 1000 gas to your initial transaction so that there is more gas available to assign to later promises, but it would still be true that no individual receipt in that chain of executions could burn more than 300 Tgas. This would be a protocol change and therefore have to go through the NEP process. I don't think this is an official proposal yet, but maybe it will be (you could maybe even propose it yourself to get the conversation going). Unfortunately, I do not think there is anything you can do about this as an application developer. If you are hitting the 300 Tgas limit then the only option would be to split what you are doing across multiple transactions. You could maybe use the yield/resume mechanism to help you save on some gas if there are parts of your application that can happen off-chain instead. |
Thanks for the detailed reply @birchmd things are clear to me now. I'll see if I can create a proposal. Thanks again for pointing me in the right direction. |
I’m currently developing a smart contract that requires multiple chained cross-contract calls. Specifically, I am using
ft_transfer_call
in a sequence of operations. The main issue I’m encountering is related to gas management, particularly when trying to allocate and efficiently utilize gas across these chained calls. I need to do multiple cross contract calls but the maximum gas limit is300Tgas
Problem Details:
1. Initial Gas Assignment:
Example Code:
2. Real-world Example with Excessive Gas Allocation:
120 TGas
to the ft_transfer_call, but it only consumed25.2 TGas
. This leaves a significant portion of gas unused, which could have been allocated to subsequent calls in the chain.25.2 TGas
, even though I assigned120 TGas upfront
. This inefficiency makes it difficult to manage gas dynamically across a series of chained contract calls.3. Chaining Calls with NEP-0264:
NEP-0264
to chain the calls and pass the remaining gas to subsequent functions using .then. However, the remaining gas is not passed directly; instead, gas is allocated based on weights, making it difficult to manage the gas efficiently across multiple chained calls.Example Scenario:
4. Callback Hell and Gas Allocation:
Example of Callback Hell:
Problem Summary:
-Failure of Subsequent Calls: Depending on how gas is allocated—either more to the initial call or the callback—some parts of the chain fail due to insufficient gas.
What I’m Looking For:
Any guidance on how to better handle gas allocation in these scenarios, or enhancements to NEP-0264 to facilitate this, would be greatly appreciated.
The text was updated successfully, but these errors were encountered: