Links

Fuel Tanks

This document outlines how to interact and modify a fuel tank.

Overview

A fuel tank is a pool of funds that can be used by multiple accounts to dispatch calls. It is created with the create_fuel_tank extrinsic, and it can have various settings and rules based on which fuel tank would pay for transaction fees and provide storage deposits. Calls are dispatched with the dispatch extrinsic. A fuel tank's settings can be modified by using the mutate_fuel_tank extrinsic.

Terminology

Accounts

Fuel tanks require a user to have an account registered with the fuel tank before they can dispatch calls. The fuel tank owner can decide if they want users to create their own accounts by modifying the user_account_management field, and accounts can even be created at the same time as dispatching by using the dispatch_and_touch extrinsic.
Accounts are added with the add_account or batch_add_account extrinsics. They are removed with the remove_account or batch_remove_account extrinsics.

Rules

When a call is dispatched, it must be dispatched through a rule set. Each rule set can contain multiple rules that determine if the call is valid or not. A fuel tank can contain multiple rule sets.
Rules can be added when the fuel tank is created. They can also be modified by using the insert_rule_set or remove_rule_set extrinsics. A rule cannot be removed if it is storing user data because this would lead to orphaned data. The user data must be removed from all accounts first, using the remove_account_rule_data extrinsic.
Each rule set can contain any combination of the following rules:
  1. 1.
    Whitelisted callers
Accounts allowed to dispatch. Any other accounts that dispatch will fail.
  1. 2.
    Whitelisted collections
Collection ids that dispatch is allowed to call. If other collections are called through dispatch, it will fail.
  1. 3.
    Max fuel burn per transaction
Maximum fuel that can be consumed in a single transaction
  1. 4.
    User fuel budget
Fuel budget for a user. Includes consumption in all rule sets.
  1. 5.
    Tank fuel budget
Fuel budget for a tank. Includes consumption in all rule sets.
  1. 5.
    Require token
Require the caller to have a token. If they don’t have it, the call will fail.
  1. 5.
    Permitted calls
Calls that are permitted for this rule set. Other calls will fail.
  1. 5.
    Permitted extrinsics
Extrinsics that are permitted for this rule set, all other extrinsics will fail. While setting this rule, note that the params passed to the extrinsic does not matter, only the function name matters. For example, in the below image, the multiTokens->transfer call is permitted, no matter what parameters are passed.

Freezing

Some changes on the fuel tank require the fuel tank or a rule set to be frozen. While a fuel tank or rule set is frozen, no dispatches are allowed. To apply a freeze, call schedule_mutate_freeze_state
with the
is_frozen parameter set to true.
Then call any extrinsics to make any changes you want. Then call schedule_mutate_freeze_state again with is_frozen set to false.
You can either freeze the entire fuel tank, or you can freeze an individual rule set. It is recommended to only freeze the rule set you need to modify if possible. Some mutations, like removing an account, require the entire fuel tank to be frozen.

Descriptor

Descriptor is the list of all the data used to create a fuel tank. It contains, name, account_management_rules, rule_sets and other configuration information related to the fuel tank.

Why use a fuel tank?

A fuel tank can be created by anyone using the create_fuel_tank extrinsic function, once the fuel tank is created, the owner can add funds to it and subsidise transactions that are permitted by the fuel tank. The primary function of a fuel tank is to allow a user to interact with the efinity parachain without paying transaction fees (called gas fees in Ethereum).
Having transaction fees is necessary to ensure parachain security. But often this ends up being a barrier for adoption since a user cannot use a parachain without first acquiring some tokens to pay for transaction fees. A fuel tank will subsidise (pay a portion of) or completely cover transaction cost for the user subject to rules set by the fuel tank creator.

How to create a fuel tank?

This guide uses a Custom PolkadotJS explorer pages, which are available at https://console.enjin.io/, and Fuel Tanks page is located in Network tab.
Create a new fuel tank by providing a descriptor. Extrinsic generates a discrete AccountId for the fuel tank based on passed in parameters, it takes a storage deposit and emits FuelTankCreated event in the success case. Creation of already existing fuel tank will result in FuelTankAlreadyExists error, while duplicate rule kinds within a rule set will result in DuplicateRuleKinds
To create a new fuel tank using the explorer, head to Network -> Fuel tanks -> Create fuel tank. This page allows you to create a new fuel tank by specifying the following options:
  • Name: Name for the fuel tank
  • User Account Management: (Optional) Allows setting the permission level to add new accounts to the fuel tank
  • Provides Deposit: boolean -> Allows specifying if Fuel Tank is providing a storage deposit for operations that require it
  • Account Rules: (Optional) Allows specifying rules that are evaluated before an account is added to the fuel tank
  • Dispatch Rules: (Optional) Allows specifying rules that are evaluated before an extrinsic is executed via the fuel tank
Even though the account rules and dispatch rules are optional, it is recommended to have some rules setup for your fuel tank, a fuel tank without any rules is open to anyone without limits.
This image shows how to create a fuel tank, with some basic rules, in the following fuel tank configuration only the account Alice (test account) can join the fuel tank and only Alice can dispatch operations via the fuel tank.

How to modify a fuel tank?

To mutate or modify a fuel tank, the caller must be the owner of the fuel tank, otherwise call will result in NoPermission error. Tank must be frozen in order to mutate it, otherwise call will result in RequiresFrozenTankOrRuleset error. Duplicate rule kinds within a rule set will result in DuplicateRuleKinds. In success case emits FuelTankMutated event
To mutate a fuel tank using the explorer, head to Network -> Fuel tanks. Mutating a fuel tank involves three steps 1. Freeze the fuel tank 2. Mutate the fuel tank 3. Unfreeze the fuel tank
  1. 1.
    To Freeze the fuel tank, head to Network -> Fuel tanks, this will present a list of all fuel tanks, select the fuel tank you wish to mutate and select freeze/unfreeze. In the next dialog box, set is_frozen to true and execute the transaction
  1. 2.
    To mutate the fuel tank, head to Network -> Fuel Tanks -> under specific fuel tank -> Mutate
In the modal, select the Account (the owner of the fuel tank) and select the fuel tank id (you can get this from the fuel tanks page in the previous step) and add any modifications to the fuel tank.
In this image, we are changing the providesDeposit to true
If successful, you should see the event with you changes in the explorer Network tab
  1. 3.
    To unfreeze the fuel tank, head to Network -> Fuel tanks, and perform the same step but select the is_frozen to false

Add an account to fuel tank

Adds a new fuel tank user account, which not only allows using fuel tank, but also stores user consumption and rule set data. An account can be created only if account rules are successfully validated. An account is required to dispatch calls. A storage deposit is required, and may be paid by the user or the fuel tank, depending on the settings. Could fail with NoPermission if caller is not the owner and user management settings dont allow users to create accounts on their own. Creation of already existing account withing a tank will result in AccountAlreadyExists error. In case some of account rules cannot validate the caller, rule specific error will be returned. In success case emits AccountAdded event
To add an account to a fuel tank using the explorer, head to Network -> Fuel tanks,
  • Select the fuel tank and click add account
  • Select account to be added.
  • On success you should see an AccountAdded event

Remove an account from fuel tank

Removes a user account from a fuel tank and returns the storage deposits. Fuel tank must be frozen for operation to succeed, otherwise call will result in RequiresFrozenTank error. Account must not contain any rule data, otherwise call will result in AccountContainsRuleData error. In success case emits AccountRemoved event
To remove an account from a fuel tank using the explorer, head to Network -> Fuel tanks, and follow similar instructions to remove an account
  • select the fuel tank and click remove account

Remove an account rule data

As part of enforcing the budget and max spending limits of the fuel tank, the fuel tank stores data for every account. The remove_account_rule_data allows to remove a user account rule data if it exists. Requires the fuel tank or the rule set to be frozen, otherwise will result in RequiresFrozenTankOrRuleset error. Only callable by the fuel tank's owner, otherwise will result in NoPermission error. In success case emits AccountRuleDataRemoved event.
Similar to the process of mutating a Fuel Tank, the Fuel Tank must be frozen for the operation to succeed, otherwise the call will result in an error.
To remove an account rule data, head to Developer -> Extrinsic -> fuelTanks -> removeAccountRuleData, and select the tankId, accountId to remove and the rule set and rule type to remove data from.
In this example, we are removing Bob account data from the fuel budget

Dispatch a transaction using a fuel tank

Dispatch a call through the fuel tank, where the tank would pay for transaction fees and, if configured, provide a storage deposit. All calls are subject to rule set evaluation and would result in rule specific errors in case of failure. In case the inner call fails, the DispatchFailed event will be emitted with wrapped dispatch error inside. In success case emits Dispatched event.
To dispatch a transaction using a fuel tank, head to Network -> Fuel tanks, select the fuel tank you wish to use and select Dispatch.
Inside the dialog box that opens, select
  • The extrinsic you want to call via the fuel tank,
  • The rule set id you wish to apply to the transaction
  • The extrinsic to execute
  • If willing to pay for transaction fees that execeed the fuel tank budget threshold
In this example, Bob is using the fuel tank to transfer 1 EFI to Alice
On success, you should see the CallDispatched event

More Examples

Create a fuel tank that pays for transactions to your game

Lets say you are a game developer, and you have used the efinity multitokens pallet to represent some in-game assets. You want to subsidise players that want to acquire these in-game assets. Your best option is to create a fuel tank but lets set some baseline rules
  1. 1.
    You only want to subsidise transactions that are connected to your game
  2. 2.
    You want to put an upper limit on how much fee to subside for a single transaction to prevent abuse
  3. 3.
    You want to encourage more players to try out the fuel tank, so you also need a rolling budget to ensure your fuel tank is not depleted by a few heavy hitters initially.
There are many ways to structure the fuel tank rules to get this setup working, but lets use the WhitelistedCollections rule as the basis for our fuel tank rules.
  1. 1.
    Now lets assume that you have a lot of in-game assets (tokens) all grouped under a single collection_id. We are going to create a fuel tank that allows transactions that deal with these collection_id only, any other transactions will be rejected. This takes care of rule 1
  2. 2.
    We will use the MaxFuelBurnPerTransaction rule to set the max fuel burn in a single transaction to 1 EFI
  3. 3.
    We will set the TankFuelBudget rule to 2 EFI and allow it to reset every 5 blocks so that it is possible to only deplete 2 EFI every 5 block interval.
The create fuel tank transaction, will look like this:
Our fuel tank is now created, before we can test it, we have to add some funds to it, to fund the fuel tank, head to Network -> My fuel tanks, select the appropriate fuel tank and select Fund
Now that our fuel tank is funded, lets test to make sure that the rules are working as intended:
Lets use a new account, to try to dispatch a balance transfer transaction via the fuel tank We use the Ferdie account to transfer 1 EFI to Alice, this should be rejected by our fuel tank since we have setup the rules to only allow transactions that deal with our collection_id.
As expected, the transaction is rejected with InvalidTransaction error
Lets try to do an expected transaction, here we create a dispatch call for a transaction that sets the atttibutes for a token withint the collection_id permitted. We will see that the transaction suceeds this time

Extrinsics

create_fuel_tank
Create a new fuel tank by providing a descriptor. Extrinsic generates a discrete AccountId for the fuel tank based on passed in parameters, it takes a storage deposit and emits FuelTankCreated event in the success case. Creation of already existing fuel tank will result in FuelTankAlreadyExists error, while duplicate rule kinds within a rule set will result in DuplicateRuleKinds
mutate_fuel_tank
Applies provided mutation to the fuel tank. Caller must be the owner of the fuel tank, otherwise call will result in NoPermission error. Tank must be frozen in order to mutate it, otherwise call will result in RequiresFrozenTankOrRuleset error. Duplicate rule kinds within a rule set will result in DuplicateRuleKinds. In success case emits FuelTankMutated event
add_account
Adds a new fuel tank user account, which not only allows using fuel tank, but also stores user consumption and rule set data. An account can be created only if account rules are successfully validated. An account is required to dispatch calls. A storage deposit is required, and may be paid by the user or the fuel tank, depending on the settings. Could fail with NoPermission if caller is not the owner and user management settings dont allow users to create accounts on their own. Creation of already existing account withing a tank will result in AccountAlreadyExists error. In case some of account rules cannot validate the caller, rule specific error will be returned. In success case emits AccountAdded event
remove_account
Removes a user account from a fuel tank and returns the storage deposits. Fuel tank must be frozen for operation to succeed, otherwise call will result in RequiresFrozenTank error. Account must not contain any rule data, otherwise call will result in AccountContainsRuleData error. In success case emits AccountRemoved event
remove_account_rule_data
Remove account rule data if it exists. Requires the fuel tank or the rule set to be frozen, otherwise will result in RequiresFrozenTankOrRuleset error. Only callable by the fuel tank's owner, otherwise will result in NoPermission error. In success case emits AccountRuleDataRemoved event
dispatch
Dispatch a call through the fuel tank, where the tank would pay for transaction fees and, if configured, provide a storage deposit. All calls are subject to rule set evaluation and would result in rule specific errors in case of failure. In case the inner call fails, the DispatchFailed event will be emitted with wrapped dispatch error inside. In success case emits Dispatched event
dispatch_and_touch
Same as dispatch, but also does create an account for a caller with add_account operation if it doesnt exist. Fuel tank's user_account_management settings must allow self-service account creation, otherwise call will result in NoPermission error.
schedule_mutate_freeze_state
Schedule mutating of fuel tank or rule set is_frozen state for on_finalize to execute with help of process_freeze_queue helper function. Additional 1 read and 1 write are added to account for on_finalize storage operations. Only callable by the fuel tank's owner, otherwise will result in NoPermission error. In case queue already has maximum number of items, will result in FreezeQueueFull error. In success case emits MutateFreezeStateScheduled event
insert_rule_set
Insert a new rule set into a fuel tank. It can be a new rule set or it can replace an existing one. If it is replacing a rule set, a rule that is storing data on any accounts cannot be removed and will result in CannotRemoveRuleThatIsStoringAccountData error. Use remove_account_rule_data to remove the data first. If a rule is being replaced, it will be mutated with the new parameters, and it will maintain any persistent data it already has. Fuel tank or rule set must be frozen, otherwise will result in RequiresFrozenTankOrRuleset error. If fuel tank already has maximum number of rule sets, will result in MaxRuleSetsExceeded error. Duplicate rule kinds within a rule set will result in DuplicateRuleKinds. In success case emits RuleSetInserted event
remove_rule_set
Remove rule set from a fuel tank. A rule that is storing data on any accounts cannot be removed and will result in CannotRemoveRuleThatIsStoringAccountData error. Use remove_account_rule_data to remove the data first. This is only callable by the fuel tank's owner, otherwise will result in NoPermission error. Fuel tank or rule set must be frozen, otherwise will result in RequiresFrozenTankOrRuleset error. In success case emits RuleSetRemoved event
batch_add_account
Same as add_account but takes a list of AccountIds to add into a fuel tank
batch_remove_account
Same as remove_account but takes a list of AccountIds to remove from a fuel tank
force_set_consumption
Force set the fuel tank consumption. If user_id is Some, it sets the consumption for that account. If it is None, it sets the consumption on the fuel tank directly. Currently only callable by fuel tank owner and sudo account, otherwise will result in NoPermission error
destroy_fuel_tank
Destroy the fuel tank. Only callable by owner, otherwise will result in NoPermission error. The fuel tank must be frozen, otherwise will result in DestroyUnfrozenTank error. Can only be destroyed if all accounts are removed, otherwise will result in DestroyWithExistingAccounts error. Returns the storage deposit and remaining balance to the fuel tank owner. In success case emits FuelTankDestroyed event
© 2023 Enjin Pte. Ltd.