Skip to content
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
109 changes: 109 additions & 0 deletions precompiles/xtokens/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ pub type CurrencyIdOf<Runtime> = <Runtime as orml_xtokens::Config>::CurrencyId;
#[derive(Debug, PartialEq)]
pub enum Action {
Transfer = "transfer(address,uint256,(uint8,bytes[]),uint64)",
TransferWithFee = "transfer_with_fee(address,uint256,uint256,(uint8,bytes[]),uint64)",
TransferMultiAsset = "transfer_multiasset((uint8,bytes[]),uint256,(uint8,bytes[]),uint64)",
TransferMultiAssetWithFee =
"transfer_multiasset_with_fee((uint8,bytes[]),uint256,uint256,(uint8,bytes[]),uint64)",
}

/// A precompile to wrap the functionality from xtokens
Expand Down Expand Up @@ -78,7 +81,11 @@ where

match selector {
Action::Transfer => Self::transfer(input, gasometer, context),
Action::TransferWithFee => Self::transfer_with_fee(input, gasometer, context),
Action::TransferMultiAsset => Self::transfer_multiasset(input, gasometer, context),
Action::TransferMultiAssetWithFee => {
Self::transfer_multiasset_with_fee(input, gasometer, context)
}
}
}
}
Expand Down Expand Up @@ -138,6 +145,59 @@ where
})
}

fn transfer_with_fee(
input: &mut EvmDataReader,
gasometer: &mut Gasometer,
context: &Context,
) -> EvmResult<PrecompileOutput> {
input.expect_arguments(gasometer, 5)?;

let to_address: H160 = input.read::<Address>(gasometer)?.into();
let amount: U256 = input.read(gasometer)?;
let fee: U256 = input.read(gasometer)?;

// We use the MultiLocation, which we have instructed how to read
// In the end we are using the encoding
let destination: MultiLocation = input.read::<MultiLocation>(gasometer)?;

let dest_weight: u64 = input.read::<u64>(gasometer)?;

let to_account = Runtime::AddressMapping::into_account_id(to_address);
// We convert the address into a currency id xtokens understands
let currency_id: <Runtime as orml_xtokens::Config>::CurrencyId =
Runtime::account_to_currency_id(to_account)
.ok_or(gasometer.revert("cannot convert into currency id"))?;

let origin = Runtime::AddressMapping::into_account_id(context.caller);

// Transferred amount
let amount = amount
.try_into()
.map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?;

// Fee amount
let fee = fee
.try_into()
.map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?;

let call = orml_xtokens::Call::<Runtime>::transfer_with_fee {
currency_id,
amount,
fee,
dest: Box::new(VersionedMultiLocation::V1(destination)),
dest_weight,
};

RuntimeHelper::<Runtime>::try_dispatch(Some(origin).into(), call, gasometer)?;

Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
cost: gasometer.used_gas(),
output: Default::default(),
logs: Default::default(),
})
}

fn transfer_multiasset(
input: &mut EvmDataReader,
gasometer: &mut Gasometer,
Expand Down Expand Up @@ -180,4 +240,53 @@ where
logs: Default::default(),
})
}

fn transfer_multiasset_with_fee(
input: &mut EvmDataReader,
gasometer: &mut Gasometer,
context: &Context,
) -> EvmResult<PrecompileOutput> {
input.expect_arguments(gasometer, 5)?;

// asset is defined as a multiLocation. For now we are assuming these are concrete
// fungible assets
let asset_multilocation: MultiLocation = input.read::<MultiLocation>(gasometer)?;
let amount: U256 = input.read(gasometer)?;
let fee: U256 = input.read(gasometer)?;

// read destination
let destination: MultiLocation = input.read::<MultiLocation>(gasometer)?;

let dest_weight: u64 = input.read::<u64>(gasometer)?;

let origin = Runtime::AddressMapping::into_account_id(context.caller);
let amount = amount
.try_into()
.map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?;
let fee = fee
.try_into()
.map_err(|_| gasometer.revert("Amount is too large for provided balance type"))?;

let call = orml_xtokens::Call::<Runtime>::transfer_multiasset_with_fee {
asset: Box::new(VersionedMultiAsset::V1(MultiAsset {
id: AssetId::Concrete(asset_multilocation.clone()),
fun: Fungibility::Fungible(amount),
})),
fee: Box::new(VersionedMultiAsset::V1(MultiAsset {
id: AssetId::Concrete(asset_multilocation),
fun: Fungibility::Fungible(fee),
})),
dest: Box::new(VersionedMultiLocation::V1(destination)),
dest_weight,
};

RuntimeHelper::<Runtime>::try_dispatch(Some(origin).into(), call, gasometer)?;

Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
cost: gasometer.used_gas(),
output: Default::default(),
logs: Default::default(),
})
}
}
Loading