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

Unwrap NEAR on Aurora->NEAR transfers #750

Merged
merged 36 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7bdc2df
Eth-connector: unwrap wnear tokens
karim-en May 4, 2023
de3daa4
Fix clippy errors
karim-en May 4, 2023
bac4a66
Fix tests
karim-en May 5, 2023
311464c
Return transfer near promise
karim-en May 5, 2023
d0e93bb
Add test to cover exit `wnear` token
karim-en May 5, 2023
79cedbb
Fix clippy errors
karim-en May 5, 2023
1a6b4f2
Use `unwrap` suffix to unwrap wnear
karim-en May 6, 2023
2cf0dbf
Fix clippy
karim-en May 6, 2023
81c860d
Merge branch 'develop' into unwrap-wnear
aleksuss May 8, 2023
a7ec557
Merge branch 'develop' into unwrap-wnear
karim-en May 15, 2023
1f0a623
Fix tests
karim-en May 15, 2023
1d63069
Fix clippy
karim-en May 15, 2023
01e96ec
Improve comparison
karim-en May 19, 2023
6ebfc12
Improve `parse_recipient`
karim-en May 19, 2023
6e29438
Remove extra `to_string`
karim-en May 19, 2023
66e9468
Add unit tests for `parse_recipient`
karim-en May 19, 2023
6a9f820
Fix clippy
karim-en May 19, 2023
b6375f2
Fix build with `error_refund` feature
karim-en May 29, 2023
a615fcb
Improve performance by avoiding `to_string`
karim-en May 29, 2023
22b626f
Fix: include more gas for the withdraw_to_near call used in XCC
birchmd May 29, 2023
cba5fac
Merge branch 'develop' into unwrap-wnear
aleksuss May 30, 2023
b78c19f
Merge branch 'develop' into unwrap-wnear
aleksuss Jun 1, 2023
89bd407
chore: buffered reader
aleksuss Jun 1, 2023
870f97a
Improve code readability
karim-en Jun 14, 2023
321c668
Merge branch 'develop' into unwrap-wnear
karim-en Jun 14, 2023
2e99150
Fix clippy
karim-en Jun 14, 2023
f3d097a
Merge branch 'develop' into unwrap-wnear
karim-en Jun 22, 2023
562fe82
Merge branch 'develop' into unwrap-wnear
karim-en Jul 10, 2023
1c5125d
Merge branch 'develop' into unwrap-wnear
karim-en Jul 14, 2023
c9df247
Standalone: commit storage
karim-en Jul 14, 2023
b527bd2
Merge branch 'develop' into unwrap-wnear
karim-en Jul 20, 2023
f7722b5
Merge branch 'develop' into unwrap-wnear
karim-en Jul 31, 2023
55d23de
Merge branch 'develop' into unwrap-wnear
karim-en Aug 14, 2023
223b6e1
Merge branch 'develop' into unwrap-wnear
aleksuss Sep 22, 2023
31b8c1f
chore: rollback gas costs in uniswap test
aleksuss Sep 22, 2023
fe377ca
fix: fix naming
aleksuss Sep 22, 2023
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
155 changes: 108 additions & 47 deletions engine-precompiles/src/native.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
use super::{EvmPrecompileResult, Precompile};
use crate::prelude::{
format,
parameters::{PromiseArgs, PromiseCreateArgs, WithdrawCallArgs},
sdk::io::{StorageIntermediate, IO},
storage::{bytes_to_key, KeyPrefix},
types::{Address, Yocto},
vec, BorshSerialize, Cow, String, ToString, Vec, U256,
};
#[cfg(feature = "error_refund")]
use crate::prelude::{
parameters::{PromiseWithCallbackArgs, RefundCallArgs},
types,
};
use crate::{
prelude::{
format,
parameters::{PromiseArgs, PromiseCreateArgs, WithdrawCallArgs},
sdk::io::{StorageIntermediate, IO},
storage::{bytes_to_key, KeyPrefix},
str,
types::{Address, Yocto},
vec, BorshSerialize, Cow, String, ToString, Vec, U256,
},
xcc::state::get_wnear_address,
};

use crate::prelude::types::EthGas;
use crate::PrecompileOutput;
use aurora_engine_types::{account_id::AccountId, types::NEP141Wei};
use aurora_engine_types::{
account_id::AccountId,
parameters::{
ExitToNearPrecompileCallbackCallArgs, PromiseWithCallbackArgs, TransferNearCallArgs,
},
types::NEP141Wei,
};
use evm::backend::Log;
use evm::{Context, ExitError};

const ERR_TARGET_TOKEN_NOT_FOUND: &str = "Target token not found";
const UNWRAP_WNEAR_MSG: &str = "unwrap";

mod costs {
use crate::prelude::types::{EthGas, NearGas};
Expand All @@ -35,9 +46,7 @@ mod costs {
pub(super) const FT_TRANSFER_GAS: NearGas = NearGas::new(10_000_000_000_000);

/// Value determined experimentally based on tests.
/// (No mainnet data available since this feature is not enabled)
#[cfg(feature = "error_refund")]
pub(super) const REFUND_ON_ERROR_GAS: NearGas = NearGas::new(5_000_000_000_000);
pub(super) const EXIT_TO_NEAR_CALLBACK_GAS: NearGas = NearGas::new(10_000_000_000_000);

// TODO(#332): Determine the correct amount of gas
pub(super) const WITHDRAWAL_GAS: NearGas = NearGas::new(100_000_000_000_000);
Expand Down Expand Up @@ -243,6 +252,27 @@ fn validate_amount(amount: U256) -> Result<(), ExitError> {
Ok(())
}

struct Recipient {
receiver_account_id: AccountId,
message: Option<String>,
}

fn parse_recipient(recipient: &[u8]) -> Result<Recipient, ExitError> {
aleksuss marked this conversation as resolved.
Show resolved Hide resolved
let recipient = str::from_utf8(recipient)
.map_err(|_| ExitError::Other(Cow::from("ERR_INVALID_RECEIVER_ACCOUNT_ID")))?;
let (receiver_account_id, message) = recipient.split_once(':').map_or_else(
|| (recipient, None),
|(recipient, msg)| (recipient, Some(msg.to_string())),
birchmd marked this conversation as resolved.
Show resolved Hide resolved
);

Ok(Recipient {
receiver_account_id: receiver_account_id
.parse()
.map_err(|_| ExitError::Other(Cow::from("ERR_INVALID_RECEIVER_ACCOUNT_ID")))?,
message,
})
}

aleksuss marked this conversation as resolved.
Show resolved Hide resolved
impl<I: IO> Precompile for ExitToNear<I> {
fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
Ok(costs::EXIT_TO_NEAR_GAS)
Expand Down Expand Up @@ -301,10 +331,8 @@ impl<I: IO> Precompile for ExitToNear<I> {
#[cfg(not(feature = "error_refund"))]
let mut input = parse_input(input)?;
let current_account_id = self.current_account_id.clone();
#[cfg(feature = "error_refund")]
let refund_on_error_target = current_account_id.clone();

let (nep141_address, args, exit_event) = match flag {
let (nep141_address, args, exit_event, method, transfer_near_args) = match flag {
0x0 => {
// ETH transfer
//
Expand All @@ -327,6 +355,8 @@ impl<I: IO> Precompile for ExitToNear<I> {
dest: dest_account.to_string(),
amount: context.apparent_value,
},
"ft_transfer",
None,
)
} else {
return Err(ExitError::Other(Cow::from(
Expand Down Expand Up @@ -356,29 +386,46 @@ impl<I: IO> Precompile for ExitToNear<I> {
input = &input[32..];

validate_amount(amount)?;
let recipient = parse_recipient(input)?;

if let Ok(receiver_account_id) = AccountId::try_from(input) {
let (args, method, transfer_near_args) = if recipient.message.as_deref()
== Some(UNWRAP_WNEAR_MSG)
&& erc20_address == get_wnear_address(&self.io).raw()
{
(
format!(r#"{{"amount": "{}"}}"#, amount.as_u128()),
"near_withdraw",
Some(TransferNearCallArgs {
target_account_id: recipient.receiver_account_id.clone(),
amount: amount.as_u128(),
}),
)
} else {
// There is no way to inject json, given the encoding of both arguments
// as decimal and valid account id respectively.
(
nep141_address,
// There is no way to inject json, given the encoding of both arguments
// as decimal and valid account id respectively.
format!(
r#"{{"receiver_id": "{}", "amount": "{}", "memo": null}}"#,
receiver_account_id,
recipient.receiver_account_id,
amount.as_u128()
),
events::ExitToNear {
sender: Address::new(erc20_address),
erc20_address: Address::new(erc20_address),
dest: receiver_account_id.to_string(),
amount,
},
"ft_transfer",
None,
)
} else {
return Err(ExitError::Other(Cow::from(
"ERR_INVALID_RECEIVER_ACCOUNT_ID",
)));
}
};

(
nep141_address,
args,
events::ExitToNear {
sender: Address::new(erc20_address),
erc20_address: Address::new(erc20_address),
dest: recipient.receiver_account_id.to_string(),
amount,
},
method,
transfer_near_args,
)
}
_ => return Err(ExitError::Other(Cow::from("ERR_INVALID_FLAG"))),
};
Expand All @@ -395,30 +442,44 @@ impl<I: IO> Precompile for ExitToNear<I> {
erc20_address,
amount: types::u256_to_arr(&exit_event.amount),
};
#[cfg(feature = "error_refund")]
let refund_promise = PromiseCreateArgs {
target_account_id: refund_on_error_target,
method: "refund_on_error".to_string(),
args: refund_args.try_to_vec().unwrap(),
attached_balance: Yocto::new(0),
attached_gas: costs::REFUND_ON_ERROR_GAS,

let precompile_call_args = ExitToNearPrecompileCallbackCallArgs {
#[cfg(feature = "error_refund")]
refund: refund_args,
#[cfg(not(feature = "error_refund"))]
refund: None,
transfer_near: transfer_near_args,
};

let callback_promise =
if precompile_call_args == ExitToNearPrecompileCallbackCallArgs::default() {
vimpunk marked this conversation as resolved.
Show resolved Hide resolved
None
} else {
Some(PromiseCreateArgs {
target_account_id: self.current_account_id.clone(),
method: "exit_to_near_precompile_callback".to_string(),
args: precompile_call_args.try_to_vec().unwrap(),
attached_balance: Yocto::new(0),
attached_gas: costs::EXIT_TO_NEAR_CALLBACK_GAS,
})
};

let transfer_promise = PromiseCreateArgs {
target_account_id: nep141_address,
method: "ft_transfer".to_string(),
method: method.to_string(),
args: args.as_bytes().to_vec(),
attached_balance: Yocto::new(1),
attached_gas: costs::FT_TRANSFER_GAS,
};

#[cfg(feature = "error_refund")]
karim-en marked this conversation as resolved.
Show resolved Hide resolved
let promise = PromiseArgs::Callback(PromiseWithCallbackArgs {
base: transfer_promise,
callback: refund_promise,
});
#[cfg(not(feature = "error_refund"))]
let promise = PromiseArgs::Create(transfer_promise);

let promise = if let Some(callback) = callback_promise {
PromiseArgs::Callback(PromiseWithCallbackArgs {
base: transfer_promise,
callback,
})
} else {
PromiseArgs::Create(transfer_promise)
};
let promise_log = Log {
address: exit_to_near::ADDRESS.raw(),
topics: Vec::new(),
Expand Down
2 changes: 2 additions & 0 deletions engine-tests/src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub const PAUSED_PRECOMPILES: &str = "paused_precompiles";
pub const RESUME_PRECOMPILES: &str = "resume_precompiles";
pub const SET_OWNER: &str = "set_owner";
pub const SET_UPGRADE_DELAY_BLOCKS: &str = "set_upgrade_delay_blocks";
pub const FACTORY_SET_WNEAR_ADDRESS: &str = "factory_set_wnear_address";

const CALLER_ACCOUNT_ID: &str = "some-account.near";

Expand Down Expand Up @@ -245,6 +246,7 @@ impl AuroraRunner {
|| method_name == RESUME_PRECOMPILES
|| method_name == SET_OWNER
|| method_name == SET_UPGRADE_DELAY_BLOCKS
|| method_name == FACTORY_SET_WNEAR_ADDRESS
{
standalone_runner.submit_raw(method_name, &self.context, &self.promise_results)?;
self.validate_standalone();
Expand Down
18 changes: 18 additions & 0 deletions engine-tests/src/test_utils/standalone/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,24 @@ impl StandaloneRunner {
self.cumulative_diff.append(outcome.diff.clone());
storage::commit(storage, &outcome);

Ok(SubmitResult::new(
TransactionStatus::Succeed(Vec::new()),
0,
Vec::new(),
))
} else if method_name == test_utils::FACTORY_SET_WNEAR_ADDRESS {
let input = &ctx.input[..];
let call_args =
Address::try_from_slice(input).expect("Unable to parse input as Address");
let transaction_hash = aurora_engine_sdk::keccak(&ctx.input);
let mut tx_msg =
Self::template_tx_msg(storage, &env, 0, transaction_hash, promise_results);
tx_msg.transaction = TransactionKind::FactorySetWNearAddress(call_args);

let outcome = sync::execute_transaction_message(storage, tx_msg).unwrap();
self.cumulative_diff.append(outcome.diff.clone());
storage::commit(storage, &outcome);

Ok(SubmitResult::new(
TransactionStatus::Succeed(Vec::new()),
0,
Expand Down
4 changes: 4 additions & 0 deletions engine-tests/src/tests/contract_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ use crate::test_utils::exit_precompile::{Tester, TesterConstructor, DEST_ACCOUNT

fn setup_test() -> (AuroraRunner, Signer, Address, Tester) {
let mut runner = AuroraRunner::new();
let wnear_token_address = runner.deploy_erc20_token("wrap.testnet");
runner
.factory_set_wnear_address(wnear_token_address)
.unwrap();
let token = runner.deploy_erc20_token("tt.testnet");
let mut signer = test_utils::Signer::random();
runner.create_address(
Expand Down
Loading