Skip to content

Commit

Permalink
Assets erc20 call delegate call hotfix runtime 902 (#999)
Browse files Browse the repository at this point in the history
* fix return types in asset erc20 and refactor asseterc20precompileset (#990)

* Fix return type matching the open zeppeling ERC20 and the way to access AssetId in PrecompileSet

* Start typescrypt testing this

* Fix typescript integration tests

* Check if caller is same as from

* add delgeate calls to contract instance

* add more cases considering delegate and normal calls

* Add contract compiled

* Increment node wake up time yet again

* Increment the proper timeout

* Add PR suggestions

* Bump specs to 902 in moonbase
  • Loading branch information
girazoki authored Nov 17, 2021
1 parent b493846 commit 4641c30
Show file tree
Hide file tree
Showing 7 changed files with 5,520 additions and 2,389 deletions.
115 changes: 35 additions & 80 deletions precompiles/assets-erc20/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use frame_support::{
};
use sp_runtime::traits::Zero;

use pallet_evm::{AddressMapping, Precompile, PrecompileSet};
use pallet_evm::{AddressMapping, PrecompileSet};
use precompile_utils::{
error, keccak256, Address, EvmData, EvmDataReader, EvmDataWriter, EvmResult, Gasometer,
LogsBuilder, RuntimeHelper,
Expand Down Expand Up @@ -94,9 +94,13 @@ pub struct Erc20AssetsPrecompileSet<Runtime, Instance: 'static = ()>(
impl<Runtime, Instance> PrecompileSet for Erc20AssetsPrecompileSet<Runtime, Instance>
where
Instance: 'static,
Erc20AssetsPrecompile<Runtime, Instance>: Precompile,
Runtime: pallet_assets::Config<Instance> + pallet_evm::Config,
Runtime: pallet_assets::Config<Instance> + pallet_evm::Config + frame_system::Config,
Runtime::Call: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
Runtime::Call: From<pallet_assets::Call<Runtime, Instance>>,
<Runtime::Call as Dispatchable>::Origin: From<Option<Runtime::AccountId>>,
BalanceOf<Runtime, Instance>: TryFrom<U256> + Into<U256> + EvmData,
Runtime: AccountIdAssetIdConversion<Runtime::AccountId, AssetIdOf<Runtime, Instance>>,
<<Runtime as frame_system::Config>::Call as Dispatchable>::Origin: OriginTrait,
{
fn execute(
address: H160,
Expand All @@ -115,49 +119,31 @@ where
// The other options is to check the asset existence in pallet-asset-manager, but
// this makes the precompiles dependent on such a pallet, which is not ideal
if !pallet_assets::Pallet::<Runtime, Instance>::total_supply(asset_id).is_zero() {
return Some(
<Erc20AssetsPrecompile<Runtime, Instance> as Precompile>::execute(
input, target_gas, context,
),
);
let result = {
let (input, selector) = match EvmDataReader::new_with_selector(input) {
Ok((input, selector)) => (input, selector),
Err(e) => return Some(Err(e)),
};

match selector {
Action::TotalSupply => Self::total_supply(asset_id, input, target_gas),
Action::BalanceOf => Self::balance_of(asset_id, input, target_gas),
Action::Allowance => Self::allowance(asset_id, input, target_gas),
Action::Approve => Self::approve(asset_id, input, target_gas, context),
Action::Transfer => Self::transfer(asset_id, input, target_gas, context),
Action::TransferFrom => {
Self::transfer_from(asset_id, input, target_gas, context)
}
}
};
return Some(result);
}
}
None
}
}

pub struct Erc20AssetsPrecompile<Runtime, Instance: 'static = ()>(PhantomData<(Runtime, Instance)>);

impl<Runtime, Instance> Precompile for Erc20AssetsPrecompile<Runtime, Instance>
where
Instance: 'static,
Runtime: pallet_assets::Config<Instance> + pallet_evm::Config,
Runtime::Call: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
Runtime::Call: From<pallet_assets::Call<Runtime, Instance>>,
<Runtime::Call as Dispatchable>::Origin: From<Option<Runtime::AccountId>>,
BalanceOf<Runtime, Instance>: TryFrom<U256> + Into<U256> + EvmData,
Runtime: AccountIdAssetIdConversion<Runtime::AccountId, AssetIdOf<Runtime, Instance>>,
<<Runtime as frame_system::Config>::Call as Dispatchable>::Origin: OriginTrait,
{
fn execute(
input: &[u8], //Reminder this is big-endian
target_gas: Option<u64>,
context: &Context,
) -> Result<PrecompileOutput, ExitError> {
let (input, selector) = EvmDataReader::new_with_selector(input)?;

match selector {
Action::TotalSupply => Self::total_supply(input, target_gas, context),
Action::BalanceOf => Self::balance_of(input, target_gas, context),
Action::Allowance => Self::allowance(input, target_gas, context),
Action::Approve => Self::approve(input, target_gas, context),
Action::Transfer => Self::transfer(input, target_gas, context),
Action::TransferFrom => Self::transfer_from(input, target_gas, context),
}
}
}

impl<Runtime, Instance> Erc20AssetsPrecompile<Runtime, Instance>
impl<Runtime, Instance> Erc20AssetsPrecompileSet<Runtime, Instance>
where
Instance: 'static,
Runtime: pallet_assets::Config<Instance> + pallet_evm::Config + frame_system::Config,
Expand All @@ -169,21 +155,16 @@ where
<<Runtime as frame_system::Config>::Call as Dispatchable>::Origin: OriginTrait,
{
fn total_supply(
asset_id: AssetIdOf<Runtime, Instance>,
input: EvmDataReader,
target_gas: Option<u64>,
context: &Context,
) -> EvmResult<PrecompileOutput> {
let mut gasometer = Gasometer::new(target_gas);
gasometer.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let execution_address = Runtime::AddressMapping::into_account_id(context.address);

// Parse input.
input.expect_arguments(0)?;

let asset_id: AssetIdOf<Runtime, Instance> =
Runtime::account_to_asset_id(execution_address).ok_or(error("non-assetId address"))?;

// Fetch info.
let amount: U256 =
pallet_assets::Pallet::<Runtime, Instance>::total_issuance(asset_id).into();
Expand All @@ -198,9 +179,9 @@ where
}

fn balance_of(
asset_id: AssetIdOf<Runtime, Instance>,
mut input: EvmDataReader,
target_gas: Option<u64>,
context: &Context,
) -> EvmResult<PrecompileOutput> {
let mut gasometer = Gasometer::new(target_gas);
gasometer.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;
Expand All @@ -212,12 +193,6 @@ where

// Fetch info.
let amount: U256 = {
let execution_address = Runtime::AddressMapping::into_account_id(context.address);

let asset_id: AssetIdOf<Runtime, Instance> =
Runtime::account_to_asset_id(execution_address)
.ok_or(error("non-assetId address"))?;

let owner: Runtime::AccountId = Runtime::AddressMapping::into_account_id(owner);
pallet_assets::Pallet::<Runtime, Instance>::balance(asset_id, &owner).into()
};
Expand All @@ -233,9 +208,9 @@ where

// This should be added once https://github.com/paritytech/substrate/pull/9757 is merged.
fn allowance(
_asset_id: AssetIdOf<Runtime, Instance>,
mut _input: EvmDataReader,
_target_gas: Option<u64>,
_context: &Context,
) -> EvmResult<PrecompileOutput> {
Err(error("unimplemented"))
/* let mut gasometer = Gasometer::new(target_gas);
Expand All @@ -249,11 +224,6 @@ where
// Fetch info.
let amount: U256 = {
let execution_address = Runtime::AddressMapping::into_account_id(context.address);
let asset_id: AssetIdOf<Runtime, Instance> =
Runtime::account_to_asset_id(execution_address)
.ok_or(error("non-assetId address"))?;
let owner: Runtime::AccountId = Runtime::AddressMapping::into_account_id(owner);
let spender: Runtime::AccountId = Runtime::AddressMapping::into_account_id(spender);
Expand All @@ -271,6 +241,7 @@ where
}

fn approve(
asset_id: AssetIdOf<Runtime, Instance>,
mut input: EvmDataReader,
target_gas: Option<u64>,
context: &Context,
Expand All @@ -285,11 +256,6 @@ where
let amount = input.read::<BalanceOf<Runtime, Instance>>()?;

{
let execution_address = Runtime::AddressMapping::into_account_id(context.address);
let asset_id: AssetIdOf<Runtime, Instance> =
Runtime::account_to_asset_id(execution_address)
.ok_or(error("non-assetId address"))?;

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

let spender: Runtime::AccountId = Runtime::AddressMapping::into_account_id(spender);
Expand Down Expand Up @@ -334,12 +300,11 @@ where
)?;
gasometer.record_cost(used_gas)?;
}

// Build output.
Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
cost: gasometer.used_gas(),
output: vec![],
output: EvmDataWriter::new().write(true).build(),
logs: LogsBuilder::new(context.address)
.log3(
SELECTOR_LOG_APPROVAL,
Expand All @@ -352,6 +317,7 @@ where
}

fn transfer(
asset_id: AssetIdOf<Runtime, Instance>,
mut input: EvmDataReader,
target_gas: Option<u64>,
context: &Context,
Expand All @@ -367,11 +333,6 @@ where

// Build call with origin.
{
let execution_address = Runtime::AddressMapping::into_account_id(context.address);
let asset_id: AssetIdOf<Runtime, Instance> =
Runtime::account_to_asset_id(execution_address)
.ok_or(error("non-assetId address"))?;

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

Expand All @@ -392,7 +353,7 @@ where
Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
cost: gasometer.used_gas(),
output: vec![],
output: EvmDataWriter::new().write(true).build(),
logs: LogsBuilder::new(context.address)
.log3(
SELECTOR_LOG_TRANSFER,
Expand All @@ -405,6 +366,7 @@ where
}

fn transfer_from(
asset_id: AssetIdOf<Runtime, Instance>,
mut input: EvmDataReader,
target_gas: Option<u64>,
context: &Context,
Expand All @@ -419,16 +381,10 @@ where
let amount = input.read::<BalanceOf<Runtime, Instance>>()?;

{
let execution_address = Runtime::AddressMapping::into_account_id(context.address);
let asset_id: AssetIdOf<Runtime, Instance> =
Runtime::account_to_asset_id(execution_address)
.ok_or(error("non-assetId address"))?;
let caller: Runtime::AccountId =
Runtime::AddressMapping::into_account_id(context.caller);
let from: Runtime::AccountId = Runtime::AddressMapping::into_account_id(from.clone());
let to: Runtime::AccountId = Runtime::AddressMapping::into_account_id(to);

// If caller is "from", it can spend as much as it wants from its own balance.
let used_gas = if caller != from {
// Dispatch call (if enough gas).
RuntimeHelper::<Runtime>::try_dispatch(
Expand All @@ -455,12 +411,11 @@ where
}?;
gasometer.record_cost(used_gas)?;
}

// Build output.
Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
cost: gasometer.used_gas(),
output: vec![],
output: EvmDataWriter::new().write(true).build(),
logs: LogsBuilder::new(context.address)
.log3(
SELECTOR_LOG_TRANSFER,
Expand Down
12 changes: 6 additions & 6 deletions precompiles/assets-erc20/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ fn approve() {
),
Some(Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: Default::default(),
output: EvmDataWriter::new().write(true).build(),
cost: 56999756u64,
logs: LogsBuilder::new(Account::AssetId(0u128).into())
.log3(
Expand Down Expand Up @@ -429,7 +429,7 @@ fn transfer() {
),
Some(Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: Default::default(),
output: EvmDataWriter::new().write(true).build(),
cost: 83206756u64, // 1 weight => 1 gas in mock
logs: LogsBuilder::new(Account::AssetId(0u128).into())
.log3(
Expand Down Expand Up @@ -578,7 +578,7 @@ fn transfer_from() {
),
Some(Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: Default::default(),
output: EvmDataWriter::new().write(true).build(),
cost: 107172756u64, // 1 weight => 1 gas in mock
logs: LogsBuilder::new(Account::AssetId(0u128).into())
.log3(
Expand Down Expand Up @@ -693,7 +693,7 @@ fn transfer_from_non_incremental_approval() {
),
Some(Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: Default::default(),
output: EvmDataWriter::new().write(true).build(),
cost: 56999756u64,
logs: LogsBuilder::new(Account::AssetId(0u128).into())
.log3(
Expand Down Expand Up @@ -726,7 +726,7 @@ fn transfer_from_non_incremental_approval() {
),
Some(Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: Default::default(),
output: EvmDataWriter::new().write(true).build(),
cost: 114357756u64,
logs: LogsBuilder::new(Account::AssetId(0u128).into())
.log3(
Expand Down Expand Up @@ -860,7 +860,7 @@ fn transfer_from_self() {
),
Some(Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: Default::default(),
output: EvmDataWriter::new().write(true).build(),
cost: 83206756u64, // 1 weight => 1 gas in mock
logs: LogsBuilder::new(Account::AssetId(0u128).into())
.log3(
Expand Down
2 changes: 1 addition & 1 deletion runtime/moonbase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("moonbase"),
impl_name: create_runtime_str!("moonbase"),
authoring_version: 3,
spec_version: 0901,
spec_version: 0902,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 2,
Expand Down
6 changes: 3 additions & 3 deletions runtime/moonbase/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,7 @@ fn asset_erc20_precompiles_transfer() {
// Expected result for a transfer
let expected_result = Some(Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: Default::default(),
output: EvmDataWriter::new().write(true).build(),
cost: 25084u64,
logs: LogsBuilder::new(asset_precompile_address)
.log3(
Expand Down Expand Up @@ -1227,7 +1227,7 @@ fn asset_erc20_precompiles_approve() {
// Expected result for approve
let expected_result = Some(Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: Default::default(),
output: EvmDataWriter::new().write(true).build(),
cost: 16035u64,
logs: LogsBuilder::new(asset_precompile_address)
.log3(
Expand Down Expand Up @@ -1260,7 +1260,7 @@ fn asset_erc20_precompiles_approve() {
// Expected result for transfer_from
let expected_result = Some(Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: Default::default(),
output: EvmDataWriter::new().write(true).build(),
cost: 31042u64,
logs: LogsBuilder::new(asset_precompile_address)
.log3(
Expand Down
Loading

0 comments on commit 4641c30

Please sign in to comment.