Skip to content
This repository has been archived by the owner on Jun 24, 2022. It is now read-only.

Commit

Permalink
[Claim] Revoke Approval hook (#2249)
Browse files Browse the repository at this point in the history
* create hook and button

* change message

* messages

* add revoke approve operationtype

* delete previousr revoke approve cb

* add modalMessage optional type

* add revokeApprovalCallback to original approve callback mod

* add revoke to investOption and change labels

* change return type to object

* styles

* remove revoke UI elements and handlers
  • Loading branch information
W3stside authored Jan 25, 2022
1 parent a8d90c6 commit 7355deb
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 37 deletions.
5 changes: 5 additions & 0 deletions src/custom/components/TransactionConfirmationModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ export enum OperationType {
WRAP_ETHER,
UNWRAP_WETH,
APPROVE_TOKEN,
REVOKE_APPROVE_TOKEN,
ORDER_SIGN,
ORDER_CANCEL,
}
Expand Down Expand Up @@ -382,6 +383,8 @@ function getOperationMessage(operationType: OperationType, chainId: number): str
return 'Approving token'
case OperationType.ORDER_CANCEL:
return 'Soft canceling your order'
case OperationType.REVOKE_APPROVE_TOKEN:
return 'Revoking token approval'

default:
return 'Almost there!'
Expand All @@ -396,6 +399,8 @@ function getOperationLabel(operationType: OperationType): string {
return t`unwrapping`
case OperationType.APPROVE_TOKEN:
return t`token approval`
case OperationType.REVOKE_APPROVE_TOKEN:
return t`revoking token approval`
case OperationType.ORDER_SIGN:
return t`order`
case OperationType.ORDER_CANCEL:
Expand Down
3 changes: 2 additions & 1 deletion src/custom/hooks/useApproveCallback/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ export function useApproveCallbackFromTrade({
}

export type OptionalApproveCallbackParams = {
transactionSummary: string
transactionSummary?: string
modalMessage?: string
}

type ApproveCallbackFromClaimParams = Omit<
Expand Down
90 changes: 85 additions & 5 deletions src/custom/hooks/useApproveCallback/useApproveCallbackMod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import { useTokenContract } from 'hooks/useContract'
import { useTokenAllowance } from 'hooks/useTokenAllowance'
import { useActiveWeb3React } from 'hooks/web3'
import { OptionalApproveCallbackParams } from '.'
import { useCurrency } from 'hooks/Tokens'
import { OperationType } from 'components/TransactionConfirmationModal'

// Use a 150K gas as a fallback if there's issue calculating the gas estimation (fixes some issues with some nodes failing to calculate gas costs for SC wallets)
const APPROVE_GAS_LIMIT_DEFAULT = BigNumber.from('150000')
export const APPROVE_GAS_LIMIT_DEFAULT = BigNumber.from('150000')

export enum ApprovalState {
UNKNOWN = 'UNKNOWN',
Expand All @@ -25,7 +27,7 @@ export enum ApprovalState {
}

export interface ApproveCallbackParams {
openTransactionConfirmationModal: (message: string) => void
openTransactionConfirmationModal: (message: string, operationType: OperationType) => void
closeModals: () => void
amountToApprove?: CurrencyAmount<Currency>
spender?: string
Expand All @@ -39,11 +41,12 @@ export function useApproveCallback({
amountToApprove,
spender,
amountToCheckAgainstAllowance,
}: ApproveCallbackParams): [ApprovalState, (optionalParams?: OptionalApproveCallbackParams) => Promise<void>] {
}: ApproveCallbackParams) {
const { account, chainId } = useActiveWeb3React()
const token = amountToApprove?.currency?.isToken ? amountToApprove.currency : undefined
const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
const pendingApproval = useHasPendingApproval(token?.address, spender)
const spenderCurrency = useCurrency(spender)

// TODO: Nice to have, can be deleted
{
Expand Down Expand Up @@ -133,7 +136,10 @@ export function useApproveCallback({
})
})

openTransactionConfirmationModal(`Approving ${amountToApprove.currency.symbol} for trading`)
openTransactionConfirmationModal(
optionalParams?.modalMessage || `Approving ${amountToApprove.currency.symbol} for trading`,
OperationType.APPROVE_TOKEN
)
return (
tokenContract
.approve(spender, useExact ? amountToApprove.quotient.toString() : MaxUint256, {
Expand Down Expand Up @@ -166,7 +172,81 @@ export function useApproveCallback({
]
)

return [approvalState, approve]
const revokeApprove = useCallback(
async (optionalParams?: OptionalApproveCallbackParams): Promise<void> => {
if (approvalState === ApprovalState.NOT_APPROVED) {
console.error('Revoke approve was called unnecessarily')
return
}
if (!chainId) {
console.error('no chainId')
return
}

if (!token) {
console.error('no token')
return
}

if (!tokenContract) {
console.error('tokenContract is null')
return
}

if (!spender) {
console.error('no spender')
return
}

const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
// general fallback for tokens who restrict approval amounts
return tokenContract.estimateGas.approve(spender, '0').catch((error) => {
console.log(
'[useApproveCallbackMod] Error estimating gas for revoking approval. Using default gas limit ' +
APPROVE_GAS_LIMIT_DEFAULT.toString(),
error
)
return APPROVE_GAS_LIMIT_DEFAULT
})
})

openTransactionConfirmationModal(
optionalParams?.modalMessage || `Revoke ${token.symbol} approval from ${spenderCurrency?.symbol || spender}`,
OperationType.REVOKE_APPROVE_TOKEN
)
return (
tokenContract
.approve(spender, '0', {
gasLimit: calculateGasMargin(chainId, estimatedGas),
})
.then((response: TransactionResponse) => {
addTransaction({
hash: response.hash,
summary: optionalParams?.transactionSummary || `Revoke ${token.symbol} approval from ${spender}`,
approval: { tokenAddress: token.wrapped.address, spender },
})
})
// .catch((error: Error) => {
// console.debug('Failed to approve token', error)
// throw error
// })
.finally(closeModals)
)
},
[
approvalState,
chainId,
token,
tokenContract,
spender,
spenderCurrency?.symbol,
openTransactionConfirmationModal,
closeModals,
addTransaction,
]
)

return { approvalState, approve, revokeApprove, isPendingApproval: pendingApproval }
}

// wraps useApproveCallback in the context of a swap
Expand Down
66 changes: 46 additions & 20 deletions src/custom/pages/Claim/InvestmentFlow/InvestOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { BigNumber } from '@ethersproject/bignumber'

import CowProtocolLogo from 'components/CowProtocolLogo'
import { InvestTokenGroup, TokenLogo, InvestSummary, InvestInput, InvestAvailableBar } from '../styled'
import { InvestTokenGroup, TokenLogo, InvestSummary, InvestInput, InvestAvailableBar, UnderlineButton } from '../styled'
import { formatSmartLocaleAware } from 'utils/format'
import Row from 'components/Row'
import CheckCircle from 'assets/cow-swap/check.svg'
import { InvestmentFlowProps } from '.'
import { ApprovalState, useApproveCallbackFromClaim } from 'hooks/useApproveCallback'
import { useCurrencyBalance } from 'state/wallet/hooks'
import { useActiveWeb3React } from 'hooks/web3'
import { ClaimType, useClaimDispatchers, useClaimState } from 'state/claim/hooks'
import { useClaimDispatchers, useClaimState } from 'state/claim/hooks'
import { StyledNumericalInput } from 'components/CurrencyInputPanel/CurrencyInputPanelMod'

import { ButtonConfirmed } from 'components/Button'
Expand Down Expand Up @@ -42,18 +42,6 @@ type InvestOptionProps = {
closeModal: InvestmentFlowProps['modalCbs']['closeModal']
}

const _claimApproveMessageMap = (type: ClaimType) => {
switch (type) {
case ClaimType.GnoOption:
return 'Approving GNO for investing in vCOW'
case ClaimType.Investor:
return 'Approving USDC for investing in vCOW'
// Shouldn't happen, type safe
default:
return 'Unknown token approval. Please check configuration.'
}
}

export default function InvestOption({ claim, optionIndex, openModal, closeModal }: InvestOptionProps) {
const { currencyAmount, price, cost: maxCost } = claim

Expand All @@ -64,8 +52,14 @@ export default function InvestOption({ claim, optionIndex, openModal, closeModal
const investmentAmount = investFlowData[optionIndex].investedAmount

// Approve hooks
const [approveState, approveCallback] = useApproveCallbackFromClaim({
openTransactionConfirmationModal: () => openModal(_claimApproveMessageMap(claim.type), OperationType.APPROVE_TOKEN),
const {
approvalState: approveState,
approve: approveCallback,
// revokeApprove: revokeApprovalCallback, // CURRENTLY TEST ONLY
// isPendingApproval, // CURRENTLY TEST ONLY
} = useApproveCallbackFromClaim({
openTransactionConfirmationModal: (message: string, operationType: OperationType) =>
openModal(message, operationType),
closeModals: closeModal,
claim,
})
Expand Down Expand Up @@ -138,9 +132,9 @@ export default function InvestOption({ claim, optionIndex, openModal, closeModal
if (!approveCallback) return

try {
// for pending state pre-BC
setApproving(true)
await approveCallback({ transactionSummary: `Approve ${token?.symbol || 'token'} for investing in vCOW` })
const summary = `Approve ${token?.symbol || 'token'} for investing in vCOW`
await approveCallback({ modalMessage: summary, transactionSummary: summary })
} catch (error) {
console.error('[InvestOption]: Issue approving.', error)
handleSetError(error?.message)
Expand All @@ -149,6 +143,29 @@ export default function InvestOption({ claim, optionIndex, openModal, closeModal
}
}, [approveCallback, handleCloseError, handleSetError, token?.symbol])

/* // CURRENTLY TEST ONLY
const handleRevokeApproval = useCallback(async () => {
// reset errors and close any modals
handleCloseError()
if (!revokeApprovalCallback) return
try {
setApproving(true)
const summary = `Revoke ${token?.symbol || 'token'} approval for vCOW contract`
await revokeApprovalCallback({
modalMessage: summary,
transactionSummary: summary,
})
} catch (error) {
console.error('[InvestOption]: Issue revoking approval.', error)
handleSetError(error?.message)
} finally {
setApproving(false)
}
}, [handleCloseError, handleSetError, revokeApprovalCallback, token?.symbol])
*/

const vCowAmount = useMemo(
() => calculateInvestmentAmounts(claim, investmentAmount)?.vCowAmount,
[claim, investmentAmount]
Expand Down Expand Up @@ -303,6 +320,14 @@ export default function InvestOption({ claim, optionIndex, openModal, closeModal
)}
</ButtonConfirmed>
)}
{/*
// CURRENTLY TEST ONLY
approveState === ApprovalState.APPROVED && (
<UnderlineButton disabled={approving || isPendingApproval} onClick={handleRevokeApproval}>
Revoke approval {approving || (isPendingApproval && <Loader size="12px" stroke="white" />)}
</UnderlineButton>
)
*/}
</span>

<span>
Expand All @@ -323,9 +348,10 @@ export default function InvestOption({ claim, optionIndex, openModal, closeModal
</i>
{/* Button should use the max possible amount the user can invest, considering their balance + max investment allowed */}
{!noBalance && isSelfClaiming && (
<button disabled={!isSelfClaiming} onClick={setMaxAmount}>
<UnderlineButton disabled={!isSelfClaiming} onClick={setMaxAmount}>
{' '}
(invest max. possible)
</button>
</UnderlineButton>
)}
</span>
<StyledNumericalInput
Expand Down
36 changes: 26 additions & 10 deletions src/custom/pages/Claim/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,30 @@ export const InvestTokenGroup = styled.div`
}
`

export const UnderlineButton = styled.button`
display: flex;
align-items: center;
gap: 0 6px;
background: none;
border: 0;
cursor: pointer;
color: ${({ theme }) => theme.primary4};
text-decoration: underline;
text-align: left;
padding: 0;
&:hover {
color: ${({ theme }) => theme.text1};
}
&:disabled {
text-decoration: none;
color: ${({ theme }) => theme.disabled};
cursor: auto;
}
`

export const InvestInput = styled.span`
display: flex;
flex-flow: column wrap;
Expand Down Expand Up @@ -984,16 +1008,8 @@ export const InvestInput = styled.span`
font-style: normal;
}
> div > label > span > button {
background: none;
border: 0;
cursor: pointer;
color: ${({ theme }) => theme.primary4};
text-decoration: underline;
&:hover {
color: ${({ theme }) => theme.text1};
}
> div > label > span > ${UnderlineButton} {
margin-left: 4px;
}
`

Expand Down
2 changes: 1 addition & 1 deletion src/custom/pages/Swap/SwapMod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ export default function Swap({
const isLoadingRoute = toggledVersion === Version.v3 && V3TradeState.LOADING === v3TradeState */

// check whether the user has approved the router on the input token
const [approvalState, approveCallback] = useApproveCallbackFromTrade({
const { approvalState, approve: approveCallback } = useApproveCallbackFromTrade({
openTransactionConfirmationModal: (message: string) =>
openTransactionConfirmationModal(message, OperationType.APPROVE_TOKEN),
closeModals,
Expand Down

0 comments on commit 7355deb

Please sign in to comment.