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
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:
- When you have
contract p in your
storage type (outside of the
big_map), the contract will be parsed before your script runs.
- 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
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.