Skip to content

Commit

Permalink
Fix/gas estimation (#235)
Browse files Browse the repository at this point in the history
* fix: add gas estimation for staking

* fix: add gas estimation for unstaking

* fix: handle user cancellation more gracefully

* fix: setMax is now handle staking and unstaking differently

* fix: missing hook dependency

* refactor: rename estimateGas
  • Loading branch information
nicolasbrugneaux authored Feb 9, 2024
1 parent 6bbac7f commit fe56093
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 31 deletions.
44 changes: 30 additions & 14 deletions src/features/swap/hooks/useStaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import logger from 'src/services/logger';
import { Mode } from 'src/types';
import { transactionEvent } from 'src/utils/ga';
import { Celo, CeloUSD, StCelo, Token } from 'src/utils/tokens';
import { useContractWrite, usePublicClient } from 'wagmi';
import { Address, useContractWrite, usePublicClient } from 'wagmi';
import { showErrorToast, showHashToast, showStakingToast } from '../utils/toast';

export function useStaking() {
Expand All @@ -28,6 +28,19 @@ export function useStaking() {

const client = usePublicClient();

const _estimateDepositGas = useCallback(
(address: Address, value: bigint) => {
return publicClient.estimateContractGas({
abi: managerContract.abi,
address: managerContract.address!,
account: address!,
functionName: 'deposit',
value,
});
},
[publicClient, managerContract]
);

const stake = useCallback(
async (callbacks?: TxCallbacks) => {
if (!address || !celoAmount || celoAmount.isEqualTo(0) || !stCeloBalance || !loadBalances) {
Expand All @@ -41,7 +54,9 @@ export function useStaking() {
value: celoAmount.displayAsBase(),
});
try {
const stakeHash = await _stake({ value: celoAmount?.toBigInt() });
const value = celoAmount?.toBigInt();
const gas = await _estimateDepositGas(address!, value);
const stakeHash = await _stake({ value, gas });
console.info('stakeHash', stakeHash);
transactionEvent({
action: Mode.stake,
Expand All @@ -67,7 +82,8 @@ export function useStaking() {
} catch (e: unknown) {
console.error(e);
showErrorToast(
(e as Error).message.includes('rejected')
(e as Error).message.includes('rejected') ||
(e as any).details?.toLowerCase().includes('cancelled')
? 'User rejected the request'
: (e as Error).message
);
Expand All @@ -81,7 +97,7 @@ export function useStaking() {
logger.error('afterDeposit error', e);
}
},
[_stake, address, api, celoAmount, client, loadBalances, stCeloBalance]
[_estimateDepositGas, _stake, address, api, celoAmount, client, loadBalances, stCeloBalance]
);

const estimateStakingGas = useCallback(async () => {
Expand All @@ -95,19 +111,19 @@ export function useStaking() {
return null;
}

const gasFee = new Token(
await publicClient.estimateContractGas({
abi: managerContract.abi,
address: managerContract.address!,
account: address!,
functionName: 'deposit',
value: celoAmount.toBigInt(),
})
);
const gasFee = new Token(await _estimateDepositGas(address!, celoAmount.toBigInt()));
const gasFeeInCelo = new Celo(gasFee.multipliedBy(suggestedGasPrice));
const gasFeeInUSD = new CeloUSD(gasFeeInCelo.multipliedBy(celoToUSDRate));
return gasFeeInUSD;
}, [celoAmount, celoBalance, managerContract, publicClient, suggestedGasPrice, celoToUSDRate]);
}, [
celoAmount,
celoBalance,
address,
managerContract.address,
_estimateDepositGas,
suggestedGasPrice,
celoToUSDRate,
]);

const receivedStCelo = useMemo(
() => (celoAmount ? new StCelo(celoAmount.multipliedBy(stakingRate).dp(0)) : null),
Expand Down
16 changes: 13 additions & 3 deletions src/features/swap/hooks/useSwap.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import BigNumber from 'bignumber.js';
import { useCallback, useEffect, useState } from 'react';
import { MAX_AMOUNT_THRESHOLD } from 'src/config/consts';
import { useAccountContext } from 'src/contexts/account/AccountContext';
import { TxCallbacks } from 'src/contexts/blockchain/useBlockchain';
import { useProtocolContext } from 'src/contexts/protocol/ProtocolContext';
import { Mode } from 'src/types';
import { Celo, CeloUSD, StCelo, Token } from 'src/utils/tokens';
import { useStaking } from './useStaking';
import { useUnstaking } from './useUnstaking';
import { MAX_AMOUNT_THRESHOLD } from 'src/config/consts';

export function useSwap(mode: Mode) {
const { stakingRate, unstakingRate } = useProtocolContext();
Expand Down Expand Up @@ -47,8 +47,18 @@ export function useSwap(mode: Mode) {
}

const setMaxAmount = useCallback(() => {
const maxAmount = new Token(BigNumber.max(0, balance.minus(MAX_AMOUNT_THRESHOLD.toString())));
setAmount(maxAmount);
let maxAmount: Token;
if (Mode.stake) {
// NOTE: we're doing this weird `minus` operation to make sure the
// account owner has enough currency leftover to pay for gasfees.

// NOTE2: this is a good use-case for alternative gas currencies
// (cUSD, cEUR, etc...)
maxAmount = new Token(BigNumber.max(0, balance.minus(MAX_AMOUNT_THRESHOLD.toString())));
} else if (Mode.unstake) {
maxAmount = new Token(BigNumber.max(0, balance));
}
setAmount(maxAmount!);
}, [setAmount, balance]);

// Don't override gasFee when estimateGas function is not the latest one
Expand Down
44 changes: 30 additions & 14 deletions src/features/swap/hooks/useUnstaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useAPI } from 'src/hooks/useAPI';
import logger from 'src/services/logger';
import { Mode } from 'src/types';
import { Celo, CeloUSD, StCelo, Token } from 'src/utils/tokens';
import { useContractWrite, usePublicClient } from 'wagmi';
import { Address, useContractWrite, usePublicClient } from 'wagmi';
import { transactionEvent } from '../../../utils/ga';
import { showErrorToast, showUnstakingToast } from '../utils/toast';

Expand All @@ -26,6 +26,19 @@ export function useUnstaking() {
args: [stCeloAmount?.toBigInt() || 0n] as const,
});

const _estimateWithdrawGas = useCallback(
(address: Address, value: bigint) => {
return publicClient.estimateContractGas({
abi: managerContract.abi,
address: managerContract.address!,
account: address,
functionName: 'withdraw',
args: [value || 0n] as const,
});
},
[publicClient, managerContract]
);

const unstake = useCallback(
async (callbacks?: TxCallbacks) => {
if (
Expand All @@ -43,23 +56,34 @@ export function useUnstaking() {
value: stCeloAmount.displayAsBase(),
});
try {
await _unstake();
const gas = await _estimateWithdrawGas(address, stCeloAmount?.toBigInt());
await _unstake({ gas });
await api.withdraw(address);
showUnstakingToast();
await Promise.all([loadBalances(), loadPendingWithdrawals?.()]);
setStCeloAmount(null);
} catch (e: unknown) {
logger.error(e);
showErrorToast(
(e as Error).message.includes('rejected')
(e as Error).message.includes('rejected') ||
(e as any).details?.toLowerCase().includes('cancelled')
? 'User rejected the request'
: (e as Error).message
);
} finally {
callbacks?.onSent?.();
}
},
[_unstake, address, api, loadBalances, loadPendingWithdrawals, managerContract, stCeloAmount]
[
_estimateWithdrawGas,
_unstake,
address,
api,
loadBalances,
loadPendingWithdrawals,
managerContract,
stCeloAmount,
]
);

const estimateUnstakingGas = useCallback(async () => {
Expand All @@ -72,23 +96,15 @@ export function useUnstaking() {
return null;
}

const gasFee = new Token(
await publicClient.estimateContractGas({
abi: managerContract.abi,
address: managerContract.address!,
account: address!,
functionName: 'withdraw',
args: [stCeloAmount?.toBigInt() || 0n] as const,
})
);
const gasFee = new Token(await _estimateWithdrawGas(address!, stCeloAmount?.toBigInt()));
const gasFeeInCelo = new Celo(gasFee.multipliedBy(suggestedGasPrice));
const gasFeeInUSD = new CeloUSD(gasFeeInCelo.multipliedBy(celoToUSDRate));
return gasFeeInUSD;
}, [
stCeloAmount,
stCeloBalance,
managerContract,
publicClient,
_estimateWithdrawGas,
address,
suggestedGasPrice,
celoToUSDRate,
Expand Down

0 comments on commit fe56093

Please sign in to comment.