The Claim Module

Users are required participate in core network activities to claim their airdrop. An Airdrop recipient is given 20% of the airdrop amount which is not in vesting, and then they have to perform the following activities to get the rest:

  • 20% vesting over 3 months by staking
  • 60% vesting over 3 months by liquid staking

These claimable assets 'expire' if not claimed. Users have three months (AirdropDuration) to claim their full airdrop amount. After three months from launch, all unclaimed tokens get sent to the community pool. At initialization, module stores all airdrop users with amounts from genesis inside KVStore. Airdrop users are eligible to claim their vesting or free amount only once in the initial period of 3 months. After the initial period, users can claim tokens monthly.

Actions

There are 2 types of actions, each of which release another 50% of the airdrop allocation.
The 2 actions are as follows:

ActionLiquidStake  Action = 0
ActionDelegateStake Action = 1

These actions are monitored by registring claim hooks to the stakeibc, and staking modules.
This means that when you perform an action, the claims module will immediately unlock those coins if they are applicable.
These actions can be performed in any order.

The code is structured by separating out a segment of the tokens as "claimable", indexed by each action type.
So if Alice delegates tokens, the claims module will move the 50% of the claimables associated with staking to her liquid balance.
If she delegates again, there will not be additional tokens given, as the relevant action has already been performed.
Every action must be performed to claim the full amount.

ClaimRecords

A claim record is a struct that contains data about the claims process of each airdrop recipient.

It contains an address, the initial claimable airdrop amount, and an array of bools representing
whether each action has been completed. The position in the array refers to enum number of the action.

So for example, [true, true] means that ActionLiquidStake and ActionDelegateStake are completed.

type ClaimRecord struct {
 // address of claim user
 Address string
 // weight that represent the portion from total allocation
 Weight sdk.Dec
 // true if action is completed
 // index of bool in array refers to action enum #
 ActionCompleted []bool
}

Params

The airdrop logic has 4 parameters:

type Params struct {
    // Time that marks the beginning of the airdrop disbursal,
    // should be set to chain launch time.
    AirdropStartTime   time.Time
    AirdropDuration time.Duration
    // denom of claimable asset
    ClaimDenom string
    // address of distributor account
    DistributorAddress string
}

Keeper functions

Claim keeper module provides utility functions to manage epochs.

  GetModuleAccountAddress(ctx sdk.Context) sdk.AccAddress
  GetDistributorAccountBalance(ctx sdk.Context) sdk.Coin
  EndAirdrop(ctx sdk.Context) error
  GetClaimRecord(ctx sdk.Context, addr sdk.AccAddress) (types.ClaimRecord, error)
  GetClaimRecords(ctx sdk.Context) []types.ClaimRecord
  SetClaimRecord(ctx sdk.Context, claimRecord types.ClaimRecord) error
  SetClaimRecords(ctx sdk.Context, claimRecords []types.ClaimRecord) error
  GetClaimableAmountForAction(ctx sdk.Context, addr sdk.AccAddress, action types.Action, includeClaimed bool) (sdk.Coins, error)
  GetUserTotalClaimable(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coins, error)
  ClaimCoinsForAction(ctx sdk.Context, addr sdk.AccAddress, action types.Action) (sdk.Coins, error)
  clearInitialClaimables(ctx sdk.Context)
  fundRemainingsToCommunity(ctx sdk.Context) error

React Hooks

The claim module reacts on the following hooks, executed in external modules.

20% of airdrop is sent to a vesting account when staking.AfterDelegationModified hook is triggered.
20% of airdrop is sent to a vesting account when stakeibc.AfterLiquidStake hook is triggered.

Once the airdrop is claimed for a specific hook type, it can't be claimed again.

Claim Records

// A Claim Records is the metadata of claim data per address
message ClaimRecord {
  // address of claim user
  string address = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ];

  // weight that represent the portion from total allocations
  double weight = 2;

  // true if action is completed
  // index of bool in array refers to action enum #
  repeated bool action_completed = 3 [
    (gogoproto.moretags) = "yaml:\"action_completed\""
  ];
}

When a user get airdrop for his/her action, claim record is created to prevent duplicated actions on future actions.

State

message GenesisState {
  // params defines all the parameters of the module.
  Params params = 2 [
    (gogoproto.moretags) = "yaml:\"params\"",
    (gogoproto.nullable) = false
  ];

  // list of claim records, one for every airdrop recipient
  repeated ClaimRecord claim_records = 3 [
    (gogoproto.moretags) = "yaml:\"claim_records\"",
    (gogoproto.nullable) = false
  ];
}

Claim module's state consists of params, and claim_records.

Claim module provides below params

// Params defines the claim module's parameters.
message Params {
  google.protobuf.Timestamp airdrop_start_time = 1 [
    (gogoproto.stdtime) = true,
    (gogoproto.nullable) = false,
    (gogoproto.moretags) = "yaml:\"airdrop_start_time\""
  ];
  google.protobuf.Timestamp airdrop_duration = 2 [
    (gogoproto.nullable) = false,
    (gogoproto.stdduration) = true,
    (gogoproto.jsontag) = "airdrop_duration,omitempty",
    (gogoproto.moretags) = "yaml:\"airdrop_duration\""
  ];
  // denom of claimable asset
  string claim_denom = 3;
  // airdrop distribution account
  string distributor_address = 4;
}
  1. airdrop_start_time refers to the time when user can start to claim airdrop.
  2. airdrop_duration refers to the duration from start time to end time.
  3. claim_denom refers to the denomination of claiming tokens. As a default, it's ustrd.
  4. distributor_address refers to the address of distribution account.

Queries

GRPC queries

Claim module provides below GRPC queries to query claim status

service Query {
  rpc DistributorAccountBalance(QueryDistributorAccountBalanceRequest) returns (QueryDistributorAccountBalanceResponse) {}
  rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {}
  rpc ClaimRecord(QueryClaimRecordRequest) returns (QueryClaimRecordResponse) {}
  rpc ClaimableForAction(QueryClaimableForActionRequest) returns (QueryClaimableForActionResponse) {}
  rpc TotalClaimable(QueryTotalClaimableRequest) returns (QueryTotalClaimableResponse) {}
}

CLI commands

For the following commands, you can change $(strided keys show -a {your key name}) with the address directly.

Query the claim record for a given address

strided query claim claim-record $(strided keys show -a {your key name})

Query the claimable amount that would be earned if a specific action is completed right now.


strided query claim claimable-for-action $(strided keys show -a {your key name}) ActionAddLiquidity

Query the total claimable amount that would be earned if all remaining actions were completed right now.

strided query claim total-claimable $(strided keys show -a {your key name}) ActionAddLiquidity

Events

claim module emits the following events at the time of hooks:

TypeAttribute KeyAttribute Value
claimsender{receiver}
claimamount{claim_amount}