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

fix: reversible evm address #902

Merged
merged 7 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
108 changes: 1 addition & 107 deletions integration-tests/src/evm_permit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ use frame_support::traits::Contains;
use frame_support::{assert_noop, assert_ok, sp_runtime::codec::Encode};
use frame_system::RawOrigin;
use hydradx_runtime::evm::precompiles::{CALLPERMIT, DISPATCH_ADDR};
use hydradx_runtime::{
Balances, Currencies, EVMAccounts, MultiTransactionPayment, Omnipool, RuntimeCall, RuntimeOrigin, Tokens,
};
use hydradx_runtime::{Balances, Currencies, MultiTransactionPayment, Omnipool, RuntimeCall, RuntimeOrigin, Tokens};
use libsecp256k1::{sign, Message, SecretKey};
use orml_traits::MultiCurrency;
use pallet_evm_accounts::EvmNonceProvider;
Expand Down Expand Up @@ -234,110 +232,6 @@ fn dispatch_permit_fee_should_be_paid_in_hdx_when_no_currency_is_set() {
})
}

#[test]
fn fee_should_be_paid_in_hdx_when_permit_is_dispatched_and_address_is_bounded() {
TestNet::reset();
let user_evm_address = alith_evm_address();
let user_secret_key = alith_secret_key();
let user_acc = MockAccount::new(alith_evm_account());
let treasury_acc = MockAccount::new(Treasury::account_id());

Hydra::execute_with(|| {
init_omnipool_with_oracle_for_block_10();
pallet_transaction_payment::pallet::NextFeeMultiplier::<hydradx_runtime::Runtime>::put(
hydradx_runtime::MinimumMultiplier::get(),
);

// Prepare user evm account - bind and fund
assert_ok!(EVMAccounts::bind_evm_address(hydradx_runtime::RuntimeOrigin::signed(
user_acc.address()
)));
assert_ok!(hydradx_runtime::Currencies::update_balance(
hydradx_runtime::RuntimeOrigin::root(),
user_acc.address(),
HDX,
100_000_000_000_000i128,
));
//Fund some DOT to sell in omnipool
assert_ok!(hydradx_runtime::Currencies::update_balance(
hydradx_runtime::RuntimeOrigin::root(),
user_acc.address(),
DOT,
100_000_000i128,
));

let initial_treasury_hdx_balance = treasury_acc.balance(HDX);
let initial_user_hdx_balance = user_acc.balance(HDX);
let initial_user_weth_balance = user_acc.balance(WETH);
let initial_user_dot_balance = user_acc.balance(DOT);

// just reset the weth balance to 0 - to make sure we dont have enough WETH
assert_ok!(hydradx_runtime::Currencies::update_balance(
hydradx_runtime::RuntimeOrigin::root(),
user_acc.address(),
WETH,
-(initial_user_weth_balance as i128),
));
let initial_user_weth_balance = user_acc.balance(WETH);
assert_eq!(initial_user_weth_balance, 0);

let omni_sell =
hydradx_runtime::RuntimeCall::Omnipool(pallet_omnipool::Call::<hydradx_runtime::Runtime>::sell {
asset_in: DOT,
asset_out: WETH,
amount: 10_000_000,
min_buy_amount: 0,
});

let gas_limit = 1000000;
let deadline = U256::from(1000000000000u128);

let permit =
pallet_evm_precompile_call_permit::CallPermitPrecompile::<hydradx_runtime::Runtime>::generate_permit(
CALLPERMIT,
user_evm_address,
DISPATCH_ADDR,
U256::from(0),
omni_sell.encode(),
gas_limit,
U256::zero(),
deadline,
);
let secret_key = SecretKey::parse(&user_secret_key).unwrap();
let message = Message::parse(&permit);
let (rs, v) = sign(&message, &secret_key);

//Execute omnipool via EVM
assert_ok!(MultiTransactionPayment::dispatch_permit(
hydradx_runtime::RuntimeOrigin::none(),
user_evm_address,
DISPATCH_ADDR,
U256::from(0),
omni_sell.encode(),
gas_limit,
deadline,
v.serialize(),
H256::from(rs.r.b32()),
H256::from(rs.s.b32()),
));
// Verify evm fee amount
let user_hdx_balance = user_acc.balance(HDX);
let fee_amount = initial_user_hdx_balance - user_hdx_balance;
assert!(fee_amount > 0);
let new_treasury_hdx_balance = treasury_acc.balance(HDX);
let treasury_hdx_diff = new_treasury_hdx_balance - initial_treasury_hdx_balance;
assert_eq!(fee_amount, treasury_hdx_diff);

// Verify omnipool sell
let user_weth_balance = user_acc.balance(WETH);
assert_eq!(user_weth_balance, 3_565_408_466_680);
let user_dot_balance = user_acc.balance(DOT);
assert!(user_dot_balance < initial_user_dot_balance);
let dot_diff = initial_user_dot_balance - user_dot_balance;
assert_eq!(dot_diff, 10_000_000);
})
}

#[test]
fn fee_should_be_paid_in_hdx_when_permit_is_dispatched_and_address_is_not_bounded() {
TestNet::reset();
Expand Down
21 changes: 18 additions & 3 deletions pallets/evm-accounts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ pub mod pallet {
#[pallet::error]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub enum Error<T> {
/// EVM Account's nonce is not zero
/// Active EVM account cannot be bound
TruncatedAccountAlreadyUsed,
/// Address is already bound
AddressAlreadyBound,
Expand Down Expand Up @@ -201,6 +201,10 @@ pub mod pallet {
Error::<T>::AddressAlreadyBound
);

ensure!(
!Self::is_evm_account(who.clone()),
Error::<T>::TruncatedAccountAlreadyUsed
);
let nonce = T::EvmNonceProvider::get_nonce(evm_address);
ensure!(nonce.is_zero(), Error::<T>::TruncatedAccountAlreadyUsed);

Expand Down Expand Up @@ -311,20 +315,31 @@ pub mod pallet {
}
}

impl<T: Config> Pallet<T> {
fn _is_evm_account(account_id: &[u8; 32]) -> bool {
let account_ref = account_id.as_ref();
Roznovjak marked this conversation as resolved.
Show resolved Hide resolved
&account_ref[0..4] == b"ETH\0" && account_ref[24..32] == [0u8; 8]
}
}

impl<T: Config> InspectEvmAccounts<T::AccountId> for Pallet<T>
where
T::AccountId: AsRef<[u8; 32]> + frame_support::traits::IsType<AccountId32>,
{
/// Returns `True` if the account is EVM truncated account.
fn is_evm_account(account_id: T::AccountId) -> bool {
let account_ref = account_id.as_ref();
&account_ref[0..4] == b"ETH\0" && account_ref[24..32] == [0u8; 8]
Self::_is_evm_account(account_ref)
}

/// Get the EVM address from the substrate address.
fn evm_address(account_id: &impl AsRef<[u8; 32]>) -> EvmAddress {
let acc = account_id.as_ref();
EvmAddress::from_slice(&acc[..20])
if Self::_is_evm_account(&acc) {
EvmAddress::from_slice(&acc[4..24])
} else {
EvmAddress::from_slice(&acc[..20])
}
}

/// Get the truncated address from the EVM address.
Expand Down
53 changes: 53 additions & 0 deletions pallets/evm-accounts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,47 @@ fn eth_address_should_convert_to_full_address_when_bound() {
});
}

#[test]
fn evm_address_is_reversible_from_account_id() {
ExtBuilder::default().build().execute_with(|| {
let evm_address = H160::from(hex!["222222ff7Be76052e023Ec1a306fCca8F9659D80"]);
assert_eq!(
EVMAccounts::evm_address(&EVMAccounts::account_id(evm_address)),
evm_address
);
});
}

#[test]
fn account_id_is_reversible_from_evm_address() {
ExtBuilder::default().build().execute_with(|| {
let evm_address = H160::from(hex!["222222ff7Be76052e023Ec1a306fCca8F9659D80"]);
assert_eq!(
EVMAccounts::account_id(EVMAccounts::evm_address(&EVMAccounts::account_id(evm_address))),
EVMAccounts::account_id(evm_address)
);
});
}

#[test]
fn account_id_is_reversible_from_bound_evm_address() {
ExtBuilder::default().build().execute_with(|| {
assert_ok!(EVMAccounts::bind_evm_address(RuntimeOrigin::signed(ALICE)));
assert_eq!(EVMAccounts::account_id(EVMAccounts::evm_address(&ALICE)), ALICE);
});
}

#[test]
fn bound_evm_address_is_reversible_from_account_id() {
ExtBuilder::default().build().execute_with(|| {
assert_ok!(EVMAccounts::bind_evm_address(RuntimeOrigin::signed(ALICE)));
assert_eq!(
EVMAccounts::evm_address(&EVMAccounts::account_id(EVMAccounts::evm_address(&ALICE))),
EVMAccounts::evm_address(&ALICE)
);
});
}

#[test]
fn bind_address_should_fail_when_nonce_is_not_zero() {
ExtBuilder::default()
Expand All @@ -71,6 +112,18 @@ fn bind_address_should_fail_when_nonce_is_not_zero() {
});
}

#[test]
fn bind_address_should_fail_when_binding_evm_truncated_account() {
ExtBuilder::default().build().execute_with(|| {
let evm_address = H160::from(hex!["222222ff7Be76052e023Ec1a306fCca8F9659D80"]);
let account_id = EVMAccounts::account_id(evm_address);
assert_noop!(
EVMAccounts::bind_evm_address(RuntimeOrigin::signed(account_id)),
Error::<Test>::TruncatedAccountAlreadyUsed
);
});
}

#[test]
fn bind_address_should_fail_when_already_bound() {
ExtBuilder::default().build().execute_with(|| {
Expand Down
Loading