Skip to content

Commit

Permalink
Merge branch 'main' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
alfetopito committed Jan 29, 2025
2 parents 0b85a17 + 0c6b889 commit ce156d8
Show file tree
Hide file tree
Showing 41 changed files with 346 additions and 207 deletions.
6 changes: 3 additions & 3 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"apps/cowswap-frontend": "1.96.2",
"apps/cowswap-frontend": "1.97.0",
"apps/explorer": "2.42.0",
"libs/permit-utils": "0.6.0",
"libs/widget-lib": "0.18.0",
Expand All @@ -9,15 +9,15 @@
"libs/assets": "1.12.0",
"libs/common-const": "1.13.0",
"libs/common-hooks": "1.6.1",
"libs/common-utils": "1.8.0",
"libs/common-utils": "1.9.0",
"libs/core": "1.6.0",
"libs/ens": "1.3.0",
"libs/events": "1.5.0",
"libs/snackbars": "1.1.1",
"libs/tokens": "1.14.0",
"libs/types": "1.5.0",
"libs/ui": "1.18.0",
"libs/wallet": "1.9.0",
"libs/wallet": "1.9.1",
"apps/cow-fi": "1.19.3",
"libs/wallet-provider": "1.0.0",
"libs/ui-utils": "1.1.0",
Expand Down
12 changes: 12 additions & 0 deletions apps/cowswap-frontend/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## [1.97.0](https://github.com/cowprotocol/cowswap/compare/cowswap-v1.96.2...cowswap-v1.97.0) (2025-01-29)


### Features

* make useContract chainId aware and handle errors and loading state ([#5352](https://github.com/cowprotocol/cowswap/issues/5352)) ([d66e8ff](https://github.com/cowprotocol/cowswap/commit/d66e8ffc8668ad7563130228fb9bec28ae1d7e7e))


### Bug Fixes

* use provider.send('eth_chainId',[]) to avoid cached values ([#5354](https://github.com/cowprotocol/cowswap/issues/5354)) ([ad34521](https://github.com/cowprotocol/cowswap/commit/ad3452181287acb151b22d541feb156d208db482))

## [1.96.2](https://github.com/cowprotocol/cowswap/compare/cowswap-v1.96.1...cowswap-v1.96.2) (2025-01-27)


Expand Down
2 changes: 1 addition & 1 deletion apps/cowswap-frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cowprotocol/cowswap",
"version": "1.96.2",
"version": "1.97.0",
"description": "CoW Swap",
"main": "index.js",
"author": "",
Expand Down
10 changes: 4 additions & 6 deletions apps/cowswap-frontend/src/common/hooks/useApproveCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useCallback } from 'react'

import { Erc20 } from '@cowprotocol/abis'
import { calculateGasMargin, getIsNativeToken } from '@cowprotocol/common-utils'
import { useWalletInfo } from '@cowprotocol/wallet'
import { BigNumber } from '@ethersproject/bignumber'
import { MaxUint256 } from '@ethersproject/constants'
import { TransactionResponse } from '@ethersproject/providers'
Expand Down Expand Up @@ -56,15 +55,14 @@ export function useApproveCallback(
amountToApprove?: CurrencyAmount<Currency>,
spender?: string,
): (summary?: string) => Promise<TransactionResponse | undefined> {
const { chainId } = useWalletInfo()
const currency = amountToApprove?.currency
const token = currency && !getIsNativeToken(currency) ? currency : undefined
const tokenContract = useTokenContract(token?.address)
const { contract: tokenContract, chainId: tokenChainId } = useTokenContract(token?.address)
const addTransaction = useTransactionAdder()

return useCallback(async () => {
if (!chainId || !token || !tokenContract || !amountToApprove || !spender) {
console.error('Wrong input for approve: ', { chainId, token, tokenContract, amountToApprove, spender })
if (!tokenChainId || !token || !tokenContract || !amountToApprove || !spender) {
console.error('Wrong input for approve: ', { tokenChainId, token, tokenContract, amountToApprove, spender })
return
}

Expand All @@ -81,5 +79,5 @@ export function useApproveCallback(
})
return response
})
}, [chainId, token, tokenContract, amountToApprove, spender, addTransaction])
}, [tokenChainId, token, tokenContract, amountToApprove, spender, addTransaction])
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ import { getIsComposableCowParentOrder } from 'utils/orderUtils/getIsComposableC
import { getIsTheLastTwapPart } from 'utils/orderUtils/getIsTheLastTwapPart'

export function useGetOnChainCancellation(): (order: Order) => Promise<OnChainCancellation> {
const ethFlowContract = useEthFlowContract()
const settlementContract = useGP2SettlementContract()
const { contract: ethFlowContract, chainId: ethFlowChainId } = useEthFlowContract()
const { contract: settlementContract, chainId: settlementChainId } = useGP2SettlementContract()
const cancelTwapOrder = useCancelTwapOrder()

return useCallback(
(order: Order) => {
if (ethFlowChainId !== settlementChainId) {
throw new Error(
`Chain Id from contracts should match (ethFlow=${ethFlowChainId}, settlement=${settlementChainId})`,
)
}

if (getIsTheLastTwapPart(order.composableCowInfo)) {
return cancelTwapOrder(order.composableCowInfo!.parentId!, order)
}
Expand All @@ -37,6 +43,6 @@ export function useGetOnChainCancellation(): (order: Order) => Promise<OnChainCa

return getOnChainCancellation(settlementContract!, order)
},
[ethFlowContract, settlementContract, cancelTwapOrder]
[ethFlowContract, settlementContract, cancelTwapOrder, ethFlowChainId, settlementChainId],
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,30 @@ describe('useSendOnChainCancellation() + useGetOnChainCancellation()', () => {
ethFlowInvalidationMock.mockResolvedValue({ hash: ethFlowCancellationTxHash })

mockUseEthFlowContract.mockReturnValue({
estimateGas: {
invalidateOrder: () => Promise.resolve(BigNumber.from(100)),
},
invalidateOrder: ethFlowInvalidationMock,
} as any)
contract: {
estimateGas: {
invalidateOrder: () => Promise.resolve(BigNumber.from(100)),
},
invalidateOrder: ethFlowInvalidationMock,
} as any,
chainId,
error: null,
loading: false,
})

settlementInvalidationMock.mockResolvedValue({ hash: settlementCancellationTxHash })

mockUseGP2SettlementContract.mockReturnValue({
estimateGas: {
invalidateOrder: () => Promise.resolve(BigNumber.from(200)),
},
invalidateOrder: settlementInvalidationMock,
} as any)
contract: {
estimateGas: {
invalidateOrder: () => Promise.resolve(BigNumber.from(200)),
},
invalidateOrder: settlementInvalidationMock,
} as any,
chainId,
error: null,
loading: false,
})
})

afterEach(() => {
Expand Down
109 changes: 70 additions & 39 deletions apps/cowswap-frontend/src/common/hooks/useContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,73 +12,104 @@ import {
Weth,
WethAbi,
} from '@cowprotocol/abis'
import { V_COW_CONTRACT_ADDRESS, WRAPPED_NATIVE_CURRENCIES } from '@cowprotocol/common-const'
import { getContract, getEthFlowContractAddress } from '@cowprotocol/common-utils'
import { COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS } from '@cowprotocol/cow-sdk'
import {
COWSWAP_ETHFLOW_CONTRACT_ADDRESS,
V_COW_CONTRACT_ADDRESS,
WRAPPED_NATIVE_CURRENCIES,
} from '@cowprotocol/common-const'
import { getContract, isEns, isProd, isStaging } from '@cowprotocol/common-utils'
import { COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS, SupportedChainId } from '@cowprotocol/cow-sdk'
import { useWalletInfo } from '@cowprotocol/wallet'
import { useWalletProvider } from '@cowprotocol/wallet-provider'
import { Contract } from '@ethersproject/contracts'
import { Web3Provider } from '@ethersproject/providers'

const WETH_CONTRACT_ADDRESS_MAP = Object.fromEntries(
Object.entries(WRAPPED_NATIVE_CURRENCIES).map(([chainId, token]) => [chainId, token.address]),
)

const contractEnv = isProd || isStaging || isEns ? 'prod' : 'barn'
export const COWSWAP_ETHFLOW_CONTRACT_ADDRESS_MAP = COWSWAP_ETHFLOW_CONTRACT_ADDRESS[contractEnv]

export type UseContractResult<T extends Contract = Contract> = {
contract: T | null
error: unknown | null
loading: boolean
chainId: SupportedChainId
}

// returns null on errors
export function useContract<T extends Contract = Contract>(
addressOrAddressMap: string | { [chainId: number]: string } | undefined,
addressOrAddressMap: string | { [chainId: number]: string | undefined | null } | undefined,
ABI: any,
withSignerIfPossible = true,
customProvider?: Web3Provider
): T | null {
customProvider?: Web3Provider,
): UseContractResult<T> {
const defaultProvider = useWalletProvider()
const { account, chainId } = useWalletInfo()
const provider = customProvider || defaultProvider

return useMemo(() => {
if (!addressOrAddressMap || !ABI || !provider || !chainId) return null
let address: string | undefined
if (typeof addressOrAddressMap === 'string') address = addressOrAddressMap
else address = addressOrAddressMap[chainId]
if (!address) return null
if (!addressOrAddressMap || !ABI || !provider) {
// Loading, waiting for chain or some basic information to instantiate the contract
return {
contract: null,
error: null,
chainId,
loading: true,
}
}

// Get the address for the contract
const address = typeof addressOrAddressMap === 'string' ? addressOrAddressMap : addressOrAddressMap[chainId]

if (!address) {
// No address, no contract
return {
contract: null,
error: null,
chainId,
loading: false,
}
}
try {
return getContract(address, ABI, provider, withSignerIfPossible && account ? account : undefined)
// Load and return the contract
const contract = getContract(address, ABI, provider, withSignerIfPossible && account ? account : undefined)
return {
contract,
error: null,
chainId,
loading: false,
}
} catch (error: any) {
// Error getting the contract instance
console.error('Failed to get contract', error)
return null
return {
contract: null,
error: error,
chainId,
loading: false,
}
}
}, [addressOrAddressMap, ABI, provider, chainId, withSignerIfPossible, account]) as T
}, [addressOrAddressMap, ABI, provider, chainId, withSignerIfPossible, account]) as UseContractResult<T>
}

export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean) {
return useContract<Erc20>(tokenAddress, Erc20Abi, withSignerIfPossible)
}

export function useWETHContract(withSignerIfPossible?: boolean) {
const { chainId } = useWalletInfo()
return useContract<Weth>(
chainId ? WRAPPED_NATIVE_CURRENCIES[chainId]?.address : undefined,
WethAbi,
withSignerIfPossible
)
export function useWethContract(withSignerIfPossible?: boolean) {
return useContract<Weth>(WETH_CONTRACT_ADDRESS_MAP, WethAbi, withSignerIfPossible)
}

export function useEthFlowContract(): CoWSwapEthFlow | null {
const { chainId } = useWalletInfo()

const contractAddress = getEthFlowContractAddress(chainId)

return useContract<CoWSwapEthFlow>(contractAddress, CoWSwapEthFlowAbi, true)
export function useEthFlowContract(): UseContractResult<CoWSwapEthFlow> {
return useContract<CoWSwapEthFlow>(COWSWAP_ETHFLOW_CONTRACT_ADDRESS_MAP, CoWSwapEthFlowAbi, true)
}

export function useGP2SettlementContract(): GPv2Settlement | null {
const { chainId } = useWalletInfo()
return useContract<GPv2Settlement>(
chainId ? COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS[chainId] : undefined,
GPv2SettlementAbi,
true
)
export function useGP2SettlementContract(): UseContractResult<GPv2Settlement> {
return useContract<GPv2Settlement>(COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS, GPv2SettlementAbi, true)
}

export function useVCowContract() {
const { chainId } = useWalletInfo()
const vCowAddress = V_COW_CONTRACT_ADDRESS[chainId] || undefined

return useContract<VCow>(vCowAddress, vCowAbi, true)
export function useVCowContract(): UseContractResult<VCow> {
return useContract<VCow>(V_COW_CONTRACT_ADDRESS, vCowAbi, true)
}
4 changes: 2 additions & 2 deletions apps/cowswap-frontend/src/legacy/hooks/useGasPrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ const CHAIN_DATA_ABI = [
*/
export default function useGasPrice(): JSBI | undefined {
const { address } = useENSAddress('fast-gas-gwei.data.eth')
const contract = useContract(address ?? undefined, CHAIN_DATA_ABI, false)
const { contract } = useContract(address ?? undefined, CHAIN_DATA_ABI, false)

const { data: result } = useSWR(contract ? ['useGasPrice', contract] : null, async ([, _contract]) =>
_contract.callStatic.latestAnswer()
_contract.callStatic.latestAnswer(),
)
const resultStr = result?.toString()

Expand Down
10 changes: 5 additions & 5 deletions apps/cowswap-frontend/src/legacy/hooks/useTokenAllowance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@ const ALLOWANCES_SWR_CONFIG = { refreshInterval: ms`10s` }
export function useTokenAllowance(
token: Nullish<Token>,
owner?: string,
spender?: string
spender?: string,
): CurrencyAmount<Token> | undefined {
const tokenAddress = token?.address
const contract = useTokenContract(tokenAddress, false)
const { contract: tokenContract } = useTokenContract(tokenAddress, false)

const { data: allowance } = useSWR(
owner && spender && contract ? ['useTokenAllowance', tokenAddress, owner, spender, contract] : null,
owner && spender && tokenContract ? ['useTokenAllowance', tokenAddress, owner, spender, tokenContract] : null,
async ([, , _owner, _spender, _contract]) => _contract?.callStatic.allowance(_owner, _spender),
ALLOWANCES_SWR_CONFIG
ALLOWANCES_SWR_CONFIG,
)

return useMemo(
() => (token && allowance ? CurrencyAmount.fromRawAmount(token, allowance.toString()) : undefined),
[token, allowance]
[token, allowance],
)
}
19 changes: 10 additions & 9 deletions apps/cowswap-frontend/src/legacy/hooks/useWrapCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { getChainCurrencySymbols, RADIX_HEX } from '@cowprotocol/common-const'
import {
calculateGasMargin,
formatTokenAmount,
getChainIdImmediately,
getIsNativeToken,
isRejectRequestProviderError,
} from '@cowprotocol/common-utils'
import { SupportedChainId } from '@cowprotocol/cow-sdk'
import { Command } from '@cowprotocol/types'
import { BigNumber } from '@ethersproject/bignumber'
import { Contract } from '@ethersproject/contracts'
import { TransactionResponse } from '@ethersproject/providers'
import { JsonRpcProvider, TransactionResponse } from '@ethersproject/providers'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'

import { useTransactionAdder } from 'legacy/state/enhancedTransactions/hooks'
Expand Down Expand Up @@ -113,14 +114,14 @@ async function wrapContractCall(
const estimatedGas = await wethContract.estimateGas.deposit({ value: amountHex }).catch(_handleGasEstimateError)
const gasLimit = calculateGasMargin(estimatedGas)

const network = await wethContract.provider.getNetwork()
if (network.chainId !== chainId) {
throw new Error('Wallet chain differs from order params.')
const network = await getChainIdImmediately(wethContract.provider as JsonRpcProvider)
if (network !== chainId) {
throw new Error(`Wallet chainId differs from app chainId. Wallet: ${network}, App: ${chainId}`)
}

const tx = await wethContract.populateTransaction.deposit({ value: amountHex, gasLimit })

return wethContract.signer.sendTransaction({ ...tx, chainId: network.chainId })
return wethContract.signer.sendTransaction({ ...tx, chainId: network })
}

async function unwrapContractCall(
Expand All @@ -133,12 +134,12 @@ async function unwrapContractCall(

const tx = await wethContract.populateTransaction.withdraw(amountHex, { gasLimit })

const network = await wethContract.provider.getNetwork()
if (network.chainId !== chainId) {
throw new Error('Wallet chain differs from order params.')
const network = await getChainIdImmediately(wethContract.provider as JsonRpcProvider)
if (network !== chainId) {
throw new Error(`Wallet chainId differs from app chainId. Wallet: ${network}, App: ${chainId}`)
}

return wethContract.signer.sendTransaction({ ...tx, chainId: network.chainId })
return wethContract.signer.sendTransaction({ ...tx, chainId: network })
}

function _handleGasEstimateError(error: any): BigNumber {
Expand Down
Loading

0 comments on commit ce156d8

Please sign in to comment.