From 8538d307d5d227e2b8a3439fb3461b5de087f250 Mon Sep 17 00:00:00 2001 From: Warm Beer Date: Mon, 21 Oct 2024 11:06:47 +0200 Subject: [PATCH] feat(evmlib): add token allowance and approval functions & set contract approval to infinite --- evmlib/src/contract/network_token.rs | 14 ++++++++ evmlib/src/wallet.rs | 52 ++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/evmlib/src/contract/network_token.rs b/evmlib/src/contract/network_token.rs index a6fad9243d..013d572037 100644 --- a/evmlib/src/contract/network_token.rs +++ b/evmlib/src/contract/network_token.rs @@ -75,6 +75,20 @@ where Ok(balance) } + /// See how many tokens are approved to be spent. + pub async fn allowance(&self, owner: Address, spender: Address) -> Result { + debug!("Getting allowance of owner: {owner} for spender: {spender}",); + let balance = self + .contract + .allowance(owner, spender) + .call() + .await + .inspect_err(|err| error!("Error getting allowance: {err:?}"))? + ._0; + debug!("Allowance of owner: {owner} for spender: {spender} is: {balance}"); + Ok(balance) + } + /// Approve spender to spend a raw amount of tokens. pub async fn approve(&self, spender: Address, value: U256) -> Result { debug!("Approving spender to spend raw amt of tokens: {value}"); diff --git a/evmlib/src/wallet.rs b/evmlib/src/wallet.rs index 8c4ec78298..b9504f69a1 100644 --- a/evmlib/src/wallet.rs +++ b/evmlib/src/wallet.rs @@ -70,12 +70,12 @@ impl Wallet { /// Returns the raw balance of payment tokens for this wallet. pub async fn balance_of_tokens(&self) -> Result { - balance_of_tokens(wallet_address(&self.wallet), &self.network).await + balance_of_tokens(self.address(), &self.network).await } /// Returns the raw balance of gas tokens for this wallet. pub async fn balance_of_gas_tokens(&self) -> Result { - balance_of_gas_tokens(wallet_address(&self.wallet), &self.network).await + balance_of_gas_tokens(self.address(), &self.network).await } /// Transfer a raw amount of payment tokens to another address. @@ -96,6 +96,20 @@ impl Wallet { transfer_gas_tokens(self.wallet.clone(), &self.network, to, amount).await } + /// See how many tokens of the owner may be spent by the spender. + pub async fn token_allowance(&self, spender: Address) -> Result { + token_allowance(&self.network, self.address(), spender).await + } + + /// Approve an address / smart contract to spend this wallet's payment tokens. + pub async fn approve_to_spend_tokens( + &self, + spender: Address, + amount: U256, + ) -> Result { + approve_to_spend_tokens(self.wallet.clone(), &self.network, spender, amount).await + } + /// Pays for a single quote. Returns transaction hash of the payment. pub async fn pay_for_quote( &self, @@ -188,8 +202,20 @@ pub async fn balance_of_gas_tokens( Ok(balance) } +/// See how many tokens of the owner may be spent by the spender. +pub async fn token_allowance( + network: &Network, + owner: Address, + spender: Address, +) -> Result { + debug!("Getting allowance for owner: {owner} and spender: {spender}",); + let provider = http_provider(network.rpc_url().clone()); + let network_token = NetworkToken::new(*network.payment_token_address(), provider); + network_token.allowance(owner, spender).await +} + /// Approve an address / smart contract to spend this wallet's payment tokens. -async fn approve_to_spend_tokens( +pub async fn approve_to_spend_tokens( wallet: EthereumWallet, network: &Network, spender: Address, @@ -250,16 +276,28 @@ pub async fn pay_for_quotes>( let mut tx_hashes_by_quote = BTreeMap::new(); - // Approve the contract to spend enough of the client's tokens. - approve_to_spend_tokens( - wallet.clone(), + // Check allowance + let allowance = token_allowance( network, + wallet_address(&wallet), *network.data_payments_address(), - total_amount, ) .await .map_err(|err| PayForQuotesError(Error::from(err), tx_hashes_by_quote.clone()))?; + // TODO: Get rid of approvals altogether, by using permits or whatever.. + if allowance < total_amount { + // Approve the contract to spend all the client's tokens. + approve_to_spend_tokens( + wallet.clone(), + network, + *network.data_payments_address(), + U256::MAX, + ) + .await + .map_err(|err| PayForQuotesError(Error::from(err), tx_hashes_by_quote.clone()))?; + } + let provider = http_provider_with_wallet(network.rpc_url().clone(), wallet); let data_payments = DataPaymentsHandler::new(*network.data_payments_address(), provider);