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
6 changes: 6 additions & 0 deletions src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::prelude::*;
use crate::storage::{self, EthConnectorStorageId, KeyPrefix};
use borsh::{BorshDeserialize, BorshSerialize};

pub(crate) const ERC20_ADMIN_PREFIX: &str = "erc20.";
pub(crate) const ERR_NOT_ENOUGH_BALANCE_FOR_FEE: &str = "ERR_NOT_ENOUGH_BALANCE_FOR_FEE";
pub const NO_DEPOSIT: Balance = 0;
const GAS_FOR_FINISH_DEPOSIT: Gas = 50_000_000_000_000;
Expand Down Expand Up @@ -632,6 +633,11 @@ impl EthConnectorContract {
}
}

pub(crate) fn erc20_admin_address(base_account_id: &[u8]) -> Address {
let erc20_admin_account_id = [ERC20_ADMIN_PREFIX.as_bytes(), base_account_id].concat();
crate::types::near_account_to_evm_address(erc20_admin_account_id.as_slice())
}

impl AdminControlled for EthConnectorContract {
fn get_paused(&self) -> PausedMask {
self.paused_mask
Expand Down
44 changes: 38 additions & 6 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ use evm::executor::{StackExecutor, StackSubstateMetadata};
use evm::ExitFatal;
use evm::{Config, CreateScheme, ExitError, ExitReason};

use crate::connector::EthConnectorContract;
#[cfg(feature = "contract")]
use crate::contract::current_address;
use crate::connector::{self, EthConnectorContract};
use crate::map::{BijectionMap, LookupMap};
use crate::parameters::{
FunctionCallArgs, NEP141FtOnTransferArgs, NewCallArgs, PromiseCreateArgs, SubmitResult,
TransactionStatus, ViewCallArgs,
};

use crate::precompiles::Precompiles;
use crate::prelude::{is_valid_account_id, Address, TryInto, Vec, H256, U256};
use crate::prelude::{is_valid_account_id, Address, Cow, String, TryInto, Vec, H256, U256};
use crate::sdk;
use crate::state::AuroraStackState;
use crate::storage::{address_to_key, bytes_to_key, storage_to_key, KeyPrefix, KeyPrefixU8};
Expand Down Expand Up @@ -790,15 +788,49 @@ impl Engine {
ethabi::Token::Uint(args.amount.into()),
]);

let erc20_admin_address = connector::erc20_admin_address(&sdk::current_account_id());
unwrap_res_or_finish!(
self.call(
current_address(),
erc20_admin_address,
erc20_token,
Wei::zero(),
[selector, tail.as_slice()].concat(),
u64::MAX,
Vec::new(), // TODO: are there values we should put here?
),
)
.and_then(|submit_result| {
match submit_result.status {
TransactionStatus::Succeed(_) => Ok(()),
TransactionStatus::Revert(bytes) => {
let error_message = crate::prelude::format!(
"Reverted with message: {}",
String::from_utf8_lossy(&bytes)
);
Err(EngineError {
kind: EngineErrorKind::EvmError(ExitError::Other(Cow::from(
error_message,
))),
gas_used: submit_result.gas_used,
})
}
TransactionStatus::OutOfFund => Err(EngineError {
kind: EngineErrorKind::EvmError(ExitError::OutOfFund),
gas_used: submit_result.gas_used,
}),
TransactionStatus::OutOfOffset => Err(EngineError {
kind: EngineErrorKind::EvmError(ExitError::OutOfOffset),
gas_used: submit_result.gas_used,
}),
TransactionStatus::OutOfGas => Err(EngineError {
kind: EngineErrorKind::EvmError(ExitError::OutOfGas),
gas_used: submit_result.gas_used,
}),
TransactionStatus::CallTooDeep => Err(EngineError {
kind: EngineErrorKind::EvmError(ExitError::CallTooDeep),
gas_used: submit_result.gas_used,
}),
}
Comment on lines +802 to +832
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only thing that I can comment on is that this feels weird to me.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this mapping is to turn any failed status into a proper error, which will cause the NEAR transaction to fail. The reason we want to fail the NEAR transaction in this case, but not in the case of submit calls is because this is internal to Aurora. There is no nonce that needs to be incremented or EVM gas that needs to be charged, so we do not need to worry about state being reverted because the transaction fails.

It should also be true that in production none of these failure statuses ever come up (again because its an internal call). But I thought it was still better to catch and properly report them rather than silently swallowing failures, just to be safe.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No no I get the logic behind it. It just feels like we need a From<T> type of implementation given its structure but that isn't exactly possible.

}),
output_on_fail
);

Expand Down
9 changes: 3 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub unsafe fn on_alloc_error(_: core::alloc::Layout) -> ! {
mod contract {
use borsh::{BorshDeserialize, BorshSerialize};

use crate::connector::EthConnectorContract;
use crate::connector::{self, EthConnectorContract};
use crate::engine::{Engine, EngineState, GasPaymentError};
use crate::fungible_token::FungibleTokenMetadata;
#[cfg(feature = "evm_bully")]
Expand Down Expand Up @@ -383,12 +383,13 @@ mod contract {

let mut engine = Engine::new(predecessor_address()).sdk_unwrap();

let erc20_admin_address = connector::erc20_admin_address(&sdk::current_account_id());
let erc20_contract = include_bytes!("../etc/eth-contracts/res/EvmErc20.bin");
let deploy_args = ethabi::encode(&[
ethabi::Token::String("Empty".to_string()),
ethabi::Token::String("EMPTY".to_string()),
ethabi::Token::Uint(ethabi::Uint::from(0)),
ethabi::Token::Address(current_address()),
ethabi::Token::Address(erc20_admin_address),
]);

let address = match Engine::deploy_code_with_input(
Expand Down Expand Up @@ -695,10 +696,6 @@ mod contract {
fn predecessor_address() -> Address {
near_account_to_evm_address(&sdk::predecessor_account_id())
}

pub fn current_address() -> Address {
near_account_to_evm_address(&sdk::current_account_id())
}
}

pub trait AuroraState {
Expand Down
4 changes: 4 additions & 0 deletions src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ pub fn origin() -> AccountId {
"aurora".to_string()
}

pub fn erc20_admin_account() -> AccountId {
[crate::connector::ERC20_ADMIN_PREFIX, &origin()].concat()
}

pub(crate) const SUBMIT: &str = "submit";

pub(crate) mod erc20;
Expand Down
4 changes: 2 additions & 2 deletions src/tests/contract_call.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::test_utils::{origin, AuroraRunner, Signer};
use crate::test_utils::{AuroraRunner, Signer};

use crate::test_utils;
use crate::test_utils::exit_precompile::{Tester, TesterConstructor};
Expand All @@ -23,7 +23,7 @@ fn setup_test() -> (AuroraRunner, Signer, [u8; 20], Tester) {
token,
tester.contract.address.into(),
1_000_000_000,
origin(),
test_utils::erc20_admin_account(),
);

(runner, signer, token, tester)
Expand Down
9 changes: 7 additions & 2 deletions src/tests/erc20_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ fn test_mint() {
let balance = runner.balance_of(token, address, origin());
assert_eq!(balance, U256::from(0));
let amount = 10;
let _result = runner.mint(token, address, amount, origin());
let _result = runner.mint(token, address, amount, test_utils::erc20_admin_account());
let balance = runner.balance_of(token, address, origin());
assert_eq!(balance, U256::from(amount));
}
Expand Down Expand Up @@ -361,7 +361,12 @@ fn test_transfer_erc20_token() {
U256::zero()
);

runner.mint(token, peer0.address, to_mint, origin());
runner.mint(
token,
peer0.address,
to_mint,
test_utils::erc20_admin_account(),
);

assert_eq!(
runner.balance_of(token, peer0.address, origin()),
Expand Down