# What to expect? - Airdrop pallet
Source: https://github.com/ibriz/ice-substrate/tree/airdrop-pallet
## Use case
This pallet serves as an interface to do airdrop. i.e use can verify themselves as owner of valid icon address and they are subjected to recive the token value equivalent to that of icon as per collected in certain snapshot.
## Terminoalgy
### Creditor account
- This serves as the bank-like account in essence that it holds all of the tokens required to distrbute all airdrop requests.
> Subject to change. Creditor account is planned to be shifted towards gensis config rather than static one.
- In this pallet, it is an special account that is generated from given seed-like string. Once called, this account will remain same over the lifetime of node. This account is "computed" in runtime so it is not subjected with any private key. This account can be read/used by calling `Pallet::get_creditor_account()` interface.
- If ths account does not contains enough balance to sucessfully transfer the airdrop, it will fail at the point after when all of checks is done and actual transfer is to be done in `Pallet::complete_transfer`
- Seed-like input to generate this account is of type `palletId` and is converted through `.into_account()`. This is an value type paramater in `Pallet::Config` named `CreditorAccount`
### Offchain account
- This is an special account that offchain will use to sign the transaction to call extrinsic. It is called special in the sense that it is provided with special permission.
In simple terms, this is an account to let `airdrop-pallet` know that this transaction came from offchain account.
- This is an usual `ed25519`/`Sr25519` or any of substrate supported account. It is stored as value in `offchainAccount` storage of same pallet.
This value is set to `None` in any fresh-starting node. And this can be updated through `Pallet::setOffchainAccount` extrinsic call ( this required root/sudo permission ).
- Once initialized this storage is never expected to be None. As `setOffchainAccount` only provides a way to update value.
- In absense of any account in this storage, all of the call airdropping fails at the point when it is expected to send on-chain call. i.e final step for offchain-worker
- This account must also be owner of few unit of token to pay fees for. However, this fee need not to be much high because all of the valid call will have `Pays::No` as their `postDispatchInfo`
- In privilaged extrinsic this is generally verified with `Pallet::ensure_root_or_offchain`
- This account is associated with an respective private key. This implies that the party which posses the private key or seed will be able to act as privilaged user. This is upto the node operator itself to keep this key under high security
> Is it also any better to make this configurable from Genesis config
## Icesnapshot/Iconsnapshot Map
These term is being frequently used in communicatioon between team. This simply refers to the storage map `IceSnapshotMap`. This is an single map structure kept icon-address as key and type `types::SnapshotInfo` as value.
## Pending queue
This is actually a daul-key map defines as:
` (block_number, icon-adress) -> retry_remainig `
This serves as an queue from which offchain worker reads entried from.
Prefixed block number is kept to maintain FIFO order.
## Ice address
Refer to actual polkadot address in chain. Main source of this address is through `Origin` value of extrinsic. Ice address, Substrate address, AccountId and Polkadot address are interchangable most of the times.
## Icon address
Actual wallet address of the user who is subjected to receive airdrop
## Signature verification
Process that is went through to verify that the ice address claiming for airdrop is really the owner of respective icon address too
## Pallet
Throughout development, term `pallet` is vaugely used to refer `pallet-airdrop` unless specified otherwise
# Code structure
#### `src/lib.rs`
Major defining file for pallet itself.
#### `src/types.rs`
Module that will contain the user defined defination of types and aliases as well as trait defination.
#### `src/mock.rs`
Defination to intialize and configure mock runtime which can later be used in test. This tries to mimic the most possible behaviour of actual runtime as defined in `../../runtime/lib.rs`. However, it is not exact mock, some behaviour like paying fee is not mimiced accurately in mock runtime.
#### `src/utils.rs`
As name suggest, contains utilities functions and trait implementation.
> This file is just newelt introduced at the time of writing this. So many logic are yet to be seperated. The main intention is to keep `lib.rs` very logic centric and free from verbose declerations
#### `src/tests/*.rs`
Test cases. These are also seperated into multiple files named according to the name of file itself.
> This is also work in progress. This is best-effore, i.e not all the cases are yet covered ( it's gonna be never ending process )
---
# 100 foot overview

The whole flow can be taken as:
1) Use call extrinsic to signal they want to receiver claim. They pass icon address here
2) We validate icon address belonges to this user
3) Insert into InceSnapshotMap and PendingClaims if already noe exists
4) Offchain worker is triggered in every few blocks
5) It reads from PendingClaims prserving FIFO order.
6) Offchain worker sends http request to fetch iconDetails and call to complete_transfer/remove_from queue accordingly
7) Complete_transfer do the transfer of certain amount instantly and remaining in vested manner
8) Expecting more than this? That's it
## Claim request
* Main entry point where user/app front end send request to register claim request. Flow is as in:

## Offchain worker
Offchain worker can be summed up as:
- Collect all entries from last processedUpto to current block
- For each entry, send http request to server
- Parse response
- According to response, call one of `complete_transfer`, `register_failed_claim` or `remove_from_pending_queue`.

# "I haven't received my amount" - said a user
Airdropping is an rwo step process.
1) user doing the request
2) Offchain fetching data and doing actual transfer
Here, first step is done by user and is non-continous to second. Might be a case where regestering for airdrop succeed and offchain failed to process the request.
In such case, offchain keep trying for couple of times ( 3 at he time of writing ). If processing fails fro offchain side then user will notice s/he didn't received what they are eligible for. And when reaching to the ICE team, node operator can:
- Expect to receive icon & ice address.
>This still needs to be verified in some way. What if someone compails with the icon address they dont own? So might need to do some sort of signature verification before receiving complain
- Check on server side that this icon address is eligible for sufficient tokens or not.
- Query in IceSnapshot map that this icon address is inserted. This verifies that user had done an claim request
> This is planned to split in two seperate flags. Namely, `vesting_status` and `instant_status` to signify vested transfer and instant transfer
- See the flag `claim_status` in `SnapshotInfo` value in `IceSnapshotMap` storage. If this flag is true, this means that user did received an airdrop
- While doing claim request user is also provided with block number they were in when they done claim request. i.e an event is emited when `Pallet::claim_request` passes. It would be helpful if we are provided with such block number. ( Our front-end might also make this process automatic by storing the blocknumber in some way. )
- See log file `airdrop-pallet.log` to see the reason of why request had failed.
All logs include *ice-address*, *icon-address* and *block-number* when applicable. Filter through log using this data and check the reason of failing request.
- Once being confident that this request did failed in real, manually set `claim_status` flag to true before doing any other manual operation.
- Depends on the reason of failing airdrop done either of:
##### If failing reason is recoverable, eg: insufficient balance in creditor:
- Remove this entry from IceSnapshotMap and PendingQueue
- Ask user to do claim request again, or it can be done by node operator on behalf of user through `forceClaimRequest
##### For other reason
- Do manual native transfer
> In progress: Remove entry from IceSnapshotMap when failed several times. Doing this would allow user to do claim request again.
> Doing Claim request again does not potentially have any side effect. If vesting started from day 1 and user did first claim request in day 2, vesting should have ended in day 365. If claiming fails and user have to do claim request again at day 50, user would still not loose the time so s/he would receive all of amount at original day 365 and not 375.
## Reason by which processing of claim request might fail [non-exhaustive]
1. ### Creditor account do not have sufficient balance
- To prevent this, obvious way is to keep sufficient funds in this account
- Have a check which notify node operator when balance is dropping too low that it can't suffice no more than few hundred average request to come
2. ### Offchain account is not synced with one stored in keystore
To recap,
we have to configure a account in offchain local keystore from which we can sign the transaction. In case of airdrop the key is `_air`. For more details refer to https://hackmd.io/@sudipghimire533/ryeNQNDZ5
Additionally, same account in use must also be set as previlaged one through sudo call `Pallet::setOffchainAccount`.
- In both of places mentioned, there should live same account so that the extrinsic passes permission check.
- If they are not synced, the offchain will dispatch the extrinsic and assumes it passed but in actual it would fail
3. ### Implementation of offchain sendSignedExtrinsic is faulty
- It is our responsiblity to implement `sendSignedTransaction` trait in runtime. This is currently done through `<root>/runtime/lib.rs`. If this implmentation is faulty on itself, offchain account will never send any transaction on-chain.
- This implies that it will also not be able to update countr ( which points upto where we have done processing of claim request ).
- Above point now lead to the case where offchain will only process the request but never be able to complete them. And as copunter is also not incremented it will process ever-increasing request over time.
- If this error occured, all calls will fail with error `CantDispatch`. Refer to log file if such error is occuring
4. ### User have too low balance for airdrop
- If balance is too low that it cant be done in vesting, it will be transferred instantly adding to instant tranfser amount.
- Even on if doing above, amount is still low, such that even transfer can't be done, then the request will fail from `pallet_balances::Error`.
- It is assumes that server should never return such data.
5. ### Eror from other pallet in use,
Other pallet that come to play are `pallet_vesting` and `pallet_balances`. While doing vested/instant transfer some error might be thrown from these pallet itself
6. ### We never know ;)
# Integrating Ice and Snow in same source code
At the time of wiriting, only difference between these two networkd was how claim is to be airdropped. In Ice, we do vesting and in snow we do all instant.
This is currently done by making use of feature macro in rust. But as differences between these two grows, it is preferred to seperate these codebase.
If current base is splitted into three parts,
- shared logic
- ice logic
- icon logic
We keep each node independent frome each other
# Unresolved questions:
- How to manage running offchain worker in multiple node such that every node processing power is utilized. This will preserve true meaning of decentrialization