Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw error as event as well #48

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 78 additions & 64 deletions pallets/airdrop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,51 +112,13 @@ pub mod pallet {

/// Creditor balance is running low
CreditorBalanceLow,
}

#[pallet::storage]
#[pallet::getter(fn get_airdrop_state)]
pub(super) type AirdropChainState<T: Config> = StorageValue<_, types::AirdropState, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn get_icon_snapshot_map)]
pub(super) type IconSnapshotMap<T: Config> =
StorageMap<_, Blake2_128, types::IconAddress, types::SnapshotInfo<T>, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn get_ice_to_icon_map)]
pub(super) type IceIconMap<T: Config> =
StorageMap<_, Twox64Concat, types::AccountIdOf<T>, types::IconAddress, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn get_airdrop_server_account)]
pub(super) type ServerAccount<T: Config> = StorageValue<_, types::AccountIdOf<T>, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn get_exchange_account)]
pub type ExchangeAccountsMap<T: Config> =
StorageMap<_, Twox64Concat, types::IconAddress, types::BalanceOf<T>, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn try_get_merkle_root)]
pub type MerkleRoot<T: Config> = StorageValue<_, [u8; 32], OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn try_get_creditor_account)]
pub(super) type CreditorAccount<T: Config> =
StorageValue<_, types::AccountIdOf<T>, OptionQuery>;

#[pallet::type_value]
pub(super) fn DefaultStorageVersion<T: Config>() -> u32 {
1_u32.into()
/// A mechanism to throw error as event
ErrorAsEvent(DispatchError),
}

#[pallet::storage]
#[pallet::getter(fn get_storage_version)]
pub(super) type StorageVersion<T: Config> =
StorageValue<Value = u32, QueryKind = ValueQuery, OnEmpty = DefaultStorageVersion<T>>;

#[pallet::error]
#[derive(Clone, Eq, PartialEq)]
pub enum Error<T> {
/// This error will occur when signature validation failed.
InvalidSignature,
Expand Down Expand Up @@ -229,6 +191,48 @@ pub mod pallet {
InvalidClaimAmount,
}

#[pallet::storage]
#[pallet::getter(fn get_airdrop_state)]
pub(super) type AirdropChainState<T: Config> = StorageValue<_, types::AirdropState, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn get_icon_snapshot_map)]
pub(super) type IconSnapshotMap<T: Config> =
StorageMap<_, Blake2_128, types::IconAddress, types::SnapshotInfo<T>, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn get_ice_to_icon_map)]
pub(super) type IceIconMap<T: Config> =
StorageMap<_, Twox64Concat, types::AccountIdOf<T>, types::IconAddress, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn get_airdrop_server_account)]
pub(super) type ServerAccount<T: Config> = StorageValue<_, types::AccountIdOf<T>, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn get_exchange_account)]
pub type ExchangeAccountsMap<T: Config> =
StorageMap<_, Twox64Concat, types::IconAddress, types::BalanceOf<T>, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn try_get_merkle_root)]
pub type MerkleRoot<T: Config> = StorageValue<_, [u8; 32], OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn try_get_creditor_account)]
pub(super) type CreditorAccount<T: Config> =
StorageValue<_, types::AccountIdOf<T>, OptionQuery>;

#[pallet::type_value]
pub(super) fn DefaultStorageVersion<T: Config>() -> u32 {
1_u32.into()
}

#[pallet::storage]
#[pallet::getter(fn get_storage_version)]
pub(super) type StorageVersion<T: Config> =
StorageValue<Value = u32, QueryKind = ValueQuery, OnEmpty = DefaultStorageVersion<T>>;

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Dispatchable to be called by server with privileged account
Expand Down Expand Up @@ -260,7 +264,7 @@ pub mod pallet {
info!(
"claim request by: {icon_address:?}. Rejected at: validate_message_payload(). Error: {e:?}"
);
e
Self::error_and_event(e)
})?;

// We expect a valid proof of this exchange call
Expand All @@ -269,14 +273,14 @@ pub mod pallet {
info!(
"claim request by: {icon_address:?}. Rejected at: validate_merkle_proof()"
);
e
Self::error_and_event(e)
},
)?;

// Validate icon signature
Self::validate_icon_address(&icon_address, &icon_signature, &message).map_err(|e| {
info!("claim request by: {icon_address:?}. Rejected at: validate_icon_address()");
e
Self::error_and_event(e)
})?;

// Validate ice signature
Expand All @@ -285,7 +289,7 @@ pub mod pallet {
info!(
"claim request by: {icon_address:?}. Rejected at: validate_ice_signature()"
);
e
Self::error_and_event(e)
},
)?;

Expand All @@ -295,25 +299,25 @@ pub mod pallet {
Self::insert_or_get_snapshot(&icon_address, &ice_address, defi_user, total_amount)
.map_err(|e| {
info!("claim request by: {icon_address:?}. Rejected at: insert_or_get_snapshot. error: {e:?}");
e
Self::error_and_event(e)
})?;

// Make sure this user is eligible for claim.
Self::ensure_claimable(&snapshot).map_err(|e| {
info!("claim request by: {icon_address:?}. Rejected at: ensure_claimable(). Snapshot: {snapshot:?}.");
e
Self::error_and_event(e)
})?;

// We also make sure creditor have enough fund to complete this airdrop
Self::validate_creditor_fund(total_amount).map_err(|e| {
error!("claim request by: {icon_address:?}. Rejected at: validate_creditor_fund(). Amount: {total_amount:?}");
e
Self::error_and_event(e)
})?;

// Do the actual transfer if eligible
Self::do_transfer(&mut snapshot, &icon_address).map_err(|e| {
error!("claim request by: {icon_address:?}. Failed at: do_transfer(). Reason: {e:?}. Snapshot: {snapshot:?}");
e
Self::error_and_event(e)
})?;

Self::deposit_event(Event::ClaimSuccess(icon_address));
Expand All @@ -334,17 +338,19 @@ pub mod pallet {
proofs: types::MerkleProofs<T>,
) -> DispatchResultWithPostInfo {
ensure_root(origin).map_err(|_| Error::<T>::DeniedOperation)?;
Self::ensure_exchange_claim_switch()?;
Self::ensure_exchange_claim_switch().map_err(|e| Self::error_and_event(e))?;

let amount = Self::validate_whitelisted(&icon_address)?;
ensure!(total_amount == amount, Error::<T>::InvalidClaimAmount);
if total_amount != amount {
Err(Self::error_and_event(Error::<T>::InvalidClaimAmount))?
}

Self::validate_merkle_proof(&icon_address, total_amount, defi_user, proofs).map_err(
|e| {
info!(
"Exchange for: {icon_address:?}. Failed at: validate_merkle_proof(). Reason: {e:?}"
);
e
Self::error_and_event(e)
},
)?;
Self::validate_creditor_fund(total_amount).map_err(|e| {
Expand All @@ -358,16 +364,16 @@ pub mod pallet {
error!(
"Exchange for: {icon_address:?}. Failed at: insert_or_get_snapshot."
);
e
Self::error_and_event(e)
})?;

Self::ensure_claimable(&snapshot).map_err(|e| {
info!("Exchange for: {icon_address:?}. Failed at: ensure_claimable. Snapshot: {snapshot:?}");
e
Self::error_and_event(e)
})?;
Self::do_transfer(&mut snapshot, &icon_address).map_err(|e| {
error!("Exchange for: {icon_address:?}. Failed at: do_transfer. Snapshot: {snapshot:?}. Reason: {e:?}");
e
Self::error_and_event(e)
})?;

Self::deposit_event(Event::ClaimSuccess(icon_address));
Expand Down Expand Up @@ -462,11 +468,11 @@ pub mod pallet {

/// Check weather node is set to block incoming exchange request
/// Return error in that case else return Ok
pub fn ensure_exchange_claim_switch() -> DispatchResult {
pub fn ensure_exchange_claim_switch() -> Result<(), Error<T>> {
let is_disabled = Self::get_airdrop_state().block_exchange_request;

if is_disabled {
Err(Error::<T>::NewExchangeRequestBlocked.into())
Err(Error::<T>::NewExchangeRequestBlocked)
} else {
Ok(())
}
Expand All @@ -491,7 +497,7 @@ pub mod pallet {
ice_address: &types::IceAddress,
defi_user: bool,
amount: types::BalanceOf<T>,
) -> Result<types::SnapshotInfo<T>, DispatchError> {
) -> Result<types::SnapshotInfo<T>, Error<T>> {
let ice_account =
Self::convert_to_account_id(ice_address.to_vec().try_into().map_err(|_| {
error!(
Expand Down Expand Up @@ -539,17 +545,19 @@ pub mod pallet {
Ok(snapshot)
}

pub fn ensure_claimable(snapshot: &types::SnapshotInfo<T>) -> DispatchResult {
pub fn ensure_claimable(snapshot: &types::SnapshotInfo<T>) -> Result<(), Error<T>> {
let already_claimed = snapshot.done_instant && snapshot.done_vesting;

if already_claimed {
Err(Error::<T>::ClaimAlreadyMade.into())
Err(Error::<T>::ClaimAlreadyMade)
} else {
Ok(())
}
}

pub fn validate_creditor_fund(required_amount: types::BalanceOf<T>) -> DispatchResult {
pub fn validate_creditor_fund(
required_amount: types::BalanceOf<T>,
) -> Result<(), Error<T>> {
let creditor_balance =
<T as Config>::Currency::free_balance(&Self::get_creditor_account()?);
let existential_deposit = <T as Config>::Currency::minimum_balance();
Expand All @@ -558,7 +566,7 @@ pub mod pallet {
Ok(())
} else {
Self::deposit_event(Event::<T>::CreditorBalanceLow);
Err(Error::<T>::InsufficientCreditorBalance.into())
Err(Error::<T>::InsufficientCreditorBalance)
}
}

Expand Down Expand Up @@ -632,15 +640,15 @@ pub mod pallet {
amount: types::BalanceOf<T>,
defi_user: bool,
proof_hashes: types::MerkleProofs<T>,
) -> DispatchResult {
) -> Result<(), Error<T>> {
let amount = types::from_balance::<T>(amount);
let leaf_hash = merkle::hash_leaf(icon_address, amount, defi_user);
let merkle_root = Self::get_merkle_root()?;

let is_valid_proof =
<T as Config>::MerkelProofValidator::validate(leaf_hash, merkle_root, proof_hashes);
if !is_valid_proof {
return Err(Error::<T>::InvalidMerkleProof.into());
return Err(Error::<T>::InvalidMerkleProof);
}

Ok(())
Expand All @@ -665,6 +673,12 @@ pub mod pallet {
// Now snapshot have been written, return result
transfer_result
}

pub fn error_and_event<E: Into<DispatchError> + Clone>(error: E) -> DispatchError {
let e = error.into();
Self::deposit_event(Event::<T>::ErrorAsEvent(e.clone()));
e
}
}

#[cfg(feature = "runtime-benchmarks")]
Expand Down
16 changes: 11 additions & 5 deletions pallets/airdrop/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ mod user_claim;
mod utility_functions;
pub mod prelude {
pub use super::{
force_get_creditor_account, get_last_event, minimal_test_ext, mock, run_to_block, samples,
set_creditor_balance, transfer_to_creditor,
force_get_creditor_account, get_all_events, get_last_event, minimal_test_ext, mock,
run_to_block, samples, set_creditor_balance, transfer_to_creditor,
};
pub use crate as pallet_airdrop;
pub use codec::Encode;
Expand Down Expand Up @@ -136,10 +136,16 @@ pub fn run_to_block(n: types::BlockNumberOf<Test>) {
}
}

pub fn get_last_event() -> Option<<Test as frame_system::Config>::Event> {
pub fn get_all_events() -> Vec<<Test as frame_system::Config>::Event> {
<frame_system::Pallet<Test>>::events()
.pop()
.map(|v| v.event)
.iter()
.map(|a| a.event.clone())
.collect::<Vec<_>>()
}

pub fn get_last_event() -> Option<<Test as frame_system::Config>::Event> {
let all_events = <frame_system::Pallet<Test>>::events();
all_events.last().map(|a| a.event.clone())
}

pub fn set_creditor_balance(balance: u64) {
Expand Down
2 changes: 1 addition & 1 deletion pallets/airdrop/src/tests/user_claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn claim_success() {

assert_eq!(total_balance, case.amount);
assert_eq!(usable_balance, expected_usable_amount);
assert_eq!(mapped_icon_wallet, expected_icon_address);
assert_eq!(mapped_icon_wallet.clone(), expected_icon_address);
assert_eq!(snapshot.vesting_block_number, expected_vesting_block_number);
assert_eq!(snapshot.initial_transfer, usable_balance);
assert_eq!(snapshot.instant_block_number, Some(0));
Expand Down
20 changes: 18 additions & 2 deletions pallets/airdrop/src/tests/utility_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,6 @@ fn get_vesting_amounts_split() {

#[test]
fn get_vesting_amounts_split_no_vesting() {
use sp_runtime::ArithmeticError;

minimal_test_ext().execute_with(|| {
let get_split_amounts: _ = utils::get_split_amounts::<Test>;
let defi_instant = 100;
Expand Down Expand Up @@ -634,3 +632,21 @@ fn storage_version() {
assert_eq!(1_u32, version);
});
}


#[test]
fn error_as_event() {
minimal_test_ext().execute_with(|| {
run_to_block(3);

let error: sp_runtime::DispatchError = PalletError::InvalidClaimAmount.into();
assert_eq!(
AirdropModule::error_and_event(error.clone()),
error.clone().into()
);

let just_emitted_event = get_last_event();
let expected_event = PalletEvent::ErrorAsEvent(error.clone());
assert_eq!(just_emitted_event, Some(expected_event.into()));
});
}