Refunds
When execution of a receipt fails or there is some unused amount of prepaid gas left after a function call, the Runtime generates refund receipts.
The are 2 types of refunds:
- Refunds for the failed receipt for attached deposits. Let's call them deposit refunds.
- Refunds for the unused gas and fees. Let's call them gas refunds.
Refund receipts are identified by having predecessor_id == "system"
. They are also special because they don't cost any gas to generate or execute. As a result, they also do not contribute to the block gas limit.
If the execution of a refund fails, the refund amount is burnt.
The refund receipt is an ActionReceipt
that consists of a single action Transfer
with the deposit
amount of the refund.
Deposit Refunds
Deposit refunds are generated when an action receipt fails to execute. All attached deposit amounts are summed together and
sent as a refund to a predecessor_id
(because only the predecessor can attach deposits).
Deposit refunds have the following fields in the ActionReceipt
:
signer_id
issystem
signer_public_key
is ED25519 key with data equal to 32 bytes of0
.
Gas Refunds
Gas refunds are generated when a receipt used the amount of gas lower than the attached amount of gas.
If the receipt execution succeeded, the gas amount is equal to prepaid_gas + execution_gas - used_gas
.
If the receipt execution failed, the gas amount is equal to prepaid_gas + execution_gas - burnt_gas
.
The difference between burnt_gas
and used_gas
is the used_gas
also includes the fees and the prepaid gas of
newly generated receipts, e.g. from cross-contract calls in function calls actions.
Then the gas amount is converted to tokens by multiplying by the gas price at which the original transaction was generated.
Gas refunds have the following fields in the ActionReceipt
:
signer_id
is the actualsigner_id
from the receipt that generates this refund.signer_public_key
is thesigner_public_key
from the receipt that generates this refund.
Access Key Allowance refunds
When an account used a restricted access key with FunctionCallPermission
, it may have had a limited allowance.
The allowance was charged for the full amount of receipt fees including full prepaid gas.
To refund the allowance we distinguish between Deposit refunds and Gas refunds using signer_id
in the action receipt.
If the signer_id == receiver_id && predecessor_id == "system"
it means it's a gas refund and the runtime should try to refund the allowance.
Note, that it's not always possible to refund the allowance, because the access key can be deleted between the moment when the transaction was issued and when the gas refund arrived. In this case we use the best effort to refund the allowance. It means:
- the access key on the
signer_id
account with the public keysigner_public_key
should exist - the access key permission should be
FunctionCallPermission
- the allowance should be set to
Some
limited value, instead of unlimited allowance (None
) - the runtime uses saturating add to increase the allowance, to avoid overflows