Skip to content

Commit

Permalink
fix: wait for EVM approval transaction confirmation (#1706)
Browse files Browse the repository at this point in the history
* Wait for the confirmation of the approval transaction for EVM tokens before sending the swap payment

* fix allowed amount check

* fix wait_for_approval_confirmation_until

* provide better error message for approve transaction confirmation fail

* use allowance call instead of waiting for approve transaction confirmation

---------

Reviewed-by: caglaryucekaya <[email protected]>, cipig
  • Loading branch information
shamardy authored Mar 13, 2023
1 parent 944ac4e commit 169964e
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 23 deletions.
94 changes: 74 additions & 20 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2827,6 +2827,9 @@ impl EthCoin {
let swap_contract_address = try_tx_fus!(args.swap_contract_address.try_to_address());
let id = self.etomic_swap_id(args.time_lock, args.secret_hash);
let trade_amount = try_tx_fus!(wei_from_big_decimal(&args.amount, self.decimals));
let watcher_reward = args.watcher_reward.map(U256::from);
let time_lock = U256::from(args.time_lock);
let gas = U256::from(ETH_GAS);

let secret_hash = if args.secret_hash.len() == 32 {
ripemd160(args.secret_hash).to_vec()
Expand All @@ -2836,31 +2839,32 @@ impl EthCoin {

match &self.coin_type {
EthCoinType::Eth => {
let function_name = get_function_name("ethPayment", args.watcher_reward.is_some());
let function_name = get_function_name("ethPayment", watcher_reward.is_some());
let function = try_tx_fus!(SWAP_CONTRACT.function(&function_name));
let mut value = trade_amount;

let data = match args.watcher_reward {
let mut value = trade_amount;
let data = match watcher_reward {
Some(reward) => {
value += U256::from(reward);
value += reward;

try_tx_fus!(function.encode_input(&[
Token::FixedBytes(id),
Token::Address(receiver_addr),
Token::FixedBytes(secret_hash),
Token::Uint(U256::from(args.time_lock)),
Token::Uint(U256::from(reward)),
Token::Uint(time_lock),
Token::Uint(reward),
]))
},
None => try_tx_fus!(function.encode_input(&[
Token::FixedBytes(id),
Token::Address(receiver_addr),
Token::FixedBytes(secret_hash),
Token::Uint(U256::from(args.time_lock)),
Token::Uint(time_lock),
])),
};
drop_mutability!(value);

self.sign_and_send_transaction(value, Action::Call(swap_contract_address), data, U256::from(ETH_GAS))
self.sign_and_send_transaction(value, Action::Call(swap_contract_address), data, gas)
},
EthCoinType::Erc20 {
platform: _,
Expand All @@ -2870,7 +2874,7 @@ impl EthCoin {
.allowance(swap_contract_address)
.map_err(|e| TransactionErr::Plain(ERRL!("{}", e)));

let function_name = get_function_name("erc20Payment", args.watcher_reward.is_some());
let function_name = get_function_name("erc20Payment", watcher_reward.is_some());
let function = try_tx_fus!(SWAP_CONTRACT.function(&function_name));

let data = try_tx_fus!(function.encode_input(&[
Expand All @@ -2879,30 +2883,46 @@ impl EthCoin {
Token::Address(*token_addr),
Token::Address(receiver_addr),
Token::FixedBytes(secret_hash),
Token::Uint(U256::from(args.time_lock))
Token::Uint(time_lock)
]));
let value = U256::from(args.watcher_reward.unwrap_or(0));
let wait_for_required_allowance_until = args.wait_for_confirmation_until;

let arc = self.clone();
Box::new(allowance_fut.and_then(move |allowed| -> EthTxFut {
if allowed < value {
if allowed < trade_amount {
Box::new(
arc.approve(swap_contract_address, U256::max_value())
.and_then(move |_approved| {
arc.sign_and_send_transaction(
value,
Action::Call(swap_contract_address),
data,
U256::from(ETH_GAS),
.and_then(move |approved| {
// make sure the approve tx is confirmed by making sure that the allowed value has been updated
// this call is cheaper than waiting for confirmation calls
arc.wait_for_required_allowance(
swap_contract_address,
trade_amount,
wait_for_required_allowance_until,
)
.map_err(move |e| {
TransactionErr::Plain(ERRL!(
"Allowed value was not updated in time after sending approve transaction {:02x}: {}",
approved.tx_hash(),
e
))
})
.and_then(move |_| {
arc.sign_and_send_transaction(
watcher_reward.unwrap_or_else(|| 0.into()),
Action::Call(swap_contract_address),
data,
gas,
)
})
}),
)
} else {
Box::new(arc.sign_and_send_transaction(
value,
watcher_reward.unwrap_or_else(|| 0.into()),
Action::Call(swap_contract_address),
data,
U256::from(ETH_GAS),
gas,
))
}
}))
Expand Down Expand Up @@ -3499,6 +3519,40 @@ impl EthCoin {
Box::new(fut.boxed().compat())
}

fn wait_for_required_allowance(
&self,
spender: Address,
required_allowance: U256,
wait_until: u64,
) -> Web3RpcFut<()> {
const CHECK_ALLOWANCE_EVERY: f64 = 5.;

let selfi = self.clone();
let fut = async move {
loop {
if now_ms() / 1000 > wait_until {
return MmError::err(Web3RpcError::Internal(ERRL!(
"Waited too long until {} for allowance to be updated to at least {}",
wait_until,
required_allowance
)));
}

match selfi.allowance(spender).compat().await {
Ok(allowed) if allowed >= required_allowance => return Ok(()),
Ok(_allowed) => (),
Err(e) => match e.get_inner() {
Web3RpcError::Transport(e) => error!("Error {} on trying to get the allowed amount!", e),
_ => return Err(e),
},
}

Timer::sleep(CHECK_ALLOWANCE_EVERY).await;
}
};
Box::new(fut.boxed().compat())
}

fn approve(&self, spender: Address, amount: U256) -> EthTxFut {
let coin = self.clone();
let fut = async move {
Expand Down
2 changes: 2 additions & 0 deletions mm2src/coins/eth/eth_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ fn send_and_refund_erc20_payment() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = coin.send_maker_payment(maker_payment_args).wait().unwrap();
log!("{:?}", payment);
Expand Down Expand Up @@ -332,6 +333,7 @@ fn send_and_refund_eth_payment() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = coin.send_maker_payment(send_maker_payment_args).wait().unwrap();

Expand Down
1 change: 1 addition & 0 deletions mm2src/coins/eth/eth_wasm_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ async fn test_send() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let tx = coin.send_maker_payment(maker_payment_args).compat().await;
console::log_1(&format!("{:?}", tx).into());
Expand Down
1 change: 1 addition & 0 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ pub struct SendPaymentArgs<'a> {
pub swap_unique_data: &'a [u8],
pub payment_instructions: &'a Option<PaymentInstructions>,
pub watcher_reward: Option<u64>,
pub wait_for_confirmation_until: u64,
}

#[derive(Clone, Debug)]
Expand Down
3 changes: 3 additions & 0 deletions mm2src/mm2_main/src/lp_swap/maker_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,8 @@ impl MakerSwap {
Ok(res) => match res {
Some(tx) => tx,
None => {
let maker_payment_wait_confirm =
wait_for_maker_payment_conf_until(self.r().data.started_at, self.r().data.lock_duration);
let payment_fut = self.maker_coin.send_maker_payment(SendPaymentArgs {
time_lock_duration: self.r().data.lock_duration,
time_lock: self.r().data.maker_payment_lock as u32,
Expand All @@ -838,6 +840,7 @@ impl MakerSwap {
swap_unique_data: &unique_data,
payment_instructions: &self.r().payment_instructions,
watcher_reward: reward_amount,
wait_for_confirmation_until: maker_payment_wait_confirm,
});

match payment_fut.compat().await {
Expand Down
1 change: 1 addition & 0 deletions mm2src/mm2_main/src/lp_swap/taker_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1453,6 +1453,7 @@ impl TakerSwap {
swap_unique_data: &unique_data,
payment_instructions: &self.r().payment_instructions,
watcher_reward: reward_amount,
wait_for_confirmation_until: self.r().data.taker_payment_lock,
});

match payment_fut.compat().await {
Expand Down
5 changes: 5 additions & 0 deletions mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_taker() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let tx = coin.send_taker_payment(taker_payment_args).wait().unwrap();

Expand Down Expand Up @@ -111,6 +112,7 @@ fn test_search_for_swap_tx_spend_native_was_refunded_maker() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap();

Expand Down Expand Up @@ -170,6 +172,7 @@ fn test_search_for_taker_swap_tx_spend_native_was_spent_by_maker() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let tx = coin.send_taker_payment(taker_payment_args).wait().unwrap();

Expand Down Expand Up @@ -230,6 +233,7 @@ fn test_search_for_maker_swap_tx_spend_native_was_spent_by_taker() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap();

Expand Down Expand Up @@ -293,6 +297,7 @@ fn test_one_hundred_maker_payments_in_a_row_native() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let tx = coin.send_maker_payment(maker_payment_args).wait().unwrap();
if let TransactionEnum::UtxoTx(tx) = tx {
Expand Down
12 changes: 12 additions & 0 deletions mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ fn test_taker_spends_maker_payment() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap();
let payment_tx_hash = payment.tx_hash();
Expand Down Expand Up @@ -287,6 +288,7 @@ fn test_maker_spends_taker_payment() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = taker_coin.send_taker_payment(taker_payment_args).wait().unwrap();
let payment_tx_hash = payment.tx_hash();
Expand Down Expand Up @@ -372,6 +374,7 @@ fn test_maker_refunds_payment() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = coin.send_maker_payment(maker_payment).wait().unwrap();
let payment_tx_hash = payment.tx_hash();
Expand Down Expand Up @@ -434,6 +437,7 @@ fn test_taker_refunds_payment() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = coin.send_taker_payment(taker_payment_args).wait().unwrap();
let payment_tx_hash = payment.tx_hash();
Expand Down Expand Up @@ -493,6 +497,7 @@ fn test_check_if_my_payment_sent() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = coin.send_maker_payment(maker_payment_args).wait().unwrap();
let payment_tx_hash = payment.tx_hash();
Expand Down Expand Up @@ -544,6 +549,7 @@ fn test_search_for_swap_tx_spend_taker_spent() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap();
let payment_tx_hash = payment.tx_hash();
Expand Down Expand Up @@ -617,6 +623,7 @@ fn test_search_for_swap_tx_spend_maker_refunded() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap();
let payment_tx_hash = payment.tx_hash();
Expand Down Expand Up @@ -689,6 +696,7 @@ fn test_search_for_swap_tx_spend_not_spent() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap();
let payment_tx_hash = payment.tx_hash();
Expand Down Expand Up @@ -741,6 +749,7 @@ fn test_wait_for_tx_spend() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let payment = maker_coin.send_maker_payment(maker_payment_args).wait().unwrap();
let payment_tx_hash = payment.tx_hash();
Expand Down Expand Up @@ -1058,6 +1067,7 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};

let _taker_payment_tx = coin
Expand Down Expand Up @@ -1457,6 +1467,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_maker() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let tx = coin.send_maker_payment(maker_payment).wait().unwrap();

Expand Down Expand Up @@ -1515,6 +1526,7 @@ fn test_search_for_segwit_swap_tx_spend_native_was_refunded_taker() {
swap_unique_data: &[],
payment_instructions: &None,
watcher_reward: None,
wait_for_confirmation_until: 0,
};
let tx = coin.send_taker_payment(taker_payment).wait().unwrap();

Expand Down
Loading

0 comments on commit 169964e

Please sign in to comment.