Baskets
Overview
The Token Basketing Module allows users to combine multiple individual derivatives with the same denomination into a single, easily tradable and exchangeable token. For example, KIRA native liquid staking derivatives V<ID>/<denom>
that are issued when staking tokens in KIRA network and which are specific to the consensus node V<ID>
, can be aggregated using a basket. An other use case would be to aggregate different foreign tokens such as various USD stablecoins like usdt
or usdc
(similarly to Curve’s 3pool), or IBC wrapped tokens, into a single, more easily tradable and stakable asset.
However, unlike common liquidity pools like Curve or Uniswap, Token Baskets do not depend on a bonding curve equation to determine the price and are simply based on fixed weights assigned to each individual token. They are only intended to consolidate the liquidity of tokens that are collateralized/pegged by the same underlying asset. In addition, one of the unique properties of Token Baskets compared to liquidity pools is that the list of tokens in the basket can be expanded or contracted over time, while the name of the basket token representing the aggregated assets remains the same.
Token Baskets facilitate token swaps, which are subject to daily limits and are executed at fixed ratios with a programmed slippage. The surplus between the actual exchange ratio and the slippage serves as an insurance fund controlled by governance to protect token holders in the event of a depeg. Overall, the Token Basketing Module is not intended to operate as an exchange, but rather as a tool for insurance and risk management, allowing users to distribute their risks among multiple assets with the same underlying peg.
Tokens Weight Assignment in Baskets
The specific tokens that can be deposited into a basket are pre-determined. To ensure that different tokens collateralized by the same underlying asset can be accurately represented in the basket, weights are assigned to each token. These weights define the corresponding amount of the underlying derivative for a given amount of the token. For example, a "USD fiat basket" can be created by assigning a weight of 1/3.6725 = 0.2722940776
to the United Arab Emirates dirham, which is pegged to 1 USD at a rate of 1 USD = 3.6725 AED. The weight of a token can also be used to unify the number of decimal places among multiple aggregate coins with different decimal places. For instance, if two tokens, A
and B
, both collateralized by the same underlying asset have different numbers of decimal places (e.g. A
has 3 decimal places, B
has 2 decimal places), weights we can be assigned to each token to unify their decimal places. For example, a weight of 1.00
can be assigned to A
and a weight of 0.01
to B
. This means that 1 unit of A
is equal to 100 units of B
, and the basket will be expressed in terms of A
, which has 3 decimal places. This allows the basket to represent the underlying value of both tokens in a consistent way, using a single number of decimal places.
The governance must ensure that the weights of each token in the basket are properly configured to maintain the value of the issued basket tokens. Should the governance decide to re-configure one or many of the weight properties of the basket (for example due to peg changes), it is important to ensure that the sum of the products of the weight and amount of each token in the basket must be greater than or equal to the amount of issued basket tokens B<ID>/<denom>
otherwise there will not be enough tokens in the basket to redeem. For example, if the basket B<ID>
includes tokens A, B, and C, the governance must ensure that . If the weights are not correctly configured, the governance proposal to update the basket weights will fail.
How Users Deposits Translate to Basket Derivatives Tokens
When users deposit a combination of tokens A, B, C…
which have the same underlying asset/peg <denom>
into the corresponding basket B<ID>
, they receive an amount of token B<ID>/<denom>
which represent their weight-adjusted share of the total basket pool, similarly to Uniswap LP tokens. For instance, if a user deposits 100 units of token A
with a weight of 0.5 into the basket B5/ABC
, they will receive 50 units of tokens.
Maximum Weight-adjusted Tokens Shares
To prevent scenarios where one or more of the aggregated tokens becomes worthless or loses its peg, a tokens_cap
field sets the maximum percentage of the total weight-adjusted supply that can be deposited for a given aggregated token (default 1, or 100%). If the amount of tokens to be deposited or swapped would result in the tokens_cap
being exceeded for any of the aggregated tokens, the transaction interacting with the basket will fail.
Operational Controls and Limits
Token baskets include deposits
, withdraws
, and swaps
boolean fields that can globally prohibit deposits, withdraws, or swaps of one or more specific tokens in the basket. These fields can be disabled without the need to go through a governance proposal, although only accounts with the permission 61 can perform this action. This is meant to allow necessary flexibility in managing the basket, i.e. responding to potential depeg scenarios or other time-sensitive issues with individual tokens which require quick intervention.
To prevent abuse or manipulation of the Token Baskets (i.e. infinite supply of one of the aggregated tokens being flooded into the basket until the tokens_cap
is reached), Token Baskets have limits on the amount of tokens that can be minted (created), burned (redeemed for tokens), or swapped over a defined period limits_period
. These limits are defined by the mints_max
, burns_max
, and swaps_max
fields, respectively.
To prevent dust spam attacks, minimum amounts of the aggregate and basket tokens that must be minted, burned, or swapped in a single transaction are also defined by the mints_min
, burns_min
, and swaps_min
fields, respectively. Transactions that do not meet these minimum amounts will not be processed.
Basket Composition and Balance Maintenance
The claim process is designed to ensure that the basket remains balanced at all time by calculating the ratio at which each token should be credited to the withdrawing user to restore, or at least improve, the proper weighting of the pool’s balances after a withdrawal. This means that the redemption of pooled tokens against the pool’s derivative will always be adjusted based on the current pool balances, potentially resulting in a higher proportion of a specific token if the pool is imbalanced due to previous selective swaps and withdrawals.
Aggregated Tokens Swapping Mechanisms
Token Baskets implement a simple swap mechanism that enables users to exchange one or multiple aggregated tokens for a set of specific ones. This function provides a convenient way for users to consolidate their holdings into a desired set of tokens. However, the swap incurs a fixed swap_fee
and a variable slippage_fee
with a minimum slippage-fee-min
defined in the basket properties. The fees are calculated dynamically based on the degree to which the swap is disbalancing the basket. The greater the disbalance, the higher the "slippage" penalty will be. The slippage is a programmatic function and do not result from a changes in token value as seen in Liquidity Pools, as the value of all tokens remains constant.
A crucial aspect of the swap mechanism is that the sum of products of individual token amounts and their weights remain constant before and after the swap. This is expressed mathematically as:
This means that the cumulative value of the tokens in the basket remain unchanged (or slightly larger due to rounding errors). Any additional tokens due to the applied slippage is accounted for in the surplus
, while the basket_swap_fee
is be paid to the network as a fee reward. This ensures that the value of the basket remains constant, providing stability to the system.
Surplus: a Basket’s Insurance Funds
Unlike Uniswap, where swap fees are given to LP providers by issuing LP tokens proportionally, baskets swap fees paid by users and deposits from the governance are recorded in a separate surplus
and do not issue additional basket tokens B<ID>/<denom>
. These surplus
serve as insurance funds to protect users against depeg events and can also collect any potential staking rewards yielded by aggregated tokens and "donations". In the event of a token losing its peg, the deposits
, withdraws
, and swaps
properties of the depegged token may be set to false
, and the governance will deposit the necessary amount of tokens from the surplus
and/or the community treasury into the basket to re-adjust the weights, so that the remaining basket tokens B<ID>/<denom>
can redeem the equivalent value in the form of all aggregate tokens. Although surplus
funds are dedicated to their corresponding basket, the governance has the ability to redeem their entire content for redistribution to other baskets if necessary.
Kira's Unique Staking Derivatives & Rewards Handling
The Token basketing module deals with a unique scenario regarding KIRA's staking derivatives V<ID>/<denom>
, which accumulate block and fee rewards for their holders over time. Users pooling their V<ID>/<denom>
tokens to the corresponding basket are only entitled to the block rewards accumulated by the basket on their behalf. However, it is important to note that, due to how the basket token operates, block rewards become mutualized once the derivative is pooled, meaning users may gain or lose some percentage of block rewards (compared to simply holding the individual V<ID>/<denom>
itself) based on consensus nodes performance and commission rate. That being said, in KIRA, a consensus node cannot set it’s commission above 50% (or lower, depending on current network properties), hence any potential loss is minimized and should be viewed as a reasonable fee for the superior liquidity service offered by the basket.
Block Rewards
Staking derivative baskets do not track the B<ID>/<denom>
holdings of individual users over time, nor do they issue new ones to account for block rewards. Instead, they accommodate the earnings from block rewards by adjusting the weight of the different V<ID>/<denom>
pooled in the basket. As a result, the value that a B<ID>/<denom>
can redeem for the pooled tokens increases over time as block rewards accumulate, while the total number of issued B<ID>/<denom>
tokens remains unchanged.
Fee Rewards
On the other hand, fee rewards are not accounted for and are simply sent to the pool's surplus
insurance fund.
Parameters
Token Basket
NAME | TYPE | EXAMPLE | DESCRIPTION |
---|---|---|---|
id | uint64 | 1 | Basket identifier |
suffix | string | usd | Basket token denom suffix. Resulting basket token denom would be B1/usd if suffix value is "usd" |
description | string | description | Basket description |
tokens | []BasketToken | Array of aggregated tokens with their corresponding weights, current basket balance and transaction abilities status | |
amount | int | 10000000000 | Total supply of B1/usd that can be minted |
tokens_cap | float | 0.9 | The maximum percentage of the basket supply that a single aggregated token can represent. Transactions such as minting, burning, and swapping will fail if they cause an aggregated token's supply to exceed this percentage. Decimal from 0 to 1 |
limits_period | uint64 | 86400 | Period after which all mint, swap & burn limits are reset (in Unix time/seconds, default 1 day) |
mints_disabled | boolean | false | true to disable all aggregated token deposits (B1/usd minting is disabled) |
mints_min | int | 10000000 | Minimum amount of B1/usd that must be minted per transaction (spam prevention) |
mints_max | int | 10000000000 | Maximum cumulative daily B1/usd issuance |
burns_disabled | boolean | false | true to disable B1/usd redemptions |
burns_min | int | 100 | Minimum amount of B1/usd redemption per transaction (spam prevention) |
burns_max | int | 1000000000 | Maximum cumulative daily amount of B1/usd redemptions |
swaps_disabled | boolean | false | true to disable basket swaps |
swaps_min | int | 100 | Minimum amount of aggregate tokens, expressed in B1/usd , that must be swapped in a single transaction |
swaps_max | int | 100000000000 | Maximum cumulative daily amount of basket swaps, expressed in B1/usd |
swap_fee | float | 0.0015 | Percentage fee to be paid for swapping tokens. Decimal from 0 to 1, default 0.0015 (0.15%) |
slippage_fee_min | float | 0.0015 | Minimum percentage to be paid as penalty for disbalancing the basket. Decimal from 0 to 1, default 0.0015 (0.15%) |
surplus | []sdk.Coin | ["1000ukex","20000ueth"] | Insurance funds from excess tokens accumulated as result of fee rewards from KIRA’s staking derivative and/or swaps fees |
BasketToken type
NAME | TYPE | EXAMPLE | DESCRIPTION |
---|---|---|---|
denom | string | usdt | Denom of one of the aggregate tokens |
weight | float | 0.99 | Relative value of the token to its underlying derivative. Here, 100 deposit should result in 99 issuance. |
amount | int | 1000000 | Total deposited amount of usdt available for withdrawal |
deposits | bool | true | Defines if deposits of usdt resulting in minting are allowed |
withdraws | bool | true | Defines if withdrawals of usdt as result of burns are allowed |
swaps | bool | true | Defines if swaps of usdt for other tokens are allowed |
CLI Syntax & Examples
Each CLI command and proposal process in KIRA requires specific permissions. These permissions must be added to the account's whitelist or obtained as sudo permissions for direct changes. Refer to the Roles & Permissions documentation for more details.
$SIGNER
represents the transaction signer's account name or address. For instructions on setting common flags as environment variables, such as $FLAGS_TX
and $FLAGS_QR
, see the CLI Guide page.
- Transactions
- Queries
- Governance
Transactions
mint-basket-tokens | Deposit aggregated tokens into a basket. |
---|---|
burn-basket-tokens | Withdraw aggregated tokens from a basket. |
swap-basket-tokens | Swap tokens within a basket. |
disable-basket-deposits | Disable deposits into baskets (emergency). |
disable-basket-withdraws | Disable withdrawals from baskets (emergency). |
disable-basket-swaps | Disable swaps in baskets (emergency). |
basket-claim-rewards | Claim rewards from staking derivative baskets. |
Deposit Aggregated Tokens Into Basket
The mint-basket-tokens
command is used to deposit aggregated tokens into a specific basket, identified by $BASKET_ID
.
Args
$BASKET_ID
: Unique ID of the basket.$DEPOSIT_AMOUNTS
: Coin denomination and amount to be deposited, formatted as<amount><denom>
or as a comma-separated list.
sekaid tx basket mint-basket-tokens \
--from=$SIGNER $FLAGS_TX \
$BASKET_ID $DEPOSIT_AMOUNTS
Withdraw Aggregated Tokens From Basket
The burn-basket-tokens
command allows for the withdrawal of aggregated tokens from a basket by burning the corresponding amount of basket token $REDEMPTION
.
Args
$BASKET_ID
: Unique ID of the basket.$REDEMPTION
: Amount of basket token to be redeemed, formatted as<amount><denom>
.
sekaid tx basket burn-basket-tokens \
--from=$SIGNER $FLAGS_TX \
$BASKET_ID $REDEMPTION
Swap Basket Tokens
The swap-basket-tokens
command is used for swapping tokens within a basket, alternating pairs of input token amounts and their corresponding desired output tokens.
Args
$BASKET_ID
: Unique ID of the basket.$IN_AMOUNTn
and$OUT_TOKENn
: Pairs of the amount of a token to swap from and the token to swap to, formatted as<amount><denom>
.
sekaid tx basket swap-basket-tokens \
--from=$SIGNER $FLAGS_TX \
$BASKET_ID $IN_AMOUNT1 $OUT_TOKEN1 $IN_AMOUNT2 $OUT_TOKEN2 (...)
Disable Basket Deposits, Withdrawals, and Swaps (emergency)
Commands like disable-basket-deposits
, disable-basket-withdraws
, and disable-basket-swaps
are used to globally disable respective basket abilities, requiring sudo permission 61.
Args
$BASKET_ID
: Unique ID of the basket.$DISABLED
: Boolean (true
to disable the ability).
sekaid tx basket disable-basket-deposits \
--from=$SIGNER $FLAGS_TX \
$BASKET_ID $DISABLED
Claim Derivatives Basket Rewards
The basket-claim-rewards
command is used to claim outstanding rewards of staking derivative baskets for one or many aggregate V<ID>
tokens.
Args
$TOKENS
: A list of coins specifying the amount and denomination.
sekaid tx basket basket-claim-rewards \
--from=$SIGNER $FLAGS_TX \
$TOKENS
Queries
token-basket-by-denom | Query a single basket by its denominat |
---|---|
token-basket-by-id | Query details of a basket by its ID. |
token-baskets | List baskets filtered by denominations/derivatives. |
historical-mints | Query historical mints for a basket. |
historical-burns | Query historical burns for a basket. |
historical-swaps | Query historical swaps made in a basket. |
Query Basket by Denomination
Retrieve a single token basket based on its denomination using the token-basket-by-denom
command.
Args
$DENOM
: The token denomination of the basket, formatted asB<ID>/<denom>
.
sekaid query basket token-basket-by-denom $DENOM $FLAGS_QR | jq
Query Basket by Id
Retrieve details of a token basket using its unique ID with the token-basket-by-id
command.
Args
$BASKET_ID
: The unique identifier of the token basket.
sekaid query basket token-basket-by-id $BASKET_ID $FLAGS_QR | jq
List Token Baskets
If $DENOMS
is not provided, it returns all token baskets.
Retrieve token baskets by filtering them based on aggregated token denominations and staking derivatives using the token-baskets
command.
Args
$DENOMS
: A comma-separated list of token denominations.$IS_DERIVATIVE
: Specify if the tokens are staking derivatives (true
orfalse
).
sekaid query basket token-baskets $DENOMS $IS_DERIVATIVE $FLAGS_QR | jq
Query Historical Mints
Retrieve a list of historical mints for a token basket within the specified limits_period
using the historical-mints
command.
Args
$BASKET_ID
: The ID of the token basket.
sekaid query basket historical-mints $BASKET_ID $FLAGS_QR | jq
Query Historical Burns
Retrieve a list of historical burns for a token basket using the historical-burns
command.
Args
$BASKET_ID
: The ID of the token basket.
sekaid query basket historical-burns $BASKET_ID $FLAGS_QR | jq
Query Historical Swaps
Retrieve a list of historical swaps made in a token basket with the historical-swaps
command.
Args
$BASKET_ID
: The ID of the token basket.
sekaid query basket historical-swaps $BASKET_ID $FLAGS_QR | jq
Governance
proposal-create-basket | Create a proposal to create a new token basket. |
---|---|
proposal-edit-basket | Create a proposal to edit an existing token basket. |
proposal-basket-withdraw-surplus | Create a proposal to withdraw surplus from baskets. |
Creating and Updating a Token Basket Proposal
The proposal-create-basket
CLI command creates a token basket proposal. To vote on this proposal, accounts require permission 59. This command allows for specifying various parameters related to the basket such as its suffix, description, tokens included, and various limits and fees associated with the basket.
Flags
$TITLE
: The title of the proposal.$DESCRIPTION
: The proposal’s description.$BASKET_SUFFIX
: The suffix of the basket token.$BASKET_DESCRIPTION
: The description of the basket.$BASKET_TOKENS
: Comma-separated list of aggregated tokens with corresponding rates and abilities.$TOKENS_CAP
: Tokens cap on the basket.$LIMITS_PERIOD
: Period after which limits are reset.$MINTS_DISABLED
: Disable all basket’s deposits.$MINTS_MIN
: Minimum amount of basket token issuance.$MINTS_MAX
: Maximum cumulative daily basket token issuance.$BURNS_DISABLED
: Disable all basket’s redemptions.$BURNS_MIN
: Minimum amount of basket token redemption.$BURNS_MAX
: Maximum cumulative daily amount of basket token redemptions.$SWAPS_DISABLED
: Disable all basket swaps.$SWAPS_MIN
: Minimum amount of aggregated tokens swaps.$SWAPS_MAX
: Maximum cumulative daily amount of aggregated tokens swaps.$SWAP_FEE
: Percentage fee for swapping tokens.$SLIPPAGE_FEE_MIN
: Minimum percentage penalty for disbalancing the basket.
sekaid tx basket proposal-create-basket \
--from=$SIGNER $FLAGS_TX \
--title=$TITLE --description=$DESCRIPTION \
--basket-suffix=$BASKET_SUFFIX --basket-description=$BASKET_DESCRIPTION \
--basket-tokens=$BASKET_TOKENS --tokens-cap=$TOKENS_CAP --limits-period=$LIMITS_PERIOD \
--mints-disabled=$MINTS_DISABLED \
--mints-min=$MINTS_MIN --mints-max=$MINTS_MAX \
--burns-disabled=$BURNS_DISABLED \
--burns-min=$BURNS_MIN --burns-max=$BURNS_MAX \
--swaps-disabled=$SWAPS_DISABLED \
--swaps-min=$SWAPS_MIN --swaps-max=$SWAPS_MAX \
--swap-fee=$SWAP_FEE --slippage-fee-min=$SLIPPAGE_FEE_MIN
Editing an Existing Token Basket Proposal
The proposal-edit-basket
CLI command is used to edit existing baskets. This editing process requires governance permissions similar to those required for creating a basket, and the basket’s ID ($BASKET_ID
) must be specified.
Flags
- Similar flags to
proposal-create-basket
. $BASKET_ID
: The ID of the basket to be edited.
sekaid tx basket proposal-edit-basket \
--from=$SIGNER $FLAGS_TX \
--title=$TITLE --description=$DESCRIPTION \
--basket-suffix=$BASKET_SUFFIX --basket-description=$BASKET_DESCRIPTION \
--basket-tokens=$BASKET_TOKENS --tokens-cap=$TOKENS_CAP --limits-period=$LIMITS_PERIOD \
--mints-disabled=$MINTS_DISABLED \
--mints-min=$MINTS_MIN --mints-max=$MINTS_MAX \
--burns-disabled=$BURNS_DISABLED \
--burns-min=$BURNS_MIN --burns-max=$BURNS_MAX \
--swaps-disabled=$SWAPS_DISABLED \
--swaps-min=$SWAPS_MIN --swaps-max=$SWAPS_MAX \
--swap-fee=$SWAP_FEE --slippage-fee-min=$SLIPPAGE_FEE_MIN
Withdrawing Surplus From a Basket Proposal
The proposal-basket-withdraw-surplus
CLI command is for creating a proposal to withdraw surplus from specific baskets.
Args
$BASKET_IDS
: A comma-separated list of basket IDs from which the surplus will be withdrawn.$RECEIVER_ADDR
: The target address for the surplus withdrawal.
Flags
$TITLE
: The title of the proposal.$DESCRIPTION
: The proposal’s description.
sekaid tx customgov proposal-basket-withdraw-surplus \
--from=$SIGNER $FLAGS_TX \
--title=$TITLE --description=$DESCRIPTION \