Fee Grants
Fee grants allow an account to pay for the transaction fees of another account, which is especially useful for onboarding new users who might not have enough tokens to cover transaction fees. Fee grants use the Cosmos Fee Grant (opens in a new tab) module and can be set up so that one account (the granter) can pay for the transaction fees of another account (the grantee). The granter specifies conditions under which the fees will be paid as well as limits.
Fee Allowance types
-
BasicAllowance (opens in a new tab)
BasicAllowance
implements Allowance with a one-time grant of tokens that optionally expires. The grantee can use up to SpendLimit to cover fees. -
PeriodicAllowance (opens in a new tab)
PeriodicAllowance
extends Allowance to allow for both a maximum cap, and a limit per time period. -
AllowedMsgAllowance (opens in a new tab)
AllowedMsgAllowance
creates allowance only for specified message types.
Granting
seid
seid tx feegrant grant [granter_key_or_address] [grantee] [flags]
Flags
Flags:
-a, --account-number uint The account number of the signing account (offline mode only)
--allowed-messages strings Set of allowed messages for fee allowance
-b, --broadcast-mode string Transaction broadcasting mode (sync|async|block) (default "sync")
--dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible)
--expiration string The RFC 3339 timestamp after which the grant expires for the user
--fee-account string Fee account pays fees for the transaction instead of deducting from the signer
--fees string Fees to pay along with transaction; eg: 10uatom
--from string Name or address of private key with which to sign
--gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically (default 200000)
--gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1)
--gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom)
--generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name)
-h, --help help for grant
--keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os")
--keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used
--ledger Use a connected Ledger device
--node string <host>:<port> to tendermint rpc interface for this chain (default "tcp://localhost:26657")
--note string Note to add a description to the transaction (previously --memo)
--offline Offline mode (does not allow any online functionality
-o, --output string Output format (text|json) (default "json")
--period int period specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset
--period-limit string period limit specifies the maximum number of coins that can be spent in the period
-s, --sequence uint The sequence number of the signing account (offline mode only)
--sign-mode string Choose sign mode (direct|amino-json), this is an advanced feature
--spend-limit string Spend limit specifies the max limit can be used, if not mentioned there is no limit
--timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height
-y, --yes Skip tx broadcasting prompt confirmation
cosmjs
const { SigningStargateClient, GasPrice } = require("@cosmjs/stargate");
const { DirectSecp256k1HdWallet } = require("@cosmjs/proto-signing");
async function grantFeeGrant(rpcEndpoint, granterMnemonic, granteeAddress) {
const granterWallet = await DirectSecp256k1HdWallet.fromMnemonic(granterMnemonic, { prefix: "sei" });
const [granterAccount] = await granterWallet.getAccounts();
const client = await SigningStargateClient.connectWithSigner(rpcEndpoint, granterWallet, {
gasPrice: GasPrice.fromString("0.025usei"),
});
const msg = {
typeUrl: "/cosmos.feegrant.v1beta1.MsgGrantAllowance",
value: {
granter: granterAccount.address,
grantee: granteeAddress,
allowance: {
typeUrl: "/cosmos.feegrant.v1beta1.BasicAllowance",
value: {
spendLimit: [{ denom: "usei", amount: "1000000" }],
expiration: null,
},
},
},
};
const fee = {
amount: [{ denom: "usei", amount: "5000" }],
gas: "200000",
};
const result = await client.signAndBroadcast(granterAccount.address, [msg], fee);
console.log(result);
}
const rpcEndpoint = "https://rpc-endpoint";
const granterMnemonic = "your-granter-mnemonic";
const granteeAddress = "sei1granteeaddress";
grantFeeGrant(rpcEndpoint, granterMnemonic, granteeAddress);
Revoking
seid
seid tx feegrant revoke [granter] [grantee] [flags]
Flags
Flags:
-a, --account-number uint The account number of the signing account (offline mode only)
-b, --broadcast-mode string Transaction broadcasting mode (sync|async|block) (default "sync")
--dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible)
--fee-account string Fee account pays fees for the transaction instead of deducting from the signer
--fees string Fees to pay along with transaction; eg: 10uatom
--from string Name or address of private key with which to sign
--gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically (default 200000)
--gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1)
--gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom)
--generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name)
-h, --help help for revoke
--keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) (default "os")
--keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used
--ledger Use a connected Ledger device
--node string <host>:<port> to tendermint rpc interface for this chain (default "tcp://localhost:26657")
--note string Note to add a description to the transaction (previously --memo)
--offline Offline mode (does not allow any online functionality
-o, --output string Output format (text|json) (default "json")
-s, --sequence uint The sequence number of the signing account (offline mode only)
--sign-mode string Choose sign mode (direct|amino-json), this is an advanced feature
--timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height
-y, --yes Skip tx broadcasting prompt confirmation
cosmjs
const { SigningStargateClient, GasPrice } = require("@cosmjs/stargate");
const { DirectSecp256k1HdWallet } = require("@cosmjs/proto-signing");
async function revokeFeeGrant(rpcEndpoint, granterMnemonic, granteeAddress) {
const granterWallet = await DirectSecp256k1HdWallet.fromMnemonic(granterMnemonic, { prefix: "sei" });
const [granterAccount] = await granterWallet.getAccounts();
const client = await SigningStargateClient.connectWithSigner(rpcEndpoint, granterWallet, {
gasPrice: GasPrice.fromString("0.025usei"),
});
const msg = {
typeUrl: "/cosmos.feegrant.v1beta1.MsgRevokeAllowance",
value: {
granter: granterAccount.address,
grantee: granteeAddress,
},
};
const fee = {
amount: [{ denom: "usei", amount: "5000" }],
gas: "200000",
};
const result = await client.signAndBroadcast(granterAccount.address, [msg], fee);
console.log(result);
}
const rpcEndpoint = "https://rpc-endpoint";
const granterMnemonic = "your-granter-mnemonic";
const granteeAddress = "sei1granteeaddress";
revokeFeeGrant(rpcEndpoint, granterMnemonic, granteeAddress);
Usage
seid
Execute any transaction as normal, but add the --fee-granter
flag to specify the granter account.
cosmjs
CosmJS Stargate StdFee (opens in a new tab) object has a granter
field that can be used to specify the fee granter address.
const { DirectSecp256k1HdWallet } = require("@cosmjs/proto-signing");
const { SigningStargateClient } = require("@cosmjs/stargate");
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(YOUR_MNEMONIC, { prefix: "cosmos" });
const client = await SigningStargateClient.connectWithSigner(RPC_URL, wallet);
const amount = { denom: "usei", amount: "1000000" };
const fee = {
amount: [amount],
gas: "200000", // gas limit
granter: FEE_GRANTER_ADDRESS,
};
const txResult = await client.sendTokens(SENDER_ADDRESS, RECIPIENT_ADDRESS, [amount], fee, "Sending with fee granter");
Usage with EVM precompiles
This feature is coming soon.