Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Tarnadas committed Jul 21, 2023
1 parent 2d2161c commit 1f122f5
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 44 deletions.
11 changes: 10 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ members = [
"engine-transactions",
"engine-types",
"engine-workspace",
"etc/xcc-router",
]

exclude = [
Expand All @@ -89,7 +90,6 @@ exclude = [
"etc/tests/self-contained-5bEgfRQ",
"etc/tests/fibonacci",
"etc/tests/modexp-bench",
"etc/xcc-router",
]

[profile.release]
Expand Down
14 changes: 11 additions & 3 deletions engine-precompiles/src/xcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use aurora_engine_types::{
account_id::AccountId,
borsh::{BorshDeserialize, BorshSerialize},
format,
parameters::{CrossContractCallArgs, PromiseCreateArgs},
parameters::{CrossContractCallArgs, PromiseArgsWithSender, PromiseCreateArgs},
types::{balance::ZERO_YOCTO, Address, EthGas, NearGas},
vec, Cow, Vec, H160, H256, U256,
};
Expand Down Expand Up @@ -140,10 +140,14 @@ impl<I: IO> HandleBasedPrecompile for CrossContractCall<I> {
let callback_count = call.promise_count() - 1;
let router_exec_cost = costs::ROUTER_EXEC_BASE
+ NearGas::new(callback_count * costs::ROUTER_EXEC_PER_CALLBACK.as_u64());
let args = PromiseArgsWithSender {
sender: sender.into(),
args: call,
};
let promise = PromiseCreateArgs {
target_account_id,
method: consts::ROUTER_EXEC_NAME.into(),
args: call
args: args
.try_to_vec()
.map_err(|_| ExitError::Other(Cow::from(consts::ERR_SERIALIZE)))?,
attached_balance: ZERO_YOCTO,
Expand All @@ -153,10 +157,14 @@ impl<I: IO> HandleBasedPrecompile for CrossContractCall<I> {
}
CrossContractCallArgs::Delayed(call) => {
let attached_near = call.total_near();
let args = PromiseArgsWithSender {
sender: sender.into(),
args: call,
};
let promise = PromiseCreateArgs {
target_account_id,
method: consts::ROUTER_SCHEDULE_NAME.into(),
args: call
args: args
.try_to_vec()
.map_err(|_| ExitError::Other(Cow::from(consts::ERR_SERIALIZE)))?,
attached_balance: ZERO_YOCTO,
Expand Down
6 changes: 5 additions & 1 deletion engine-tests/src/tests/standalone/call_tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::utils::solidity::erc20::{ERC20Constructor, ERC20};
use crate::utils::{self, standalone, Signer};
use aurora_engine_modexp::AuroraModExp;
use aurora_engine_types::borsh::BorshSerialize;
use aurora_engine_types::parameters::PromiseArgsWithSender;
use aurora_engine_types::{
parameters::{CrossContractCallArgs, PromiseArgs, PromiseCreateArgs},
storage,
Expand Down Expand Up @@ -341,7 +342,10 @@ fn test_trace_precompiles_with_subcalls() {
attached_balance: Yocto::new(1),
attached_gas: NearGas::new(100_000_000_000_000),
};
let xcc_args = CrossContractCallArgs::Delayed(PromiseArgs::Create(promise));
let xcc_args = CrossContractCallArgs::Delayed(PromiseArgsWithSender {
sender: signer_address,
args: PromiseArgs::Create(promise),
});
let tx = aurora_engine_transactions::legacy::TransactionLegacy {
nonce: signer.use_nonce().into(),
gas_price: U256::zero(),
Expand Down
7 changes: 7 additions & 0 deletions engine-types/src/parameters/promise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ use borsh::{maybestd::io, BorshDeserialize, BorshSerialize};
#[cfg(feature = "borsh-compat")]
use borsh_compat::{self as borsh, maybestd::io, BorshDeserialize, BorshSerialize};

#[must_use]
#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct PromiseArgsWithSender {
pub sender: [u8; 20],
pub args: PromiseArgs,
}

#[must_use]
#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub enum PromiseArgs {
Expand Down
3 changes: 3 additions & 0 deletions etc/xcc-router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ panic = "abort"
aurora-engine-types = { path = "../../engine-types", default-features = false, features = ["borsh-compat"] }
near-sdk = "4.1"

[dev-dependencies]
aurora-engine-sdk = { workspace = true, features = ["std"] }

[features]
default = []
all-promise-actions = []
128 changes: 108 additions & 20 deletions etc/xcc-router/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use aurora_engine_types::parameters::{
NearPromise, PromiseAction, PromiseArgs, PromiseCreateArgs, PromiseWithCallbackArgs,
SimpleNearPromise,
NearPromise, PromiseAction, PromiseArgs, PromiseArgsWithSender, PromiseCreateArgs,
PromiseWithCallbackArgs, SimpleNearPromise,
};
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::{LazyOption, LookupMap};
use near_sdk::json_types::{U128, U64};
use near_sdk::serde::{Deserialize, Serialize};
use near_sdk::BorshStorageKey;
use near_sdk::{
env, near_bindgen, AccountId, Gas, PanicOnDefault, Promise, PromiseIndex, PromiseResult,
Expand All @@ -22,6 +23,29 @@ enum StorageKey {
Map,
}

#[derive(Deserialize, Serialize)]
#[serde(crate = "near_sdk::serde")]
struct FtTransferCallArgs {
pub receiver_id: AccountId,
pub amount: U128,
pub memo: Option<String>,
pub msg: String,
}

#[derive(Deserialize, Serialize)]
#[serde(crate = "near_sdk::serde")]
struct FtResolveTransferCallArgs {
pub sender: [u8; 20],
pub token_id: AccountId,
pub amount: U128,
}

// #[derive(Serialize)]
// #[serde(crate = "near_sdk::serde")]
// struct FtResolveTransferCallArgs {
// pub amount: U128,
// }

const CURRENT_VERSION: u32 = 1;

const ERR_ILLEGAL_CALLER: &str = "ERR_ILLEGAL_CALLER";
Expand All @@ -34,6 +58,8 @@ const WNEAR_WITHDRAW_GAS: Gas = Gas(5_000_000_000_000);
const WNEAR_REGISTER_GAS: Gas = Gas(5_000_000_000_000);
/// Gas cost estimated from simulation tests.
const REFUND_GAS: Gas = Gas(5_000_000_000_000);
/// Gas cost estimated from simulation tests.
const FT_RESOLVE_TRANSFER_CALL_GAS: Gas = Gas(15_000_000_000_000);
/// Registration amount computed from FT token source code, see
/// https://github.com/near/near-sdk-rs/blob/master/near-contract-standards/src/fungible_token/core_impl.rs#L50
/// https://github.com/near/near-sdk-rs/blob/master/near-contract-standards/src/fungible_token/storage_impl.rs#L101
Expand All @@ -52,7 +78,7 @@ pub struct Router {
/// This allows multiple promises to be scheduled before any of them are executed.
nonce: LazyOption<u64>,
/// The storage for the scheduled promises.
scheduled_promises: LookupMap<u64, PromiseArgs>,
scheduled_promises: LookupMap<u64, PromiseArgsWithSender>,
/// Account ID for the wNEAR contract.
wnear_account: AccountId,
}
Expand Down Expand Up @@ -114,15 +140,15 @@ impl Router {
/// The engine only calls this function when the special precompile in the EVM for NEAR cross
/// contract calls is used by the address associated with the sub-account this router contract
/// is deployed at.
pub fn execute(&self, #[serializer(borsh)] promise: PromiseArgs) {
pub fn execute(&self, #[serializer(borsh)] promise: PromiseArgsWithSender) {
self.require_parent_caller();

let promise_id = Router::promise_create(promise);
env::promise_return(promise_id)
}

/// Similar security considerations here as for `execute`.
pub fn schedule(&mut self, #[serializer(borsh)] promise: PromiseArgs) {
pub fn schedule(&mut self, #[serializer(borsh)] promise: PromiseArgsWithSender) {
self.require_parent_caller();

let nonce = self.nonce.get().unwrap_or_default();
Expand Down Expand Up @@ -186,6 +212,37 @@ impl Router {

Promise::new(parent).transfer(REFUND_AMOUNT)
}

#[private]
pub fn ft_resolve_transfer_call(&self, sender: [u8; 20], token_id: AccountId, amount: U128) {
let used_amount = match env::promise_result(0) {
PromiseResult::Successful(value) => {
near_sdk::serde_json::from_slice::<U128>(&value).unwrap()
}
PromiseResult::Failed => 0.into(),
PromiseResult::NotReady => panic!(),
};
let unused_amount = U128(amount.0 - used_amount.0);
near_sdk::log!("amount {}", amount.0);
near_sdk::log!("used_amount {}", used_amount.0);
near_sdk::log!("unused_amount {}", unused_amount.0);
if unused_amount.0 > 0 {
let promise_idx = env::promise_create(
token_id,
"ft_transfer_call",
&near_sdk::serde_json::to_vec(&FtTransferCallArgs {
receiver_id: self.parent.get().unwrap(),
amount: unused_amount,
msg: sender.map(|c| format!("{:02x?}", c)).concat(),
memo: Some("refund".to_string()),
})
.unwrap(),
1u128,
WNEAR_REGISTER_GAS,
);
env::promise_return(promise_idx)
}
}
}

impl Router {
Expand All @@ -208,16 +265,18 @@ impl Router {
}
}

fn promise_create(promise: PromiseArgs) -> PromiseIndex {
match promise {
PromiseArgs::Create(call) => Self::base_promise_create(&call),
PromiseArgs::Callback(cb) => Self::cb_promise_create(&cb),
PromiseArgs::Recursive(p) => Self::recursive_promise_create(&p),
fn promise_create(promise: PromiseArgsWithSender) -> PromiseIndex {
near_sdk::log!("PROMISE {:?}", &promise);
near_sdk::log!("prepaid_gas {}", near_sdk::env::prepaid_gas().0);
match promise.args {
PromiseArgs::Create(call) => Self::base_promise_create(promise.sender, &call),
PromiseArgs::Callback(cb) => Self::cb_promise_create(promise.sender, &cb),
PromiseArgs::Recursive(p) => Self::recursive_promise_create(promise.sender, &p),
}
}

fn cb_promise_create(promise: &PromiseWithCallbackArgs) -> PromiseIndex {
let base = Self::base_promise_create(&promise.base);
fn cb_promise_create(sender: [u8; 20], promise: &PromiseWithCallbackArgs) -> PromiseIndex {
let base = Self::base_promise_create(sender, &promise.base);
let promise = &promise.callback;

env::promise_then(
Expand All @@ -230,20 +289,49 @@ impl Router {
)
}

fn base_promise_create(promise: &PromiseCreateArgs) -> PromiseIndex {
env::promise_create(
near_sdk::AccountId::new_unchecked(promise.target_account_id.to_string()),
fn base_promise_create(sender: [u8; 20], promise: &PromiseCreateArgs) -> PromiseIndex {
// let gas = if promise.method == "ft_transfer_call" {
// (promise.attached_gas.as_u64() - FT_RESOLVE_TRANSFER_CALL_GAS.0).into()
// } else {
// promise.attached_gas.as_u64().into()
// };

let account_id = near_sdk::AccountId::new_unchecked(promise.target_account_id.to_string());
let mut res = env::promise_create(
account_id.clone(),
promise.method.as_str(),
&promise.args,
promise.attached_balance.as_u128(),
// gas,
promise.attached_gas.as_u64().into(),
)
);

if promise.method == "ft_transfer_call" {
let FtTransferCallArgs { amount, .. } =
near_sdk::serde_json::from_slice::<FtTransferCallArgs>(&promise.args).unwrap();

res = env::promise_then(
res,
env::current_account_id(),
"ft_resolve_transfer_call",
&near_sdk::serde_json::to_vec(&FtResolveTransferCallArgs {
sender,
token_id: account_id,
amount,
})
.unwrap(),
0,
FT_RESOLVE_TRANSFER_CALL_GAS,
);
}

res
}

fn recursive_promise_create(promise: &NearPromise) -> PromiseIndex {
fn recursive_promise_create(sender: [u8; 20], promise: &NearPromise) -> PromiseIndex {
match promise {
NearPromise::Simple(x) => match x {
SimpleNearPromise::Create(call) => Self::base_promise_create(call),
SimpleNearPromise::Create(call) => Self::base_promise_create(sender, call),
SimpleNearPromise::Batch(batch) => {
let target =
near_sdk::AccountId::new_unchecked(batch.target_account_id.to_string());
Expand All @@ -253,7 +341,7 @@ impl Router {
}
},
NearPromise::Then { base, callback } => {
let base_index = Self::recursive_promise_create(base);
let base_index = Self::recursive_promise_create(sender, base);
match callback {
SimpleNearPromise::Create(call) => env::promise_then(
base_index,
Expand All @@ -275,7 +363,7 @@ impl Router {
NearPromise::And(promises) => {
let indices: Vec<PromiseIndex> = promises
.iter()
.map(Self::recursive_promise_create)
.map(|p| Self::recursive_promise_create(sender, p))
.collect();
env::promise_and(&indices)
}
Expand Down
Loading

0 comments on commit 1f122f5

Please sign in to comment.