Skip to content

Commit

Permalink
Feat: allow withdraw by users directly (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
birchmd authored Apr 5, 2023
1 parent e6cec80 commit 23ca014
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 14 deletions.
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

0 comments on commit 23ca014

Please sign in to comment.