Advanced Concepts
Fee Grants

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

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.