Skip to content

Commit

Permalink
Emit an event to know withdraw source for EBD (#887)
Browse files Browse the repository at this point in the history
* Emit an event to know withdraw source for EBD

* Address PR comments

* Fix test

* Address Z comments and remove unneeded clone

---------

Co-authored-by: JasonTulp <jason.j.tulp@gmail.com>
  • Loading branch information
KarishmaBothara and JasonTulp authored Oct 9, 2024
1 parent 2c44def commit c2825fe
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 177 deletions.
29 changes: 21 additions & 8 deletions pallet/erc20-peg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ pub use pallet::*;
pub mod pallet {
use super::{DispatchResult, *};

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);

#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T>(_);

#[pallet::genesis_config]
Expand Down Expand Up @@ -152,7 +156,7 @@ pub mod pallet {
/// Map from DelayedPaymentId to PendingPayment
#[pallet::storage]
pub type DelayedPayments<T: Config> =
StorageMap<_, Twox64Concat, DelayedPaymentId, PendingPayment>;
StorageMap<_, Twox64Concat, DelayedPaymentId, PendingPayment<T::AccountId>>;

/// Map from block number to DelayedPaymentIds scheduled for that block
#[pallet::storage]
Expand Down Expand Up @@ -215,7 +219,12 @@ pub mod pallet {
/// A bridged erc20 deposit succeeded.
Erc20Deposit { asset_id: AssetId, amount: Balance, beneficiary: T::AccountId },
/// Tokens were burnt for withdrawal on Ethereum as ERC20s
Erc20Withdraw { asset_id: AssetId, amount: Balance, beneficiary: EthAddress },
Erc20Withdraw {
asset_id: AssetId,
amount: Balance,
beneficiary: EthAddress,
source: T::AccountId,
},
/// A bridged erc20 deposit failed.
Erc20DepositFail { source: H160, abi_data: Vec<u8> },
/// The peg contract address has been set.
Expand Down Expand Up @@ -500,7 +509,7 @@ impl<T: Config> Pallet<T> {
let _imbalance = Self::burn_or_transfer(asset_id, &origin, amount)?;
Self::delay_payment(
delay,
PendingPayment::Withdrawal(message),
PendingPayment::Withdrawal((origin.clone(), message)),
asset_id,
origin,
);
Expand All @@ -517,7 +526,7 @@ impl<T: Config> Pallet<T> {

// Process transfer or withdrawal of payment asset
let _imbalance = Self::burn_or_transfer(asset_id, &origin, amount)?;
Self::process_withdrawal(message, asset_id)
Self::process_withdrawal(origin, message, asset_id)
}

/// For a withdrawal, either transfer ROOT tokens to Peg address or burn all other tokens
Expand Down Expand Up @@ -551,6 +560,7 @@ impl<T: Config> Pallet<T> {

/// Process withdrawal and send
fn process_withdrawal(
sender: T::AccountId,
withdrawal_message: WithdrawMessage,
asset_id: AssetId,
) -> Result<Option<u64>, DispatchError> {
Expand All @@ -574,6 +584,7 @@ impl<T: Config> Pallet<T> {
asset_id,
amount: withdrawal_message.amount.saturated_into(),
beneficiary: withdrawal_message.beneficiary,
source: sender,
});
Ok(Some(event_proof_id))
}
Expand All @@ -590,13 +601,15 @@ impl<T: Config> Pallet<T> {
});
}
},
PendingPayment::Withdrawal(withdrawal_message) => {
PendingPayment::Withdrawal((source, withdrawal_message)) => {
// At this stage it is assumed that a mapping between erc20 to asset id exists
// for this token
let asset_id = Erc20ToAssetId::<T>::get(withdrawal_message.token_address);
if let Some(asset_id) = asset_id {
// Process transfer or withdrawal of payment asset
if Self::process_withdrawal(withdrawal_message.clone(), asset_id).is_err() {
if Self::process_withdrawal(source, withdrawal_message.clone(), asset_id)
.is_err()
{
Self::deposit_event(Event::<T>::DelayedErc20WithdrawalFailed {
asset_id,
beneficiary: withdrawal_message.beneficiary.into(),
Expand All @@ -616,7 +629,7 @@ impl<T: Config> Pallet<T> {
/// Delay a withdrawal or deposit until a later block
pub fn delay_payment(
delay: BlockNumberFor<T>,
pending_payment: PendingPayment,
pending_payment: PendingPayment<T::AccountId>,
asset_id: AssetId,
source: T::AccountId,
) {
Expand Down Expand Up @@ -644,7 +657,7 @@ impl<T: Config> Pallet<T> {

// Throw event for delayed payment
match pending_payment {
PendingPayment::Withdrawal(withdrawal) => {
PendingPayment::Withdrawal((_sender, withdrawal)) => {
Self::deposit_event(Event::<T>::Erc20WithdrawalDelayed {
payment_id,
scheduled_block: payment_block,
Expand Down
2 changes: 1 addition & 1 deletion pallet/erc20-peg/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ fn withdraw_with_delay() {
assert_eq!(DelayedPaymentSchedule::<Test>::get(payment_block), vec![delayed_payment_id]);
assert_eq!(
DelayedPayments::<Test>::get(delayed_payment_id),
Some(PendingPayment::Withdrawal(message))
Some(PendingPayment::Withdrawal((account, message)))
);
// Check payment id has been increased
assert_eq!(<NextDelayedPaymentId<Test>>::get(), delayed_payment_id + 1);
Expand Down
4 changes: 2 additions & 2 deletions pallet/erc20-peg/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ pub enum WithdrawCallOrigin {

/// A pending deposit or withdrawal
#[derive(Debug, Clone, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen)]
pub enum PendingPayment {
pub enum PendingPayment<AccountId> {
/// A deposit event (deposit_event, tx_hash)
Deposit(Erc20DepositEvent),
/// A withdrawal (withdrawal_message)
Withdrawal(WithdrawMessage),
Withdrawal((AccountId, WithdrawMessage)),
}

/// A deposit event made by the ERC20 peg contract on Ethereum
Expand Down
215 changes: 215 additions & 0 deletions runtime/src/migrations/erc20_peg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
// Copyright 2022-2024 Futureverse Corporation Limited
//
// Licensed under the LGPL, Version 3.0 (the "License");
// you may not use this file except in compliance with the License.
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// You may obtain a copy of the License at the root of this project source code

use crate::{Erc20Peg, Runtime, Weight};
use frame_support::{
dispatch::GetStorageVersion,
traits::{OnRuntimeUpgrade, StorageVersion},
};

#[allow(unused_imports)]
use sp_runtime::DispatchError;
#[allow(unused_imports)]
use sp_std::vec::Vec;

pub struct Upgrade;

impl OnRuntimeUpgrade for Upgrade {
fn on_runtime_upgrade() -> Weight {
let current = Erc20Peg::current_storage_version();
let onchain = Erc20Peg::on_chain_storage_version();
log::info!(target: "Migration", "Erc20Peg: Running migration with current storage version {current:?} / on-chain {onchain:?}");

let mut weight = <Runtime as frame_system::Config>::DbWeight::get().reads(2);

if onchain < 1 {
log::info!(target: "Migration", "XRPLBridge: Migrating from on-chain version {onchain:?} to on-chain version {current:?}.");
weight += v1::migrate::<Runtime>();

StorageVersion::new(1).put::<Erc20Peg>();

log::info!(target: "Migration", "Erc20Peg: Migration successfully completed.");
} else {
log::info!(target: "Migration", "Erc20Peg: No migration was done, however migration code needs to be removed.");
}

weight
}

#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, DispatchError> {
log::info!(target: "Migration", "Erc20Peg: Upgrade to v1 Pre Upgrade.");
let onchain = Erc20Peg::on_chain_storage_version();
// Return OK(()) if upgrade has already been done
if onchain == 1 {
return Ok(Vec::new());
}
assert_eq!(onchain, 0);

Ok(Vec::new())
}

#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), DispatchError> {
log::info!(target: "Migration", "Erc20Peg: Upgrade to v1 Post Upgrade.");
let current = Erc20Peg::current_storage_version();
let onchain = Erc20Peg::on_chain_storage_version();
assert_eq!(current, 1);
assert_eq!(onchain, 1);
Ok(())
}
}

#[allow(dead_code)]
#[allow(unused_imports)]
pub mod v1 {
use super::*;
use crate::{
migrations::{Map, Value},
sp_api_hidden_includes_construct_runtime::hidden_include::IterableStorageMap,
};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
sp_runtime::RuntimeDebug, storage_alias, weights::Weight, BoundedVec, StorageHasher,
Twox64Concat,
};
use pallet_erc20_peg::{
types::{Erc20DepositEvent, PendingPayment, WithdrawMessage},
DelayedPayments,
};
use scale_info::TypeInfo;
use seed_primitives::{AssetId, Balance, CollectionUuid};
use sp_core::{Get, H160};
type AccountId = <Runtime as frame_system::Config>::AccountId;

#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, TypeInfo, MaxEncodedLen)]
pub enum OldPendingPayment {
/// A deposit event (deposit_event, tx_hash)
Deposit(Erc20DepositEvent),
/// A withdrawal (withdrawal_message)
Withdrawal(WithdrawMessage),
}

pub fn migrate<T: frame_system::Config + pallet_erc20_peg::Config>() -> Weight
where
AccountId: From<H160>,
{
log::info!(target: "Migration", "ERC20Peg:[DelayedPayments] Migrating from on-chain version 0 to on-chain version 1");
let mut weight: Weight = Weight::zero();
let default_account = AccountId::default();

// Get total number of participants in SaleParticipation
DelayedPayments::<T>::translate::<OldPendingPayment, _>(|_, pending_payment| {
// Reads: DelayedPayments
// Writes: DelayedPayments
weight += <Runtime as frame_system::Config>::DbWeight::get().reads_writes(1, 1);
let new_pending_payment = match pending_payment {
OldPendingPayment::Deposit(deposit) => PendingPayment::Deposit(deposit),
OldPendingPayment::Withdrawal(withdrawal_message) => {
PendingPayment::Withdrawal((default_account, withdrawal_message))
},
};
Some(new_pending_payment)
});

log::info!(target: "Migration", "ERC20Peg: successfully migrated DelayedPayments");

weight
}

#[cfg(test)]
mod tests {
use super::*;
use crate::migrations::tests::new_test_ext;
use pallet_erc20_peg::types::DelayedPaymentId;
use sp_core::{H160, U256};
use sp_runtime::Permill;

fn create_account(seed: u64) -> AccountId {
AccountId::from(H160::from_low_u64_be(seed))
}

#[test]
fn migration_test() {
new_test_ext().execute_with(|| {
// Setup storage
StorageVersion::new(0).put::<Erc20Peg>();

// Deposit pending payment
let payment_id_key_1 = Twox64Concat::hash(&(1 as DelayedPaymentId).encode());
let pending_payment_1 = OldPendingPayment::Deposit(Erc20DepositEvent {
token_address: create_account(12).into(),
amount: U256::from(13),
beneficiary: create_account(14).into(),
});

Map::unsafe_storage_put::<OldPendingPayment>(
b"Erc20Peg",
b"DelayedPayments",
&payment_id_key_1,
pending_payment_1.clone(),
);

// Withdrawal pending payment
let payment_id_key_2 = Twox64Concat::hash(&(2 as DelayedPaymentId).encode());
let pending_payment_2 = OldPendingPayment::Withdrawal(WithdrawMessage {
token_address: create_account(15).into(),
amount: U256::from(16),
beneficiary: create_account(17).into(),
});

Map::unsafe_storage_put::<OldPendingPayment>(
b"Erc20Peg",
b"DelayedPayments",
&payment_id_key_2,
pending_payment_2.clone(),
);

// Do runtime upgrade
Upgrade::on_runtime_upgrade();
assert_eq!(Erc20Peg::on_chain_storage_version(), 1);

// Verify storage
let expected_pending_payment_1 = PendingPayment::Deposit(Erc20DepositEvent {
token_address: create_account(12).into(),
amount: U256::from(13),
beneficiary: create_account(14).into(),
});
assert_eq!(
Map::unsafe_storage_get::<PendingPayment<AccountId>>(
b"Erc20Peg",
b"DelayedPayments",
&payment_id_key_1,
),
Some(expected_pending_payment_1)
);

// Withdrawal should now be a tuple with default AccountId
let expected_pending_payment_2 = PendingPayment::Withdrawal((
AccountId::default(),
WithdrawMessage {
token_address: create_account(15).into(),
amount: U256::from(16),
beneficiary: create_account(17).into(),
},
));
assert_eq!(
Map::unsafe_storage_get::<PendingPayment<AccountId>>(
b"Erc20Peg",
b"DelayedPayments",
&payment_id_key_2,
),
Some(expected_pending_payment_2)
);
});
}
}
}
9 changes: 5 additions & 4 deletions runtime/src/migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.
// You may obtain a copy of the License at the root of this project source code

mod xrpl_bridge;
mod erc20_peg;

use codec::{Decode, Encode, FullCodec, FullEncode};
use frame_support::{
Expand All @@ -34,16 +34,16 @@ pub struct AllMigrations;
impl OnRuntimeUpgrade for AllMigrations {
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, DispatchError> {
xrpl_bridge::Upgrade::pre_upgrade()
erc20_peg::Upgrade::pre_upgrade()
}

fn on_runtime_upgrade() -> Weight {
xrpl_bridge::Upgrade::on_runtime_upgrade()
erc20_peg::Upgrade::on_runtime_upgrade()
}

#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), DispatchError> {
xrpl_bridge::Upgrade::post_upgrade(state)
erc20_peg::Upgrade::post_upgrade(state)
}
}

Expand All @@ -53,6 +53,7 @@ mod tests {
use sp_core::H160;
use sp_runtime::BuildStorage;

#[allow(dead_code)]
pub fn create_account<AccountId: From<H160>>(seed: u64) -> AccountId {
AccountId::from(H160::from_low_u64_be(seed))
}
Expand Down
Loading

0 comments on commit c2825fe

Please sign in to comment.