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

Feat: allow withdraw by users directly #25

Merged
merged 3 commits into from
Apr 5, 2023
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
69 changes: 58 additions & 11 deletions eth-connector-tests/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ async fn test_withdraw_eth_from_near() -> anyhow::Result<()> {
let res = contract
.contract
.call("withdraw")
.args_borsh((contract.contract.id(), recipient_addr, withdraw_amount))
.args_borsh((recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
.transact()
Expand Down Expand Up @@ -130,10 +130,57 @@ async fn test_withdraw_eth_from_near_user() -> anyhow::Result<()> {

let withdraw_amount = 100;
let recipient_addr = validate_eth_address(RECIPIENT_ETH_ADDRESS);
contract.set_and_check_access_right(user_acc.id()).await?;

let res = user_acc
.call(contract.contract.id(), "withdraw")
.args_borsh((recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
.transact()
.await?;
assert!(res.is_success());

let data: WithdrawResult = res.borsh()?;
let custodian_addr = validate_eth_address(CUSTODIAN_ADDRESS);
assert_eq!(data.recipient_id, recipient_addr);
assert_eq!(data.amount, withdraw_amount);
assert_eq!(data.eth_custodian_address, custodian_addr);
assert_eq!(
contract.get_eth_on_near_balance(user_acc.id()).await?.0,
DEPOSITED_AMOUNT - withdraw_amount
);
assert_eq!(
contract.total_supply().await?.0,
DEPOSITED_AMOUNT - withdraw_amount
);
Ok(())
}

#[tokio::test]
async fn test_withdraw_eth_from_near_engine() -> anyhow::Result<()> {
let contract = TestContract::new().await?;
contract.call_deposit_eth_to_near().await?;
let user_acc = contract.create_sub_account("eth_recipient").await?;

let withdraw_amount = 100;
let recipient_addr = validate_eth_address(RECIPIENT_ETH_ADDRESS);

// Only approved accounts can call this function
let res = user_acc
.call(contract.contract.id(), "engine_withdraw")
.args_borsh((user_acc.id(), recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
.transact()
.await?;
assert!(res.is_failure());
assert!(contract.check_error_message(res, "ERR_ACCESS_RIGHT"));

// The purpose of this withdraw variant is that it can withdraw on behalf of a user.
// In this example the contract itself withdraws on behalf of the user
let res = contract
.contract
.call("engine_withdraw")
.args_borsh((user_acc.id(), recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
Expand Down Expand Up @@ -638,7 +685,7 @@ async fn test_admin_controlled_admin_can_perform_actions_when_paused() -> anyhow
// 1st withdraw call when unpaused - should succeed
let res = contract
.contract
.call("withdraw")
.call("engine_withdraw")
.args_borsh((&sender_id, recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
Expand Down Expand Up @@ -684,7 +731,7 @@ async fn test_admin_controlled_admin_can_perform_actions_when_paused() -> anyhow
// 2nd withdraw call when paused, but the admin is calling it - should succeed
let res = contract
.contract
.call("withdraw")
.call("engine_withdraw")
.args_borsh((&sender_id, recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
Expand All @@ -699,7 +746,7 @@ async fn test_admin_controlled_admin_can_perform_actions_when_paused() -> anyhow
assert_eq!(data.eth_custodian_address, custodian_addr);

let res = user_acc
.call(contract.contract.id(), "withdraw")
.call(contract.contract.id(), "engine_withdraw")
.args_borsh((&sender_id, recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
Expand All @@ -710,7 +757,7 @@ async fn test_admin_controlled_admin_can_perform_actions_when_paused() -> anyhow
assert!(contract.check_error_message(res, "ERR_ACCESS_RIGHT"));

let res = owner_acc
.call(contract.contract.id(), "withdraw")
.call(contract.contract.id(), "engine_withdraw")
.args_borsh((&sender_id, recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
Expand Down Expand Up @@ -799,7 +846,7 @@ async fn test_withdraw_from_near_pausability() -> anyhow::Result<()> {
// 1st withdraw - should succeed
let res = user_acc
.call(contract.contract.id(), "withdraw")
.args_borsh((contract.contract.id(), recipient_addr, withdraw_amount))
.args_borsh((recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
.transact()
Expand All @@ -825,7 +872,7 @@ async fn test_withdraw_from_near_pausability() -> anyhow::Result<()> {
// 2nd withdraw - should fail
let res = user_acc
.call(contract.contract.id(), "withdraw")
.args_borsh((contract.contract.id(), recipient_addr, withdraw_amount))
.args_borsh((recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
.transact()
Expand All @@ -845,7 +892,7 @@ async fn test_withdraw_from_near_pausability() -> anyhow::Result<()> {

let res = user_acc
.call(contract.contract.id(), "withdraw")
.args_borsh((contract.contract.id(), recipient_addr, withdraw_amount))
.args_borsh((recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
.transact()
Expand Down Expand Up @@ -1175,7 +1222,7 @@ async fn test_access_rights() -> anyhow::Result<()> {
let withdraw_amount = 100;
let recipient_addr = validate_eth_address(RECIPIENT_ETH_ADDRESS);
let res = user_acc
.call(contract.contract.id(), "withdraw")
.call(contract.contract.id(), "engine_withdraw")
.args_borsh((contract.contract.id(), recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
Expand All @@ -1201,7 +1248,7 @@ async fn test_access_rights() -> anyhow::Result<()> {

let res = contract
.contract
.call("withdraw")
.call("engine_withdraw")
.args_borsh((contract.contract.id(), recipient_addr, withdraw_amount))
.gas(DEFAULT_GAS)
.deposit(ONE_YOCTO)
Expand Down
13 changes: 12 additions & 1 deletion eth-connector/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ pub trait ConnectorWithdraw {
#[result_serializer(borsh)]
fn withdraw(
&mut self,
#[serializer(borsh)] sender_id: AccountId,
#[serializer(borsh)] recipient_address: Address,
#[serializer(borsh)] amount: Balance,
) -> WithdrawResult;
Expand Down Expand Up @@ -46,6 +45,18 @@ pub trait FungibleTokeStatistic {
fn get_accounts_counter(&self) -> U64;
}

/// Withdraw method for legacy implementation in Engine
#[ext_contract(ext_engine_withdraw)]
pub trait EngineConnectorWithdraw {
#[result_serializer(borsh)]
fn engine_withdraw(
&mut self,
#[serializer(borsh)] sender_id: AccountId,
#[serializer(borsh)] recipient_address: Address,
#[serializer(borsh)] amount: Balance,
) -> WithdrawResult;
}

/// Engin compatible methods for NEP-141
#[ext_contract(ext_enine_ft)]
pub trait EngineFungibleToken {
Expand Down
32 changes: 30 additions & 2 deletions eth-connector/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::admin_controlled::{AdminControlled, PausedMask, PAUSE_WITHDRAW, UNPAUSE_ALL};
use crate::connector::{
ConnectorDeposit, ConnectorFundsFinish, ConnectorWithdraw, EngineFungibleToken,
EngineStorageManagement, FungibleTokeStatistic, KnownEngineAccountsManagement,
ConnectorDeposit, ConnectorFundsFinish, ConnectorWithdraw, EngineConnectorWithdraw,
EngineFungibleToken, EngineStorageManagement, FungibleTokeStatistic,
KnownEngineAccountsManagement,
};
use crate::connector_impl::{
EthConnector, FinishDepositCallArgs, TransferCallCallArgs, WithdrawResult,
Expand Down Expand Up @@ -476,6 +477,33 @@ impl ConnectorWithdraw for EthConnectorContract {
#[payable]
#[result_serializer(borsh)]
fn withdraw(
&mut self,
#[serializer(borsh)] recipient_address: Address,
#[serializer(borsh)] amount: Balance,
) -> WithdrawResult {
assert_one_yocto();

// Check is current flow paused. If it's owner just skip assertion.
self.assert_not_paused(PAUSE_WITHDRAW)
.map_err(|_| "WithdrawErrorPaused")
.sdk_unwrap();

let sender_id = env::predecessor_account_id();
// Burn tokens to recipient
self.ft.internal_withdraw(&sender_id, amount);
WithdrawResult {
recipient_id: recipient_address,
amount,
eth_custodian_address: self.connector.eth_custodian_address,
}
}
}

#[near_bindgen]
impl EngineConnectorWithdraw for EthConnectorContract {
#[payable]
#[result_serializer(borsh)]
fn engine_withdraw(
&mut self,
#[serializer(borsh)] sender_id: AccountId,
#[serializer(borsh)] recipient_address: Address,
Expand Down