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

feat: track pending payments to MBSM #851

Merged
merged 4 commits into from
Dec 13, 2024
Merged
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
3 changes: 1 addition & 2 deletions pallets/services/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -968,11 +968,10 @@ impl<T: Config> Pallet<T> {
/// Moves a `value` amount of tokens from the caller's account to `to`.
pub fn erc20_transfer(
erc20: H160,
caller: &T::AccountId,
from: H160,
to: H160,
value: BalanceOf<T>,
) -> Result<(bool, Weight), DispatchErrorWithPostInfo> {
let from = T::EvmAddressMapping::into_address(caller.clone());
#[allow(deprecated)]
let transfer_fn = Function {
name: String::from("transfer"),
Expand Down
124 changes: 120 additions & 4 deletions pallets/services/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub mod module {
use sp_runtime::Percent;
use sp_std::vec::Vec;
use tangle_primitives::services::MasterBlueprintServiceManagerRevision;
use tangle_primitives::{services::*, MultiAssetDelegationInfo};
use tangle_primitives::{services::*, Account, MultiAssetDelegationInfo};
use types::*;

#[pallet::config]
Expand Down Expand Up @@ -291,6 +291,12 @@ pub mod module {
MaxMasterBlueprintServiceManagerVersionsExceeded,
/// The ERC20 transfer failed.
ERC20TransferFailed,
/// Missing EVM Origin for the EVM execution.
MissingEVMOrigin,
/// Expected the account to be an EVM address.
ExpectedEVMAddress,
/// Expected the account to be an account ID.
ExpectedAccountId,
}

#[pallet::event]
Expand Down Expand Up @@ -622,6 +628,15 @@ pub mod module {
OperatorProfile<T::Constraints>,
ResultQuery<Error<T>::OperatorProfileNotFound>,
>;
/// Holds the service payment information for a service request.
/// Once the service is initiated, the payment is transferred to the MBSM and this
/// information is removed.
///
/// Service Requst ID -> Service Payment
#[pallet::storage]
#[pallet::getter(fn service_payment)]
pub type StagingServicePayments<T: Config> =
StorageMap<_, Identity, u64, StagingServicePayment<T::AccountId, T::AssetId, BalanceOf<T>>>;

#[pallet::call]
impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -846,6 +861,7 @@ pub mod module {
#[pallet::weight(T::WeightInfo::request())]
pub fn request(
origin: OriginFor<T>,
evm_origin: Option<H160>,
#[pallet::compact] blueprint_id: u64,
permitted_callers: Vec<T::AccountId>,
operators: Vec<T::AccountId>,
Expand All @@ -871,10 +887,11 @@ pub mod module {
}

let mut native_value = Zero::zero();
let request_id = NextServiceRequestId::<T>::get();
shekohex marked this conversation as resolved.
Show resolved Hide resolved

if value != Zero::zero() {
// Payment transfer
match payment_asset {
let refund_to = match payment_asset {
// Handle the case of native currency.
Asset::Custom(asset_id) if asset_id == Zero::zero() => {
T::Currency::transfer(
Expand All @@ -884,6 +901,7 @@ pub mod module {
ExistenceRequirement::KeepAlive,
)?;
native_value = value;
Account::id(caller.clone())
},
Asset::Custom(asset_id) => {
T::Fungibles::transfer(
Expand All @@ -893,16 +911,31 @@ pub mod module {
value,
Preservation::Preserve,
)?;
Account::id(caller.clone())
},
Asset::Erc20(token) => {
// origin check.
let evm_origin = evm_origin.ok_or(Error::<T>::MissingEVMOrigin)?;
let mapped_origin = T::EvmAddressMapping::into_account_id(evm_origin);
ensure!(mapped_origin == caller, DispatchError::BadOrigin);
let (success, _weight) =
Self::erc20_transfer(token, &caller, Self::address(), value)?;
Self::erc20_transfer(token, evm_origin, Self::address(), value)?;
ensure!(success, Error::<T>::ERC20TransferFailed);
Account::from(evm_origin)
},
};

// Save the payment information for the service request.
let payment = StagingServicePayment {
request_id,
refund_to,
asset: payment_asset,
amount: value,
};

StagingServicePayments::<T>::insert(request_id, payment);
}

let request_id = NextServiceRequestId::<T>::get();
let (allowed, _weight) = Self::on_request_hook(
&blueprint,
blueprint_id,
Expand Down Expand Up @@ -1068,6 +1101,44 @@ pub mod module {
.map_err(|_| Error::<T>::MaxServicesPerUserExceeded)
})?;

// Payment
if let Some(payment) = Self::service_payment(request_id) {
// send payments to the MBSM
let mbsm_address = Self::mbsm_address_of(&blueprint)?;
let mbsm_account_id = T::EvmAddressMapping::into_account_id(mbsm_address);
match payment.asset {
Asset::Custom(asset_id) if asset_id == Zero::zero() => {
T::Currency::transfer(
&Self::account_id(),
&mbsm_account_id,
payment.amount,
ExistenceRequirement::AllowDeath,
)?;
},
Asset::Custom(asset_id) => {
T::Fungibles::transfer(
asset_id,
&Self::account_id(),
&mbsm_account_id,
payment.amount,
Preservation::Expendable,
)?;
},
Asset::Erc20(token) => {
let (success, _weight) = Self::erc20_transfer(
token,
Self::address(),
mbsm_address,
payment.amount,
)?;
ensure!(success, Error::<T>::ERC20TransferFailed);
},
}

// Remove the payment information.
StagingServicePayments::<T>::remove(request_id);
}

let (allowed, _weight) = Self::on_service_init_hook(
&blueprint,
blueprint_id,
Expand Down Expand Up @@ -1131,6 +1202,51 @@ pub mod module {
request_id,
});

// Refund the payment
if let Some(payment) = Self::service_payment(request_id) {
match payment.asset {
Asset::Custom(asset_id) if asset_id == Zero::zero() => {
let refund_to = payment
.refund_to
.try_into_account_id()
.map_err(|_| Error::<T>::ExpectedAccountId)?;
T::Currency::transfer(
&Self::account_id(),
&refund_to,
payment.amount,
ExistenceRequirement::AllowDeath,
)?;
},
Asset::Custom(asset_id) => {
let refund_to = payment
.refund_to
.try_into_account_id()
.map_err(|_| Error::<T>::ExpectedAccountId)?;
T::Fungibles::transfer(
asset_id,
&Self::account_id(),
&refund_to,
payment.amount,
Preservation::Expendable,
)?;
},
Asset::Erc20(token) => {
let refund_to = payment
.refund_to
.try_into_address()
.map_err(|_| Error::<T>::ExpectedEVMAddress)?;
let (success, _weight) = Self::erc20_transfer(
token,
Self::address(),
refund_to,
payment.amount,
)?;
ensure!(success, Error::<T>::ERC20TransferFailed);
},
}
StagingServicePayments::<T>::remove(request_id);
}

// TODO: make use of the returned weight from the hook.
Ok(PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes })
}
Expand Down
29 changes: 23 additions & 6 deletions pallets/services/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,7 @@ impl EvmAddressMapping<AccountId> for PalletEVMAddressMapping {
}

fn into_address(account_id: AccountId) -> H160 {
account_id.using_encoded(|b| {
let mut addr = [0u8; 20];
addr.copy_from_slice(&b[0..20]);
H160(addr)
})
H160::from_slice(&AsRef::<[u8; 32]>::as_ref(&account_id)[0..20])
1xstj marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -473,7 +469,16 @@ pub fn mock_pub_key(id: u8) -> AccountId {
}

pub fn mock_address(id: u8) -> H160 {
H160([id; 20])
H160::from_slice(&[id; 20])
}

pub fn account_id_to_address(account_id: AccountId) -> H160 {
H160::from_slice(&AsRef::<[u8; 32]>::as_ref(&account_id)[0..20])
}

pub fn address_to_account_id(address: H160) -> AccountId {
use pallet_evm::AddressMapping;
<Runtime as pallet_evm::Config>::AddressMapping::into_account_id(address)
}

pub fn mock_authorities(vec: Vec<u8>) -> Vec<AccountId> {
Expand Down Expand Up @@ -569,6 +574,18 @@ pub fn new_test_ext_raw_authorities(authorities: Vec<AccountId>) -> sp_io::TestE
);
}

for a in &authorities {
evm_accounts.insert(
account_id_to_address(a.clone()),
fp_evm::GenesisAccount {
code: vec![],
storage: Default::default(),
nonce: Default::default(),
balance: Uint::from(1_000).mul(Uint::from(10).pow(Uint::from(18))),
},
);
}

let evm_config =
pallet_evm::GenesisConfig::<Runtime> { accounts: evm_accounts, ..Default::default() };

Expand Down
Loading
Loading