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 35 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
200 changes: 148 additions & 52 deletions engine-precompiles/src/native.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
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::{parameters::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 +43,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 @@ -242,6 +248,28 @@ fn validate_amount(amount: U256) -> Result<(), ExitError> {
Ok(())
}

#[derive(Debug, PartialEq)]
struct Recipient<'a> {
receiver_account_id: AccountId,
message: Option<&'a str>,
}

fn parse_recipient(recipient: &[u8]) -> Result<Recipient<'_>, ExitError> {
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)),
);

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 @@ -300,10 +328,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 @@ -326,6 +352,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 @@ -355,29 +383,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
aleksuss marked this conversation as resolved.
Show resolved Hide resolved
== 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 @@ -394,30 +439,37 @@ 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 callback_args = ExitToNearPrecompileCallbackCallArgs {
#[cfg(feature = "error_refund")]
refund: Some(refund_args),
#[cfg(not(feature = "error_refund"))]
refund: None,
transfer_near: transfer_near_args,
};

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 callback_args == ExitToNearPrecompileCallbackCallArgs::default() {
PromiseArgs::Create(transfer_promise)
} else {
PromiseArgs::Callback(PromiseWithCallbackArgs {
base: transfer_promise,
callback: PromiseCreateArgs {
target_account_id: self.current_account_id.clone(),
method: "exit_to_near_precompile_callback".to_string(),
args: callback_args.try_to_vec().unwrap(),
attached_balance: Yocto::new(0),
attached_gas: costs::EXIT_TO_NEAR_CALLBACK_GAS,
},
})
};
let promise_log = Log {
address: exit_to_near::ADDRESS.raw(),
topics: Vec::new(),
Expand Down Expand Up @@ -620,8 +672,10 @@ impl<I: IO> Precompile for ExitToEthereum<I> {

#[cfg(test)]
mod tests {
use super::{exit_to_ethereum, exit_to_near, validate_amount, validate_input_size};
use crate::prelude::sdk::types::near_account_to_evm_address;
use super::{
exit_to_ethereum, exit_to_near, parse_recipient, validate_amount, validate_input_size,
};
use crate::{native::Recipient, prelude::sdk::types::near_account_to_evm_address};
use aurora_engine_types::U256;

#[test]
Expand Down Expand Up @@ -687,4 +741,46 @@ mod tests {
fn test_exit_with_valid_amount() {
validate_amount(U256::from(u128::MAX)).unwrap();
}

#[test]
fn test_parse_recipient() {
assert_eq!(
parse_recipient(b"test.near").unwrap(),
Recipient {
receiver_account_id: "test.near".parse().unwrap(),
message: None
}
);

assert_eq!(
parse_recipient(b"test.near:unwrap").unwrap(),
Recipient {
receiver_account_id: "test.near".parse().unwrap(),
message: Some("unwrap")
}
);

assert_eq!(
parse_recipient(b"test.near:some_msg:with_extra_colon").unwrap(),
Recipient {
receiver_account_id: "test.near".parse().unwrap(),
message: Some("some_msg:with_extra_colon")
}
);

assert_eq!(
parse_recipient(b"test.near:").unwrap(),
Recipient {
receiver_account_id: "test.near".parse().unwrap(),
message: Some("")
vimpunk marked this conversation as resolved.
Show resolved Hide resolved
}
);
}

#[test]
fn test_parse_invalid_recipient() {
assert!(parse_recipient(b"[email protected]").is_err());
assert!(parse_recipient(b"[email protected]:msg").is_err());
assert!(parse_recipient(&[0xc2]).is_err());
}
}
16 changes: 10 additions & 6 deletions engine-standalone-storage/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,12 @@ pub fn parse_transaction_kind(
})?;
TransactionKind::RegisterRelayer(address)
}
TransactionKindTag::RefundOnError => match promise_data.first().and_then(Option::as_ref) {
None => TransactionKind::RefundOnError(None),
TransactionKindTag::ExitToNear => match promise_data.first().and_then(Option::as_ref) {
None => TransactionKind::ExitToNear(None),
Some(_) => {
let args = aurora_engine_types::parameters::RefundCallArgs::try_from_slice(&bytes)
let args = aurora_engine_types::parameters::ExitToNearPrecompileCallbackCallArgs::try_from_slice(&bytes)
.map_err(f)?;
TransactionKind::RefundOnError(Some(args))
TransactionKind::ExitToNear(Some(args))
}
},
TransactionKindTag::SetConnectorData => {
Expand Down Expand Up @@ -515,9 +515,13 @@ fn non_submit_execute<I: IO + Copy>(
None
}

TransactionKind::RefundOnError(_) => {
TransactionKind::ExitToNear(_) => {
let mut handler = crate::promise::NoScheduler { promise_data };
let maybe_result = contract_methods::connector::refund_on_error(io, env, &mut handler)?;
let maybe_result = contract_methods::connector::exit_to_near_precompile_callback(
io,
env,
&mut handler,
)?;

maybe_result.map(|submit_result| TransactionExecutionResult::Submit(Ok(submit_result)))
}
Expand Down
Loading
Loading