I am trying to perform a chain of contract calls with internal transfers.

I think my current contracts are very simple, but I run out of gas after only 4 internal transfers:

A script or one of its callee took more time than the operation said it would  
Id : proto.003-PsddFKi3.gas_exhausted.operation  
Category : temporary

When the involved calls will be complex, it won't be possible for me to perform calls between two or three contracts if I keep on getting the gas exhaustion error.

What causes this, and is there anything I can do about it?


1 Answer 1


You cannot exceed the hard_gas_limit_per_operation = 400000. It will probably be increased in future protocols, though (see e.g. the "Athens" proposals).

Internal transfers can use up a lot of gas.

First, there is a fixed cost of 10000 gas per transfer.

Second, and more importantly, when a contract is 'parsed', the contract's entire code and storage are read out of the Tezos database (except for the big_map). You pay gas according the binary size of the data read. The entire code is also 'parsed', in order to pull out the parameter type of the contract, and compare it to the expected type. This happens in at least two places:

  1. When you have contract p in your storage type (outside of the big_map), the contract will be parsed before your script runs.
  2. When your script executes the CONTRACT p Michelson instruction, the contract will be parsed.

So, this means that in order to TRANSFER_TOKENS to a scripted contract, you must parse it, paying gas proportional to its code and storage size. When that contract receives the transfer and is loaded to be executed, this cost, and more, will be paid again, before the destination script even starts running.

(It is completely unnecessary for the storage to be read from the database when parsing a contract. We could fix this trivially in a future protocol upgrade -- and we really should, it can lead to very surprising vulnerabilities. It will be less easy to fix the problem with code, but I expect it will be fixed someday, by splitting the code into several pieces in the database. Maybe this can happen when we add first-class entry points.)

So, one way to mitigate this is to make sure that every contract involved has small code and storage -- except for the big_map. You only pay gas for the contents of the big_map when you GET (and maybe UNPACK) them.

Unfortunately, this seems to mean that, today, the most optimal way to write complex contracts (especially when they are the destination for internal transfers) will be to use big_map bytes bytes as storage, and to put all the code and storage inside the big_map, loading it on demand, using UNPACK.

  • Thanks, I'll upvote the answer once I confirm via --dry-run that gas cost is increasing the standard limit in this chain of internal calls.
    – user_184
    Mar 28, 2019 at 17:56
  • It's not terribly usable, but note that you can also use tezos-client run script foo.tz on storage 'Pair whatever whatever' and input 'whatever' --track-stack, which will allow you to see the remaining gas at each step.
    – Tom
    Mar 28, 2019 at 19:43
  • Seems like --track-stack doesn't work or deprected. I get following error : "Unexpected command line option --track-stack." However, As you pointed out , gas limit is exhausting.
    – user_184
    Apr 17, 2019 at 15:15
  • oops, typo, it's --trace-stack
    – Tom
    Apr 17, 2019 at 16:18
  • yep, that works. Though they have added the details provided by this flag by default in failure logs.
    – user_184
    Apr 17, 2019 at 22:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.