diff --git a/.env.base b/.env.base index d2c9723e5b2..8502a59fdbd 100644 --- a/.env.base +++ b/.env.base @@ -172,8 +172,5 @@ REACT_APP_EXPERIMENTAL_CUSTOM_SEND_NONCE=false # Sentry REACT_APP_SENTRY_DSN_URL=https://c612e7f4ef0637e4add433a2f4683aa8@o4507174990905344.ingest.de.sentry.io/4507174994444368 -# Arbitrum One RFOX proxy contract address -REACT_APP_RFOX_PROXY_CONTRACT_ADDRESS=0xac2a4fd70bcd8bab0662960455c363735f0e2b56 - # Zerion REACT_APP_ZERION_BASE_URL=https://api.proxy.shapeshift.com/api/v1/zerion \ No newline at end of file diff --git a/.env.dev b/.env.dev index 908e9d88276..30203a7b0ef 100644 --- a/.env.dev +++ b/.env.dev @@ -52,7 +52,3 @@ REACT_APP_THORCHAIN_NODE_URL=https://dev-daemon.thorchain.shapeshift.com # thorchain REACT_APP_MIDGARD_URL=https://dev-indexer.thorchain.shapeshift.com/v2 - -# Arbitrum One testing RFOX proxy contract address - *not* the actual main contract, only for testing -# Uncomment me if you want to test RFOX with a shorter cooldown period -# REACT_APP_RFOX_PROXY_CONTRACT_ADDRESS=0x1094c4a99fce60e69ffe75849309408f1262d304 diff --git a/.env.develop b/.env.develop index 956348a97ff..c9c66eed109 100644 --- a/.env.develop +++ b/.env.develop @@ -49,7 +49,3 @@ REACT_APP_THORCHAIN_NODE_URL=https://dev-daemon.thorchain.shapeshift.com # thorchain REACT_APP_MIDGARD_URL=https://dev-indexer.thorchain.shapeshift.com/v2 - -# Arbitrum One testing RFOX proxy contract address - *not* the actual main contract, only for testing -# Uncomment me if you want to test RFOX with a shorter cooldown period -# REACT_APP_RFOX_PROXY_CONTRACT_ADDRESS=0x1094c4a99fce60e69ffe75849309408f1262d304 diff --git a/package.json b/package.json index 26383e725d6..648c1d4f0db 100644 --- a/package.json +++ b/package.json @@ -91,19 +91,19 @@ "@shapeshiftoss/caip": "workspace:^", "@shapeshiftoss/chain-adapters": "workspace:^", "@shapeshiftoss/errors": "workspace:^", - "@shapeshiftoss/hdwallet-coinbase": "1.54.1", - "@shapeshiftoss/hdwallet-core": "1.54.1", - "@shapeshiftoss/hdwallet-keepkey": "1.54.1", - "@shapeshiftoss/hdwallet-keepkey-webusb": "1.54.1", - "@shapeshiftoss/hdwallet-keplr": "1.54.1", - "@shapeshiftoss/hdwallet-ledger": "1.54.1", - "@shapeshiftoss/hdwallet-ledger-webusb": "1.54.1", - "@shapeshiftoss/hdwallet-metamask": "1.54.1", - "@shapeshiftoss/hdwallet-native": "1.54.1", - "@shapeshiftoss/hdwallet-native-vault": "1.54.1", - "@shapeshiftoss/hdwallet-shapeshift-multichain": "1.54.1", - "@shapeshiftoss/hdwallet-walletconnectv2": "1.54.1", - "@shapeshiftoss/hdwallet-xdefi": "1.54.1", + "@shapeshiftoss/hdwallet-coinbase": "1.54.2", + "@shapeshiftoss/hdwallet-core": "1.54.2", + "@shapeshiftoss/hdwallet-keepkey": "1.54.2", + "@shapeshiftoss/hdwallet-keepkey-webusb": "1.54.2", + "@shapeshiftoss/hdwallet-keplr": "1.54.2", + "@shapeshiftoss/hdwallet-ledger": "1.54.2", + "@shapeshiftoss/hdwallet-ledger-webusb": "1.54.2", + "@shapeshiftoss/hdwallet-metamask": "1.54.2", + "@shapeshiftoss/hdwallet-native": "1.54.2", + "@shapeshiftoss/hdwallet-native-vault": "1.54.2", + "@shapeshiftoss/hdwallet-shapeshift-multichain": "1.54.2", + "@shapeshiftoss/hdwallet-walletconnectv2": "1.54.2", + "@shapeshiftoss/hdwallet-xdefi": "1.54.2", "@shapeshiftoss/swapper": "workspace:^", "@shapeshiftoss/types": "workspace:^", "@shapeshiftoss/unchained-client": "workspace:^", diff --git a/packages/chain-adapters/package.json b/packages/chain-adapters/package.json index ee7c1f2d926..226578dcef8 100644 --- a/packages/chain-adapters/package.json +++ b/packages/chain-adapters/package.json @@ -20,6 +20,7 @@ "@shapeshiftoss/caip": "workspace:^", "@shapeshiftoss/types": "workspace:^", "@shapeshiftoss/unchained-client": "workspace:^", + "@shapeshiftoss/utils": "workspace:^", "bech32": "^2.0.0", "coinselect": "^3.1.13", "multicoin-address-validator": "^0.5.12", diff --git a/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts b/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts index ced038c960b..7dc942656d1 100644 --- a/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts +++ b/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts @@ -1,6 +1,6 @@ import type { AssetId, ChainId } from '@shapeshiftoss/caip' import { fromChainId, generateAssetIdFromCosmosSdkDenom } from '@shapeshiftoss/caip' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, CosmosSdkChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import * as unchained from '@shapeshiftoss/unchained-client' import { bech32 } from 'bech32' @@ -75,8 +75,6 @@ export const cosmosSdkChainIds = [ KnownChainIds.ThorchainMainnet, ] as const -export type CosmosSdkChainId = (typeof cosmosSdkChainIds)[number] - export type CosmosSdkChainAdapter = cosmos.ChainAdapter | thorchain.ChainAdapter export enum Denoms { diff --git a/packages/chain-adapters/src/cosmossdk/index.ts b/packages/chain-adapters/src/cosmossdk/index.ts index 30f3678ec45..591831e5a31 100644 --- a/packages/chain-adapters/src/cosmossdk/index.ts +++ b/packages/chain-adapters/src/cosmossdk/index.ts @@ -1,4 +1,4 @@ -export type { CosmosSdkChainId, CosmosSdkChainAdapter } from './CosmosSdkBaseAdapter' +export type { CosmosSdkChainAdapter } from './CosmosSdkBaseAdapter' export { cosmosSdkChainIds, CosmosSdkBaseAdapter } from './CosmosSdkBaseAdapter' export * as cosmossdk from './types' diff --git a/packages/chain-adapters/src/cosmossdk/types.ts b/packages/chain-adapters/src/cosmossdk/types.ts index 70ed18348c8..e62b5102fbd 100644 --- a/packages/chain-adapters/src/cosmossdk/types.ts +++ b/packages/chain-adapters/src/cosmossdk/types.ts @@ -1,8 +1,8 @@ import type { AssetId } from '@shapeshiftoss/caip' +import type { CosmosSdkChainId } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import type * as types from '../types' -import type { CosmosSdkChainId } from './CosmosSdkBaseAdapter' export type Account = { sequence: string diff --git a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts index 331dd85839b..9a620f7bbd3 100644 --- a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts +++ b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts @@ -18,7 +18,7 @@ import { supportsOptimism, supportsPolygon, } from '@shapeshiftoss/hdwallet-core' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import BigNumber from 'bignumber.js' @@ -67,8 +67,8 @@ import type { BuildCustomApiTxInput, BuildCustomTxInput, EstimateGasRequest, - Fees, GasFeeDataEstimate, + NetworkFees, } from './types' import { getErc20Data } from './utils' @@ -84,8 +84,6 @@ export const evmChainIds = [ KnownChainIds.BaseMainnet, ] as const -export type EvmChainId = (typeof evmChainIds)[number] - export type EvmChainAdapter = | ethereum.ChainAdapter | avalanche.ChainAdapter @@ -284,7 +282,7 @@ export abstract class EvmBaseAdapter implements IChainAdap const isTokenSend = !!contractAddress - const fees = ((): Fees => { + const fees = ((): NetworkFees => { if (maxFeePerGas && maxPriorityFeePerGas) { return { maxFeePerGas: toHex(BigInt(maxFeePerGas)), @@ -570,7 +568,7 @@ export abstract class EvmBaseAdapter implements IChainAdap const account = await this.getAccount(from) - const fees: Fees = + const fees: NetworkFees = maxFeePerGas && maxPriorityFeePerGas ? { maxFeePerGas: toHex(BigInt(maxFeePerGas)), diff --git a/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts b/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts index c7a00bad3d3..9b774a9a0f4 100644 --- a/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts @@ -2,7 +2,7 @@ import { arbitrumAssetId, arbitrumChainId, ASSET_REFERENCE, fromChainId } from ' import type { ETHSignMessage, ETHSignTx, ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import { merge } from 'lodash' @@ -12,7 +12,7 @@ import { describe, expect, it, vi } from 'vitest' import type { BuildSendTxInput, GetFeeDataInput, SignMessageInput, SignTxInput } from '../../types' import { ValidAddressResultType } from '../../types' import { toAddressNList } from '../../utils' -import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' +import type { ChainAdapterArgs } from '../EvmBaseAdapter' import * as arbitrum from './ArbitrumChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/chain-adapters/src/evm/arbitrumNova/ArbitrumNovaChainAdapter.test.ts b/packages/chain-adapters/src/evm/arbitrumNova/ArbitrumNovaChainAdapter.test.ts index db8f57712db..7ad7621e9af 100644 --- a/packages/chain-adapters/src/evm/arbitrumNova/ArbitrumNovaChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/arbitrumNova/ArbitrumNovaChainAdapter.test.ts @@ -7,7 +7,7 @@ import { import type { ETHSignMessage, ETHSignTx, ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import { merge } from 'lodash' @@ -17,7 +17,7 @@ import { describe, expect, it, vi } from 'vitest' import type { BuildSendTxInput, GetFeeDataInput, SignMessageInput, SignTxInput } from '../../types' import { ValidAddressResultType } from '../../types' import { toAddressNList } from '../../utils' -import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' +import type { ChainAdapterArgs } from '../EvmBaseAdapter' import * as arbitrumNova from './ArbitrumNovaChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts b/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts index b12387485d2..3bd6c45d619 100644 --- a/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts @@ -7,7 +7,7 @@ import { import type { ETHSignMessage, ETHSignTx, ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import { merge } from 'lodash' @@ -17,7 +17,6 @@ import { describe, expect, it, vi } from 'vitest' import type { BuildSendTxInput, GetFeeDataInput, SignMessageInput, SignTxInput } from '../../types' import { ValidAddressResultType } from '../../types' import { toAddressNList } from '../../utils' -import type { EvmChainId } from '../EvmBaseAdapter' import * as avalanche from './AvalancheChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/chain-adapters/src/evm/base/BaseChainAdapter.test.ts b/packages/chain-adapters/src/evm/base/BaseChainAdapter.test.ts index 8a37b382ff3..49bb4808abc 100644 --- a/packages/chain-adapters/src/evm/base/BaseChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/base/BaseChainAdapter.test.ts @@ -2,7 +2,7 @@ import { ASSET_REFERENCE, baseAssetId, baseChainId, fromChainId } from '@shapesh import type { ETHSignMessage, ETHSignTx, ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import { merge } from 'lodash' @@ -12,7 +12,7 @@ import { describe, expect, it, vi } from 'vitest' import type { BuildSendTxInput, GetFeeDataInput, SignMessageInput, SignTxInput } from '../../types' import { ValidAddressResultType } from '../../types' import { toAddressNList } from '../../utils' -import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' +import type { ChainAdapterArgs } from '../EvmBaseAdapter' import * as base from './BaseChainAdapter' const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' diff --git a/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts b/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts index 61b93cf77af..bdd2b1ae5e4 100644 --- a/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts @@ -2,7 +2,7 @@ import { ASSET_REFERENCE, bscAssetId, bscChainId, fromChainId } from '@shapeshif import type { ETHSignMessage, ETHSignTx, ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import { merge } from 'lodash' @@ -12,7 +12,6 @@ import { describe, expect, it, vi } from 'vitest' import type { BuildSendTxInput, GetFeeDataInput, SignMessageInput, SignTxInput } from '../../types' import { ValidAddressResultType } from '../../types' import { toAddressNList } from '../../utils' -import type { EvmChainId } from '../EvmBaseAdapter' import * as bsc from './BscChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts b/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts index 32dc3be300f..8e6cab5e164 100644 --- a/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts @@ -2,7 +2,7 @@ import { ASSET_REFERENCE, CHAIN_REFERENCE, ethAssetId, ethChainId } from '@shape import type { ETHSignMessage, ETHSignTx, ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import { merge } from 'lodash' @@ -12,7 +12,6 @@ import { describe, expect, it, vi } from 'vitest' import type { BuildSendTxInput, GetFeeDataInput, SignMessageInput, SignTxInput } from '../../types' import { ValidAddressResultType } from '../../types' import { toAddressNList } from '../../utils' -import type { EvmChainId } from '../EvmBaseAdapter' import * as ethereum from './EthereumChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/chain-adapters/src/evm/evm.ts b/packages/chain-adapters/src/evm/evm.ts new file mode 100644 index 00000000000..56b538dc1b7 --- /dev/null +++ b/packages/chain-adapters/src/evm/evm.ts @@ -0,0 +1,2 @@ +export * from './utils' +export * from './types' diff --git a/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts b/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts index 1649ca3c0ce..47b67dfb474 100644 --- a/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts @@ -8,7 +8,7 @@ import { ASSET_REFERENCE, fromChainId, gnosisAssetId, gnosisChainId } from '@sha import type { ETHSignMessage, ETHSignTx, ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import { merge } from 'lodash' @@ -18,7 +18,7 @@ import { describe, expect, it, vi } from 'vitest' import type { BuildSendTxInput, GetFeeDataInput, SignMessageInput, SignTxInput } from '../../types' import { ValidAddressResultType } from '../../types' import { toAddressNList } from '../../utils' -import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' +import type { ChainAdapterArgs } from '../EvmBaseAdapter' import * as gnosis from './GnosisChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/chain-adapters/src/evm/index.ts b/packages/chain-adapters/src/evm/index.ts index 3c9a7594f3f..ccb0930d5d8 100644 --- a/packages/chain-adapters/src/evm/index.ts +++ b/packages/chain-adapters/src/evm/index.ts @@ -1,7 +1,7 @@ -export type { EvmChainId, EvmChainAdapter } from './EvmBaseAdapter' +export type { EvmChainAdapter } from './EvmBaseAdapter' export { isEvmChainId, evmChainIds, EvmBaseAdapter } from './EvmBaseAdapter' -export * as evm from './types' +export * as evm from './evm' export * as ethereum from './ethereum' export * as avalanche from './avalanche' diff --git a/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts b/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts index 11c9fc02c9c..f40fbafb529 100644 --- a/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts @@ -2,7 +2,7 @@ import { ASSET_REFERENCE, fromChainId, optimismAssetId, optimismChainId } from ' import type { ETHSignMessage, ETHSignTx, ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import { merge } from 'lodash' @@ -12,7 +12,7 @@ import { describe, expect, it, vi } from 'vitest' import type { BuildSendTxInput, GetFeeDataInput, SignMessageInput, SignTxInput } from '../../types' import { ValidAddressResultType } from '../../types' import { toAddressNList } from '../../utils' -import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' +import type { ChainAdapterArgs } from '../EvmBaseAdapter' import * as optimism from './OptimismChainAdapter' const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' diff --git a/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts b/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts index 771562826c3..0a439504404 100644 --- a/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts @@ -2,7 +2,7 @@ import { ASSET_REFERENCE, fromChainId, polygonAssetId, polygonChainId } from '@s import type { ETHSignMessage, ETHSignTx, ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import { merge } from 'lodash' @@ -12,7 +12,7 @@ import { describe, expect, it, vi } from 'vitest' import type { BuildSendTxInput, GetFeeDataInput, SignMessageInput, SignTxInput } from '../../types' import { ValidAddressResultType } from '../../types' import { toAddressNList } from '../../utils' -import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' +import type { ChainAdapterArgs } from '../EvmBaseAdapter' import * as polygon from './PolygonChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/chain-adapters/src/evm/types.ts b/packages/chain-adapters/src/evm/types.ts index 1ac38e4c1ca..2a3ffd8dc55 100644 --- a/packages/chain-adapters/src/evm/types.ts +++ b/packages/chain-adapters/src/evm/types.ts @@ -23,7 +23,7 @@ export type BuildCustomTxInput = { data: string value: string gasLimit: string -} & Fees +} & NetworkFees export type BuildCustomApiTxInput = Omit & { from: string } @@ -32,7 +32,7 @@ export type BuildTxInput = { contractAddress?: string data?: string sendmax?: never -} & Fees & +} & NetworkFees & L1FeeData export type EstimateFeeDataInput = common.GetFeeDataInput & { @@ -58,7 +58,7 @@ export type FeeData = { maxPriorityFeePerGas?: string } & L1FeeData -export type Fees = +export type NetworkFees = | { gasPrice: string maxFeePerGas?: never @@ -86,3 +86,8 @@ export type GetFeeDataInput = { } export type TransactionMetadata = unchained.evm.TxMetadata + +export type Fees = NetworkFees & { + gasLimit: string + networkFeeCryptoBaseUnit: string +} diff --git a/packages/chain-adapters/src/evm/utils.ts b/packages/chain-adapters/src/evm/utils.ts index 4a8344ef4ee..a31a996b50e 100644 --- a/packages/chain-adapters/src/evm/utils.ts +++ b/packages/chain-adapters/src/evm/utils.ts @@ -1,6 +1,9 @@ import { Contract } from '@ethersproject/contracts' +import { bn, bnOrZero } from '@shapeshiftoss/utils' import erc20Abi from './erc20Abi.json' +import type { EvmChainAdapter } from './EvmBaseAdapter' +import type { FeeData, Fees } from './types' export const getErc20Data = async ( to: string, @@ -12,3 +15,60 @@ export const getErc20Data = async ( const { data: callData } = await erc20Contract.populateTransaction.transfer(to, value) return callData || '' } + +type CalcNetworkFeeCryptoBaseUnitArgs = FeeData & { + supportsEIP1559: boolean +} + +export const calcNetworkFeeCryptoBaseUnit = (args: CalcNetworkFeeCryptoBaseUnitArgs) => { + const { + supportsEIP1559, + gasLimit, + gasPrice, + l1GasLimit, + l1GasPrice, + maxFeePerGas, + maxPriorityFeePerGas, + } = args + + // l1 fee if exists or 0 + const l1Fee = bnOrZero(l1GasPrice).times(bnOrZero(l1GasLimit)) + + // eip1559 fees + if (supportsEIP1559 && maxFeePerGas && maxPriorityFeePerGas) { + return bn(gasLimit).times(maxFeePerGas).plus(l1Fee).toFixed(0) + } + + // legacy fees + return bn(gasLimit).times(gasPrice).plus(l1Fee).toFixed(0) +} + +export type GetFeesArgs = { + adapter: EvmChainAdapter + data: string + to: string + value: string + from: string + supportsEIP1559: boolean +} + +export const getFees = async (args: GetFeesArgs): Promise => { + const { adapter, data, to, value, from, supportsEIP1559 } = args + + const { + average: { chainSpecific: feeData }, + } = await adapter.getFeeData({ to, value, chainSpecific: { from, data } }) + + const networkFeeCryptoBaseUnit = calcNetworkFeeCryptoBaseUnit({ + ...feeData, + supportsEIP1559, + }) + + const { gasLimit, gasPrice, maxFeePerGas, maxPriorityFeePerGas } = feeData + + if (supportsEIP1559 && maxFeePerGas && maxPriorityFeePerGas) { + return { networkFeeCryptoBaseUnit, gasLimit, maxFeePerGas, maxPriorityFeePerGas } + } + + return { networkFeeCryptoBaseUnit, gasLimit, gasPrice } +} diff --git a/packages/chain-adapters/src/types.ts b/packages/chain-adapters/src/types.ts index c258848f521..29cc7a7e4fe 100644 --- a/packages/chain-adapters/src/types.ts +++ b/packages/chain-adapters/src/types.ts @@ -6,16 +6,18 @@ import type { HDWallet, ThorchainSignTx, } from '@shapeshiftoss/hdwallet-core' -import type { ChainSpecific, KnownChainIds, UtxoAccountType } from '@shapeshiftoss/types' +import type { + ChainSpecific, + KnownChainIds, + UtxoAccountType, + UtxoChainId, +} from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import type PQueue from 'p-queue' -import * as cosmossdk from './cosmossdk/types' -import * as evm from './evm/types' -import type { UtxoChainId } from './utxo' -import * as utxo from './utxo/types' - -export { cosmossdk, evm, utxo } +import type * as cosmossdk from './cosmossdk/types' +import type * as evm from './evm/types' +import type * as utxo from './utxo/types' // this placeholder forces us to be explicit about transactions not transferring funds to humans export type ContractInteraction = Nominal<'contract-interaction', 'ContractInteraction'> diff --git a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts index d48e07c8859..61c00baee13 100644 --- a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts +++ b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts @@ -10,7 +10,7 @@ import { BTCOutputAddressType, supportsBTC, } from '@shapeshiftoss/hdwallet-core' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, UtxoChainId } from '@shapeshiftoss/types' import { KnownChainIds, UtxoAccountType } from '@shapeshiftoss/types' import type * as unchained from '@shapeshiftoss/unchained-client' import WAValidator from 'multicoin-address-validator' @@ -61,8 +61,6 @@ export const utxoChainIds = [ KnownChainIds.LitecoinMainnet, ] as const -export type UtxoChainId = (typeof utxoChainIds)[number] - export type UtxoChainAdapter = | bitcoin.ChainAdapter | bitcoincash.ChainAdapter diff --git a/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts index ad033e26c3f..70ff1dcb56d 100644 --- a/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts @@ -2,13 +2,13 @@ import { btcAssetId, btcChainId } from '@shapeshiftoss/caip' import type { BTCWallet, HDWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, UtxoChainId } from '@shapeshiftoss/types' import { KnownChainIds, UtxoAccountType } from '@shapeshiftoss/types' import { TransferType, TxStatus } from '@shapeshiftoss/unchained-client' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { Account, BuildSendTxInput, GetFeeDataInput, TxHistoryResponse } from '../../types' -import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' +import type { ChainAdapterArgs } from '../UtxoBaseAdapter' import * as bitcoin from './BitcoinChainAdapter' import manyInputsManyOutputReceive from './mockData/manyInputsManyOutputReceive' import manyInputsManyOutputSendNoChange from './mockData/manyInputsManyOutputSendNoChange' diff --git a/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts b/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts index 43068969758..8e2f9aee579 100644 --- a/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts @@ -7,12 +7,12 @@ import { } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, UtxoChainId } from '@shapeshiftoss/types' import { KnownChainIds, UtxoAccountType } from '@shapeshiftoss/types' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' -import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' +import type { ChainAdapterArgs } from '../UtxoBaseAdapter' import * as bitcoincash from './BitcoinCashChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts index 00bbeb6fcb8..a1a275d3c2f 100644 --- a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts @@ -1,12 +1,12 @@ import type { HDWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, UtxoChainId } from '@shapeshiftoss/types' import { KnownChainIds, UtxoAccountType } from '@shapeshiftoss/types' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' -import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' +import type { ChainAdapterArgs } from '../UtxoBaseAdapter' import * as dogecoin from './DogecoinChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/chain-adapters/src/utxo/index.ts b/packages/chain-adapters/src/utxo/index.ts index af8e360f9e0..0b342a7c7dd 100644 --- a/packages/chain-adapters/src/utxo/index.ts +++ b/packages/chain-adapters/src/utxo/index.ts @@ -1,4 +1,4 @@ -export type { UtxoChainId, UtxoChainAdapter } from './UtxoBaseAdapter' +export type { UtxoChainAdapter } from './UtxoBaseAdapter' export { utxoChainIds, UtxoBaseAdapter } from './UtxoBaseAdapter' export * as utxo from './types' diff --git a/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts index b00dd6dfdf0..cb18c55e536 100644 --- a/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts @@ -2,12 +2,12 @@ import { ltcAssetId, ltcChainId } from '@shapeshiftoss/caip' import type { HDWallet } from '@shapeshiftoss/hdwallet-core' import type { NativeAdapterArgs } from '@shapeshiftoss/hdwallet-native' import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' -import type { BIP44Params } from '@shapeshiftoss/types' +import type { BIP44Params, UtxoChainId } from '@shapeshiftoss/types' import { KnownChainIds, UtxoAccountType } from '@shapeshiftoss/types' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' -import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' +import type { ChainAdapterArgs } from '../UtxoBaseAdapter' import * as litecoin from './LitecoinChainAdapter' vi.mock('../../utils/validateAddress', () => ({ diff --git a/packages/contracts/package.json b/packages/contracts/package.json new file mode 100644 index 00000000000..afa2e21e28c --- /dev/null +++ b/packages/contracts/package.json @@ -0,0 +1,24 @@ +{ + "name": "@shapeshiftoss/contracts", + "packageManager": "yarn@3.5.0", + "repository": "https://github.com/shapeshift/web", + "license": "MIT", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "yarn clean && yarn run -T tsc --build", + "clean": "rm -rf dist tsconfig.tsbuildinfo", + "dev": "yarn run -T tsc --build --watch" + }, + "dependencies": { + "@shapeshiftoss/caip": "workspace:^", + "@shapeshiftoss/types": "workspace:^", + "@shapeshiftoss/utils": "workspace:^" + } +} diff --git a/src/contracts/abis/ERC20ABI.ts b/packages/contracts/src/abis/ERC20ABI.ts similarity index 100% rename from src/contracts/abis/ERC20ABI.ts rename to packages/contracts/src/abis/ERC20ABI.ts diff --git a/src/contracts/abis/FoxStakingV1.ts b/packages/contracts/src/abis/FoxStakingV1.ts similarity index 100% rename from src/contracts/abis/FoxStakingV1.ts rename to packages/contracts/src/abis/FoxStakingV1.ts diff --git a/src/contracts/abis/IUniswapV2Pair.ts b/packages/contracts/src/abis/IUniswapV2Pair.ts similarity index 100% rename from src/contracts/abis/IUniswapV2Pair.ts rename to packages/contracts/src/abis/IUniswapV2Pair.ts diff --git a/src/contracts/abis/IUniswapV2Router02.ts b/packages/contracts/src/abis/IUniswapV2Router02.ts similarity index 100% rename from src/contracts/abis/IUniswapV2Router02.ts rename to packages/contracts/src/abis/IUniswapV2Router02.ts diff --git a/src/contracts/abis/Outbox.ts b/packages/contracts/src/abis/Outbox.ts similarity index 100% rename from src/contracts/abis/Outbox.ts rename to packages/contracts/src/abis/Outbox.ts diff --git a/src/contracts/abis/THORCHAIN_RouterABI.ts b/packages/contracts/src/abis/THORCHAIN_RouterABI.ts similarity index 100% rename from src/contracts/abis/THORCHAIN_RouterABI.ts rename to packages/contracts/src/abis/THORCHAIN_RouterABI.ts diff --git a/src/contracts/abis/farmingAbi.ts b/packages/contracts/src/abis/farmingAbi.ts similarity index 100% rename from src/contracts/abis/farmingAbi.ts rename to packages/contracts/src/abis/farmingAbi.ts diff --git a/packages/contracts/src/abis/index.ts b/packages/contracts/src/abis/index.ts new file mode 100644 index 00000000000..299c256c233 --- /dev/null +++ b/packages/contracts/src/abis/index.ts @@ -0,0 +1,7 @@ +export * from './ERC20ABI' +export * from './farmingAbi' +export * from './FoxStakingV1' +export * from './IUniswapV2Pair' +export * from './IUniswapV2Router02' +export * from './Outbox' +export * from './THORCHAIN_RouterABI' diff --git a/packages/contracts/src/constants.ts b/packages/contracts/src/constants.ts new file mode 100644 index 00000000000..a9d31d8a028 --- /dev/null +++ b/packages/contracts/src/constants.ts @@ -0,0 +1,83 @@ +import type { Address } from 'viem' + +import { erc20ABI } from './abis/ERC20ABI' +import { FarmingABI } from './abis/farmingAbi' +import { IUniswapV2Pair } from './abis/IUniswapV2Pair' +import { IUniswapV2Router02 } from './abis/IUniswapV2Router02' +import { THORChain_RouterABI } from './abis/THORCHAIN_RouterABI' +import { ContractType } from './types' + +export const WETH_TOKEN_CONTRACT_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' +export const FOX_TOKEN_CONTRACT_ADDRESS = '0xc770EEfAd204B5180dF6a14Ee197D99d808ee52d' + +// Staking contract addresses as string literals +export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V1 = + '0xdd80e21669a664bce83e3ad9a0d74f8dad5d9e72' as const +export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V2 = + '0xc54b9f82c1c54e9d4d274d633c7523f2299c42a0' as const +export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V3 = + '0x212ebf9fd3c10f371557b08e993eaab385c3932b' as const +export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V4 = + '0x24fd7fb95dc742e23dc3829d3e656feeb5f67fa0' as const +export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V5 = + '0xc14eaa8284feff79edc118e06cadbf3813a7e555' as const +export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V6 = + '0xebb1761ad43034fd7faa64d84e5bbd8cb5c40b68' as const +export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V7 = + '0x5939783dbf3e9f453a69bc9ddc1e492efac1fbcb' as const +export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V8 = + '0x662da6c777a258382f08b979d9489c3fbbbd8ac3' as const +export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V9 = + '0x721720784b76265aa3e34c1c7ba02a6027bcd3e5' as const + +export const foxEthStakingContractAddresses = [ + ETH_FOX_STAKING_CONTRACT_ADDRESS_V9, + ETH_FOX_STAKING_CONTRACT_ADDRESS_V8, + ETH_FOX_STAKING_CONTRACT_ADDRESS_V7, + ETH_FOX_STAKING_CONTRACT_ADDRESS_V6, + ETH_FOX_STAKING_CONTRACT_ADDRESS_V5, + ETH_FOX_STAKING_CONTRACT_ADDRESS_V4, + ETH_FOX_STAKING_CONTRACT_ADDRESS_V3, + ETH_FOX_STAKING_CONTRACT_ADDRESS_V2, + ETH_FOX_STAKING_CONTRACT_ADDRESS_V1, +] as const + +// Exported as a string literal for contract address discrimination purposes +export const ETH_FOX_POOL_CONTRACT_ADDRESS = '0x470e8de2eBaef52014A47Cb5E6aF86884947F08c' as const + +// Exported as a string literal for contract address discrimination purposes +export const UNISWAP_V2_ROUTER_02_CONTRACT_ADDRESS = + '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' as const + +export const THOR_ROUTER_CONTRACT_ADDRESS_ETHEREUM = '0xd37bbe5744d730a1d98d8dc97c42f0ca46ad7146' + +// RFOX on Arbitrum ERC1967Proxy contract address +// Uncomment me if you want to test RFOX with a shorter cooldown period +// export const RFOX_PROXY_CONTRACT_ADDRESS: Address = '0x1094c4a99fce60e69ffe75849309408f1262d304' +export const RFOX_PROXY_CONTRACT_ADDRESS: Address = '0xac2a4fd70bcd8bab0662960455c363735f0e2b56' + +export const RFOX_REWARD_RATE = 1n * 10n ** 27n +export const RFOX_WAD = 1n * 10n ** 18n + +export const CONTRACT_ADDRESS_TO_ABI = { + [ETH_FOX_POOL_CONTRACT_ADDRESS]: IUniswapV2Pair, + [ETH_FOX_STAKING_CONTRACT_ADDRESS_V1]: FarmingABI, + [ETH_FOX_STAKING_CONTRACT_ADDRESS_V2]: FarmingABI, + [ETH_FOX_STAKING_CONTRACT_ADDRESS_V3]: FarmingABI, + [ETH_FOX_STAKING_CONTRACT_ADDRESS_V4]: FarmingABI, + [ETH_FOX_STAKING_CONTRACT_ADDRESS_V5]: FarmingABI, + [ETH_FOX_STAKING_CONTRACT_ADDRESS_V6]: FarmingABI, + [ETH_FOX_STAKING_CONTRACT_ADDRESS_V7]: FarmingABI, + [ETH_FOX_STAKING_CONTRACT_ADDRESS_V8]: FarmingABI, + [ETH_FOX_STAKING_CONTRACT_ADDRESS_V9]: FarmingABI, + [FOX_TOKEN_CONTRACT_ADDRESS]: erc20ABI, + [UNISWAP_V2_ROUTER_02_CONTRACT_ADDRESS]: IUniswapV2Router02, + // THOR Router Mainnet + [THOR_ROUTER_CONTRACT_ADDRESS_ETHEREUM]: THORChain_RouterABI, +} as const + +export const CONTRACT_TYPE_TO_ABI = { + [ContractType.UniV2Pair]: IUniswapV2Pair, + [ContractType.ERC20]: erc20ABI, + [ContractType.ThorRouter]: THORChain_RouterABI, +} as const diff --git a/packages/contracts/src/contractManager.ts b/packages/contracts/src/contractManager.ts new file mode 100644 index 00000000000..283b3058199 --- /dev/null +++ b/packages/contracts/src/contractManager.ts @@ -0,0 +1,122 @@ +import type { AssetId, ChainId } from '@shapeshiftoss/caip' +import { fromAssetId, toAssetId } from '@shapeshiftoss/caip' +import type { PartialRecord } from '@shapeshiftoss/types' +import { KnownChainIds } from '@shapeshiftoss/types' +import type { Token } from '@uniswap/sdk' +import { Fetcher } from '@uniswap/sdk' +import assert from 'assert' +import memoize from 'lodash/memoize' +import type { Address } from 'viem' +import { getAddress, getContract } from 'viem' + +import { CONTRACT_ADDRESS_TO_ABI, CONTRACT_TYPE_TO_ABI } from './constants' +import { getEthersV5Provider } from './ethersProviderSingleton' +import type { + KnownContract, + KnownContractAbiByAddress, + KnownContractAddress, + KnownContractByAddress, + KnownContractByType, +} from './types' +import { ContractType } from './types' +import { viemClientByChainId, viemEthMainnetClient } from './viemClient' + +const definedContracts: PartialRecord = {} + +export const getOrCreateContractByAddress: ( + address: A, +) => KnownContractByAddress = ( + address: A, +): KnownContractByAddress => { + const checksumAddress = getAddress(address) as A + const definedContract = definedContracts[checksumAddress] + if (definedContract !== undefined) { + return definedContract as unknown as KnownContractByAddress + } + const contractAbi: KnownContractAbiByAddress = CONTRACT_ADDRESS_TO_ABI[address] + + const contract = getContract({ + abi: contractAbi, + address: checksumAddress, + client: viemEthMainnetClient, + }) + definedContracts[checksumAddress] = contract as unknown as KnownContract + return contract as KnownContractByAddress +} + +export const getOrCreateContractByType: ({ + address, + type, + chainId, +}: { + address: string | `0x${string}` + type: T + chainId: ChainId +}) => KnownContractByType = ({ + address, + type, + chainId, +}: { + address: string | `0x${string}` + type: T + chainId: ChainId +}): KnownContractByType => { + const checksumAddress = getAddress(address) as A + const definedContract = definedContracts[checksumAddress] + if (definedContract !== undefined) { + return definedContract as unknown as KnownContractByType + } + + const publicClient = viemClientByChainId[chainId] + assert(publicClient !== undefined, `no public client found for chainId '${chainId}'`) + + const contract = getContract({ + abi: CONTRACT_TYPE_TO_ABI[type], + address: checksumAddress, + client: publicClient, + }) + definedContracts[checksumAddress] = contract as unknown as KnownContract + return contract as unknown as KnownContractByType +} + +export const fetchUniV2PairData = memoize(async (pairAssetId: AssetId) => { + const { assetReference, chainId } = fromAssetId(pairAssetId) + const checksumAddress = getAddress(assetReference) + const pair = getOrCreateContractByType({ + address: checksumAddress, + type: ContractType.UniV2Pair, + chainId: KnownChainIds.EthereumMainnet, + }) + const ethersV5Provider = getEthersV5Provider() + + const token0Address = await pair.read.token0() + const token1Address = await pair.read.token1() + const token0AssetId = toAssetId({ + chainId, + assetNamespace: 'erc20', + assetReference: token0Address, + }) + const token1AssetId = toAssetId({ + chainId, + assetNamespace: 'erc20', + assetReference: token1Address, + }) + + const { chainReference: asset0EvmChainId, assetReference: asset0Address } = + fromAssetId(token0AssetId) + const { chainReference: asset1EvmChainId, assetReference: asset1Address } = + fromAssetId(token1AssetId) + + const token0: Token = await Fetcher.fetchTokenData( + Number(asset0EvmChainId), + asset0Address, + ethersV5Provider, + ) + const token1: Token = await Fetcher.fetchTokenData( + Number(asset1EvmChainId), + asset1Address, + ethersV5Provider, + ) + + return Fetcher.fetchPairData(token0, token1, ethersV5Provider) +}) diff --git a/src/lib/ethersProviderSingleton.ts b/packages/contracts/src/ethersProviderSingleton.ts similarity index 50% rename from src/lib/ethersProviderSingleton.ts rename to packages/contracts/src/ethersProviderSingleton.ts index 16995f5b9eb..c8e38f7fdeb 100644 --- a/src/lib/ethersProviderSingleton.ts +++ b/packages/contracts/src/ethersProviderSingleton.ts @@ -1,35 +1,41 @@ import type { ChainId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import type { EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' -import { getConfig } from 'config' +import { assertUnreachable } from '@shapeshiftoss/utils' import { JsonRpcProvider } from 'ethers' import { ethers as ethersV5 } from 'ethers5' -import { assertUnreachable } from './utils' - export const rpcUrlByChainId = (chainId: EvmChainId): string => { - switch (chainId) { - case KnownChainIds.AvalancheMainnet: - return getConfig().REACT_APP_AVALANCHE_NODE_URL - case KnownChainIds.OptimismMainnet: - return getConfig().REACT_APP_OPTIMISM_NODE_URL - case KnownChainIds.BnbSmartChainMainnet: - return getConfig().REACT_APP_BNBSMARTCHAIN_NODE_URL - case KnownChainIds.PolygonMainnet: - return getConfig().REACT_APP_POLYGON_NODE_URL - case KnownChainIds.GnosisMainnet: - return getConfig().REACT_APP_GNOSIS_NODE_URL - case KnownChainIds.EthereumMainnet: - return getConfig().REACT_APP_ETHEREUM_NODE_URL - case KnownChainIds.ArbitrumMainnet: - return getConfig().REACT_APP_ARBITRUM_NODE_URL - case KnownChainIds.ArbitrumNovaMainnet: - return getConfig().REACT_APP_ARBITRUM_NOVA_NODE_URL - case KnownChainIds.BaseMainnet: - return getConfig().REACT_APP_BASE_NODE_URL - default: - assertUnreachable(chainId) + const url = (() => { + switch (chainId) { + case KnownChainIds.AvalancheMainnet: + return process.env.REACT_APP_AVALANCHE_NODE_URL + case KnownChainIds.OptimismMainnet: + return process.env.REACT_APP_OPTIMISM_NODE_URL + case KnownChainIds.BnbSmartChainMainnet: + return process.env.REACT_APP_BNBSMARTCHAIN_NODE_URL + case KnownChainIds.PolygonMainnet: + return process.env.REACT_APP_POLYGON_NODE_URL + case KnownChainIds.GnosisMainnet: + return process.env.REACT_APP_GNOSIS_NODE_URL + case KnownChainIds.EthereumMainnet: + return process.env.REACT_APP_ETHEREUM_NODE_URL + case KnownChainIds.ArbitrumMainnet: + return process.env.REACT_APP_ARBITRUM_NODE_URL + case KnownChainIds.ArbitrumNovaMainnet: + return process.env.REACT_APP_ARBITRUM_NOVA_NODE_URL + case KnownChainIds.BaseMainnet: + return process.env.REACT_APP_BASE_NODE_URL + default: + assertUnreachable(chainId) + } + })() + + if (!url) { + throw new Error(`No RPC URL found for chainId ${chainId}`) } + + return url } const ethersProviders: Map = new Map() diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts new file mode 100644 index 00000000000..295fe43e455 --- /dev/null +++ b/packages/contracts/src/index.ts @@ -0,0 +1,6 @@ +export * from './constants' +export * from './types' +export * from './contractManager' +export * from './ethersProviderSingleton' +export * from './abis' +export * from './viemClient' diff --git a/packages/contracts/src/types.ts b/packages/contracts/src/types.ts new file mode 100644 index 00000000000..754a49a86df --- /dev/null +++ b/packages/contracts/src/types.ts @@ -0,0 +1,48 @@ +import type { GetContractReturnType, PublicClient } from 'viem' + +import type { + CONTRACT_ADDRESS_TO_ABI, + CONTRACT_TYPE_TO_ABI, + ETH_FOX_POOL_CONTRACT_ADDRESS, + FOX_TOKEN_CONTRACT_ADDRESS, + foxEthStakingContractAddresses, + THOR_ROUTER_CONTRACT_ADDRESS_ETHEREUM, + UNISWAP_V2_ROUTER_02_CONTRACT_ADDRESS, +} from './constants' + +export enum ContractType { + UniV2Pair = 'UniV2Pair', + ERC20 = 'ERC20', + ThorRouter = 'ThorRouter', +} + +export type KnownContractAbiByAddress = + (typeof CONTRACT_ADDRESS_TO_ABI)[T] +export type KnownContractAbiByType = (typeof CONTRACT_TYPE_TO_ABI)[T] + +export type KnownContractByAddress = GetContractReturnType< + KnownContractAbiByAddress, + PublicClient, + T +> + +export type KnownContract = KnownContractByAddress + +export type KnownContractByType< + A extends KnownContractAddress, + T extends ContractType, +> = GetContractReturnType, PublicClient, A> + +export type KnownContractAddress = + | typeof ETH_FOX_POOL_CONTRACT_ADDRESS + | FoxEthStakingContractAddress + | typeof FOX_TOKEN_CONTRACT_ADDRESS + | typeof UNISWAP_V2_ROUTER_02_CONTRACT_ADDRESS + | typeof THOR_ROUTER_CONTRACT_ADDRESS_ETHEREUM + +export type DefinedContract = { + contract: KnownContractByAddress + address: KnownContractAddress +} + +export type FoxEthStakingContractAddress = (typeof foxEthStakingContractAddresses)[number] diff --git a/src/lib/viem-client.ts b/packages/contracts/src/viemClient.ts similarity index 58% rename from src/lib/viem-client.ts rename to packages/contracts/src/viemClient.ts index 8558d17242a..2d8c89267d7 100644 --- a/src/lib/viem-client.ts +++ b/packages/contracts/src/viemClient.ts @@ -1,9 +1,7 @@ import type { ChainId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' import { KnownChainIds } from '@shapeshiftoss/types' import assert from 'assert' -import { getConfig } from 'config' -import type { Chain, PublicClient, Transport } from 'viem' +import type { PublicClient } from 'viem' import { createPublicClient, fallback, http } from 'viem' import { arbitrum, @@ -19,86 +17,95 @@ import { export const viemEthMainnetClient = createPublicClient({ chain: mainnet, - transport: fallback([ - http(getConfig().REACT_APP_ETHEREUM_NODE_URL), + transport: fallback( // https://github.com/DefiLlama/chainlist/blob/83b8cc32ee79c10e0281e1799ebe4cd1696082b7/constants/llamaNodesRpcs.js - http('https://eth.llamarpc.com'), - ]), -}) + [process.env.REACT_APP_ETHEREUM_NODE_URL, 'https://eth.llamarpc.com'] + .filter(Boolean) + .map(url => http(url)), + ), +}) as PublicClient export const viemBscClient = createPublicClient({ chain: bsc, - transport: fallback([ - http(getConfig().REACT_APP_BNBSMARTCHAIN_NODE_URL), + transport: fallback( // https://github.com/DefiLlama/chainlist/blob/83b8cc32ee79c10e0281e1799ebe4cd1696082b7/constants/llamaNodesRpcs.js#L30 - http('https://binance.llamarpc.com'), - ]), -}) + [process.env.REACT_APP_BNBSMARTCHAIN_NODE_URL, 'https://binance.llamarpc.com'] + .filter(Boolean) + .map(url => http(url)), + ), +}) as PublicClient export const viemAvalancheClient = createPublicClient({ chain: avalanche, - transport: fallback([ - http(getConfig().REACT_APP_AVALANCHE_NODE_URL), + transport: fallback( // https://github.com/DefiLlama/chainlist/blob/83b8cc32ee79c10e0281e1799ebe4cd1696082b7/constants/extraRpcs.js#L937 - http('https://api.avax.network/ext/bc/C/rpc'), - ]), -}) + [process.env.REACT_APP_AVALANCHE_NODE_URL, 'https://api.avax.network/ext/bc/C/rpc'] + .filter(Boolean) + .map(url => http(url)), + ), +}) as PublicClient export const viemArbitrumClient = createPublicClient({ chain: arbitrum, - transport: fallback([ - http(getConfig().REACT_APP_ARBITRUM_NODE_URL), + transport: fallback( // https://github.com/DefiLlama/chainlist/blob/83b8cc32ee79c10e0281e1799ebe4cd1696082b7/constants/llamaNodesRpcs.js#L63 - http('https://arbitrum.llamarpc.com'), - ]), -}) + [process.env.REACT_APP_ARBITRUM_NODE_URL, 'https://arbitrum.llamarpc.com'] + .filter(Boolean) + .map(url => http(url)), + ), +}) as PublicClient export const viemArbitrumNovaClient = createPublicClient({ chain: arbitrumNova, - transport: fallback([ - http(getConfig().REACT_APP_ARBITRUM_NOVA_NODE_URL), + transport: fallback( // https://github.com/DefiLlama/chainlist/blob/83b8cc32ee79c10e0281e1799ebe4cd1696082b7/constants/extraRpcs.js#L1393 - http('https://nova.arbitrum.io/rpc'), - ]), -}) + [process.env.REACT_APP_ARBITRUM_NOVA_NODE_URL, 'https://nova.arbitrum.io/rpc'] + .filter(Boolean) + .map(url => http(url)), + ), +}) as PublicClient export const viemOptimismClient = createPublicClient({ chain: optimism, - transport: fallback([ - http(getConfig().REACT_APP_OPTIMISM_NODE_URL), + transport: fallback( // https://github.com/DefiLlama/chainlist/blob/83b8cc32ee79c10e0281e1799ebe4cd1696082b7/constants/llamaNodesRpcs.js#L41 - http('https://optimism.llamarpc.com'), - ]), -}) + [process.env.REACT_APP_OPTIMISM_NODE_URL, 'https://optimism.llamarpc.com'] + .filter(Boolean) + .map(url => http(url)), + ), +}) as PublicClient export const viemGnosisClient = createPublicClient({ chain: gnosis, - transport: fallback([ + transport: fallback( // https://github.com/DefiLlama/chainlist/blob/83b8cc32ee79c10e0281e1799ebe4cd1696082b7/constants/extraRpcs.js#L1978 - http(getConfig().REACT_APP_GNOSIS_NODE_URL), - http('https://rpc.gnosischain.com'), - ]), -}) + [process.env.REACT_APP_GNOSIS_NODE_URL, 'https://rpc.gnosischain.com'] + .filter(Boolean) + .map(url => http(url)), + ), +}) as PublicClient export const viemPolygonClient = createPublicClient({ chain: polygon, - transport: fallback([ - http(getConfig().REACT_APP_POLYGON_NODE_URL), + transport: fallback( // https://github.com/DefiLlama/chainlist/blob/83b8cc32ee79c10e0281e1799ebe4cd1696082b7/constants/llamaNodesRpcs.js#L52 - http('https://polygon.llamarpc.com'), - ]), -}) + [process.env.REACT_APP_POLYGON_NODE_URL, 'https://polygon.llamarpc.com'] + .filter(Boolean) + .map(url => http(url)), + ), +}) as PublicClient export const viemBaseClient = createPublicClient({ chain: base, - transport: fallback([ - http(getConfig().REACT_APP_BASE_NODE_URL), + transport: fallback( // https://github.com/DefiLlama/chainlist/blob/83b8cc32ee79c10e0281e1799ebe4cd1696082b7/constants/llamaNodesRpcs.js#L19 - http('https://base.llamarpc.com'), - ]), -}) + [process.env.REACT_APP_BASE_NODE_URL, 'https://base.llamarpc.com'] + .filter(Boolean) + .map(url => http(url)), + ), +}) as PublicClient -export const viemClientByChainId: Record> = { +export const viemClientByChainId: Record = { [KnownChainIds.EthereumMainnet]: viemEthMainnetClient, [KnownChainIds.BnbSmartChainMainnet]: viemBscClient, [KnownChainIds.AvalancheMainnet]: viemAvalancheClient, @@ -106,13 +113,11 @@ export const viemClientByChainId: Record, - [KnownChainIds.BaseMainnet]: viemBaseClient as PublicClient, + [KnownChainIds.OptimismMainnet]: viemOptimismClient, + [KnownChainIds.BaseMainnet]: viemBaseClient, } -export const viemClientByNetworkId: Record> = { +export const viemClientByNetworkId: Record = { [mainnet.id]: viemEthMainnetClient, [bsc.id]: viemBscClient, [avalanche.id]: viemAvalancheClient, @@ -120,14 +125,12 @@ export const viemClientByNetworkId: Record, - [base.id]: viemBaseClient as PublicClient, + [optimism.id]: viemOptimismClient, + [base.id]: viemBaseClient, } -export const assertGetViemClient = (chainId: ChainId): PublicClient => { - const publicClient = viemClientByChainId[chainId as EvmChainId] +export const assertGetViemClient = (chainId: ChainId): PublicClient => { + const publicClient = viemClientByChainId[chainId] assert(publicClient !== undefined, `no public client found for chainId '${chainId}'`) return publicClient } diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json new file mode 100644 index 00000000000..10555e18c59 --- /dev/null +++ b/packages/contracts/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": [ + "src/**/*", + "src/**/*.json" + ], + "exclude": [ + "dist" + ], + "references": [ + { + "path": "../types", + }, + { + "path": "../caip", + }, + { + "path": "../utils", + } + ] +} diff --git a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/endpoints.ts b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/endpoints.ts index 18e3321ae6d..4b2c3d57bef 100644 --- a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/endpoints.ts @@ -3,10 +3,11 @@ import { ParentToChildMessageStatus, ParentTransactionReceipt } from '@arbitrum/ import type { Provider } from '@ethersproject/providers' import type { AssetId } from '@shapeshiftoss/caip' import { arbitrumChainId, fromChainId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import { evm } from '@shapeshiftoss/chain-adapters' +import { getEthersV5Provider } from '@shapeshiftoss/contracts' +import type { EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import { TxStatus } from '@shapeshiftoss/unchained-client' -import { getFees } from '@shapeshiftoss/utils/dist/evm' import type { Result } from '@sniptt/monads/build' import type { InterpolationOptions } from 'node-polyglot' @@ -106,7 +107,6 @@ export const arbitrumBridgeApi: SwapperApi = { tradeQuote, supportsEIP1559, assertGetEvmChainAdapter, - getEthersV5Provider, }: GetUnsignedEvmTransactionArgs): Promise => { const step = getHopByIndex(tradeQuote, stepIndex) if (!step) throw new Error(`No hop found for stepIndex ${stepIndex}`) @@ -114,7 +114,7 @@ export const arbitrumBridgeApi: SwapperApi = { const { buyAsset, sellAsset, sellAmountIncludingProtocolFeesCryptoBaseUnit } = step const { receiveAddress } = tradeQuote - const assertion = await assertValidTrade({ buyAsset, sellAsset, getEthersV5Provider }) + const assertion = await assertValidTrade({ buyAsset, sellAsset }) if (assertion.isErr()) throw new Error(assertion.unwrapErr().message) const swap = await fetchArbitrumBridgeSwap({ @@ -126,7 +126,6 @@ export const arbitrumBridgeApi: SwapperApi = { sellAsset, sendAddress: from, assertGetEvmChainAdapter, - getEthersV5Provider, }) const { request } = swap @@ -137,7 +136,7 @@ export const arbitrumBridgeApi: SwapperApi = { txRequest: { data, value, to }, } = request - const feeData = await getFees({ + const feeData = await evm.getFees({ adapter: assertGetEvmChainAdapter(chainId), data: data.toString(), to, @@ -160,7 +159,6 @@ export const arbitrumBridgeApi: SwapperApi = { txHash, chainId, assertGetEvmChainAdapter, - getEthersV5Provider, }): Promise<{ status: TxStatus buyTxHash: string | undefined diff --git a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.test.ts b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.test.ts index ed1bdf6b256..30e8b353f36 100644 --- a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.test.ts +++ b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.test.ts @@ -14,9 +14,16 @@ vi.mock('@arbitrum/sdk', () => ({ getArbitrumNetwork: vi.fn().mockResolvedValue({ chainID: 42161 }), })) -vi.mock('@shapeshiftoss/utils/dist/evm', () => ({ - getFees: vi.fn().mockResolvedValue({ networkFeeCryptoBaseUnit: '42' }), -})) +vi.mock('@shapeshiftoss/chain-adapters', async () => { + const actual = await vi.importActual('@shapeshiftoss/chain-adapters') + + return { + ...actual, + evm: { + getFees: vi.fn().mockResolvedValue({ networkFeeCryptoBaseUnit: '42' }), + }, + } +}) describe('getTradeQuote', () => { const mockAdapter = { diff --git a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.ts b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.ts index af1ad38ed8f..a299ec652b5 100644 --- a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.ts +++ b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote.ts @@ -52,7 +52,7 @@ export const getTradeQuoteWithWallet = async ( export async function getTradeQuote( input: GetEvmTradeQuoteInput, - { assertGetEvmChainAdapter, getEthersV5Provider }: SwapperDeps, + { assertGetEvmChainAdapter }: SwapperDeps, ): Promise> { const { chainId, @@ -65,7 +65,7 @@ export async function getTradeQuote( sendAddress, } = input - const assertion = await assertValidTrade({ buyAsset, sellAsset, getEthersV5Provider }) + const assertion = await assertValidTrade({ buyAsset, sellAsset }) if (assertion.isErr()) return Err(assertion.unwrapErr()) const isDeposit = sellAsset.chainId === ethChainId @@ -86,7 +86,6 @@ export async function getTradeQuote( sendAddress: sendAddress ?? '', receiveAddress, assertGetEvmChainAdapter, - getEthersV5Provider, }) const buyAmountBeforeFeesCryptoBaseUnit = sellAmountIncludingProtocolFeesCryptoBaseUnit diff --git a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/fetchArbitrumBridgeSwap.ts b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/fetchArbitrumBridgeSwap.ts index a82b72edab5..54f41b0e04b 100644 --- a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/fetchArbitrumBridgeSwap.ts +++ b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/fetchArbitrumBridgeSwap.ts @@ -5,11 +5,11 @@ import type { } from '@arbitrum/sdk/dist/lib/dataEntities/transactionRequest' import type { ChainId } from '@shapeshiftoss/caip' import { ethAssetId, ethChainId, fromAssetId } from '@shapeshiftoss/caip' -import type { EvmChainAdapter, EvmChainId } from '@shapeshiftoss/chain-adapters' +import type { EvmChainAdapter } from '@shapeshiftoss/chain-adapters' +import { evm } from '@shapeshiftoss/chain-adapters' +import { getEthersV5Provider } from '@shapeshiftoss/contracts' import { type Asset, KnownChainIds } from '@shapeshiftoss/types' import { assertUnreachable, bn } from '@shapeshiftoss/utils' -import { getFees } from '@shapeshiftoss/utils/dist/evm' -import type { ethers as ethersV5 } from 'ethers5' import { BigNumber } from 'ethers5' import { arbitrum } from 'viem/chains' @@ -24,7 +24,6 @@ export type FetchArbitrumBridgeSwapInput = { sellAsset: Asset sendAddress: string assertGetEvmChainAdapter: (chainId: ChainId) => EvmChainAdapter - getEthersV5Provider: (chainId: EvmChainId) => ethersV5.providers.JsonRpcProvider } // https://github.com/OffchainLabs/arbitrum-token-bridge/blob/d17c88ef3eef3f4ffc61a04d34d50406039f045d/packages/arb-token-bridge-ui/src/util/TokenDepositUtils.ts#L45-L51 @@ -41,7 +40,6 @@ export const fetchArbitrumBridgeSwap = async ({ receiveAddress, supportsEIP1559, assertGetEvmChainAdapter, - getEthersV5Provider, }: FetchArbitrumBridgeSwapInput): Promise<{ request: | Omit @@ -77,7 +75,7 @@ export const fetchArbitrumBridgeSwap = async ({ destinationAddress: receiveAddress, }) - const { networkFeeCryptoBaseUnit } = await getFees({ + const { networkFeeCryptoBaseUnit } = await evm.getFees({ adapter, data: request.txRequest.data.toString(), to: request.txRequest.to, @@ -97,7 +95,7 @@ export const fetchArbitrumBridgeSwap = async ({ destinationAddress: receiveAddress, }) - const { networkFeeCryptoBaseUnit } = await getFees({ + const { networkFeeCryptoBaseUnit } = await evm.getFees({ adapter, data: request.txRequest.data.toString(), to: request.txRequest.to, @@ -152,7 +150,7 @@ export const fetchArbitrumBridgeSwap = async ({ } // Actual fees - const feeData = await getFees({ + const feeData = await evm.getFees({ adapter, data: maybeRequest.txRequest.data.toString(), to: maybeRequest.txRequest.to, @@ -177,7 +175,7 @@ export const fetchArbitrumBridgeSwap = async ({ destinationAddress: receiveAddress, }) - const { networkFeeCryptoBaseUnit } = await getFees({ + const { networkFeeCryptoBaseUnit } = await evm.getFees({ adapter, data: request.txRequest.data.toString(), to: request.txRequest.to, diff --git a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/helpers.ts b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/helpers.ts index 44f66540c7c..7a0cc0cc686 100644 --- a/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/helpers.ts +++ b/packages/swapper/src/swappers/ArbitrumBridgeSwapper/utils/helpers.ts @@ -1,10 +1,9 @@ import { Erc20Bridger, getArbitrumNetwork } from '@arbitrum/sdk' import { arbitrumAssetId, ethAssetId, ethChainId, fromAssetId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import { getEthersV5Provider } from '@shapeshiftoss/contracts' import { type Asset, KnownChainIds } from '@shapeshiftoss/types' import type { Result } from '@sniptt/monads/build' import { Err, Ok } from '@sniptt/monads/build' -import type { ethers as ethersV5 } from 'ethers5' import { getAddress, isAddressEqual } from 'viem' import { arbitrum } from 'viem/chains' @@ -17,11 +16,9 @@ import { arbitrumBridgeSupportedChainIds } from './types' export const assertValidTrade = async ({ buyAsset, sellAsset, - getEthersV5Provider, }: { buyAsset: Asset sellAsset: Asset - getEthersV5Provider: (chainId: EvmChainId) => ethersV5.providers.JsonRpcProvider }): Promise> => { if ( !arbitrumBridgeSupportedChainIds.includes( diff --git a/packages/swapper/src/swappers/CowSwapper/endpoints.ts b/packages/swapper/src/swappers/CowSwapper/endpoints.ts index f6254332161..451ba08ac19 100644 --- a/packages/swapper/src/swappers/CowSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/CowSwapper/endpoints.ts @@ -1,5 +1,5 @@ import { fromAssetId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import type { EvmChainId } from '@shapeshiftoss/types' import { TxStatus } from '@shapeshiftoss/unchained-client' import { bn } from '@shapeshiftoss/utils' import type { Result } from '@sniptt/monads/build' diff --git a/packages/swapper/src/swappers/LifiSwapper/endpoints.ts b/packages/swapper/src/swappers/LifiSwapper/endpoints.ts index bf61b59e99a..787bbcca9c1 100644 --- a/packages/swapper/src/swappers/LifiSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/LifiSwapper/endpoints.ts @@ -1,9 +1,9 @@ import type { ChainKey, ExtendedTransactionInfo, GetStatusRequest, Route } from '@lifi/sdk' import { getStepTransaction } from '@lifi/sdk' import { type ChainId, fromChainId } from '@shapeshiftoss/caip' +import { evm } from '@shapeshiftoss/chain-adapters' import { TxStatus } from '@shapeshiftoss/unchained-client' import { bn } from '@shapeshiftoss/utils' -import { getFees } from '@shapeshiftoss/utils/dist/evm' import type { Result } from '@sniptt/monads/build' import { Err } from '@sniptt/monads/build' import type { InterpolationOptions } from 'node-polyglot' @@ -110,7 +110,7 @@ export const lifiApi: SwapperApi = { }) } - const feeData = await getFees({ + const feeData = await evm.getFees({ adapter: assertGetEvmChainAdapter(chainId), data: data.toString(), to, diff --git a/packages/swapper/src/swappers/LifiSwapper/utils/getNetworkFeeCryptoBaseUnit/getNetworkFeeCryptoBaseUnit.ts b/packages/swapper/src/swappers/LifiSwapper/utils/getNetworkFeeCryptoBaseUnit/getNetworkFeeCryptoBaseUnit.ts index 4e473db1970..36c913c4324 100644 --- a/packages/swapper/src/swappers/LifiSwapper/utils/getNetworkFeeCryptoBaseUnit/getNetworkFeeCryptoBaseUnit.ts +++ b/packages/swapper/src/swappers/LifiSwapper/utils/getNetworkFeeCryptoBaseUnit/getNetworkFeeCryptoBaseUnit.ts @@ -1,9 +1,9 @@ import { getStepTransaction } from '@lifi/sdk' import type { LiFiStep } from '@lifi/types' import type { ChainId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' -import type { KnownChainIds } from '@shapeshiftoss/types' -import { calcNetworkFeeCryptoBaseUnit } from '@shapeshiftoss/utils/dist/evm' +import { evm } from '@shapeshiftoss/chain-adapters' +import { viemClientByChainId } from '@shapeshiftoss/contracts' +import type { EvmChainId, KnownChainIds } from '@shapeshiftoss/types' import { getContract } from 'viem' import type { SwapperDeps } from '../../../../types' @@ -38,7 +38,7 @@ export const getNetworkFeeCryptoBaseUnit = async ({ throw new Error('getStepTransaction failed') } - const publicClient = deps.viemClientByChainId[chainId as EvmChainId] + const publicClient = viemClientByChainId[chainId as EvmChainId] const abi = [ { @@ -75,7 +75,7 @@ export const getNetworkFeeCryptoBaseUnit = async ({ if (!estimatedGasLimit) throw new Error('failed to get estimated gas limit') - const networkFeeCryptoBaseUnit = calcNetworkFeeCryptoBaseUnit({ + const networkFeeCryptoBaseUnit = evm.calcNetworkFeeCryptoBaseUnit({ ...average, supportsEIP1559, gasLimit: estimatedGasLimit.toString(), diff --git a/packages/swapper/src/swappers/OneInchSwapper/endpoints.ts b/packages/swapper/src/swappers/OneInchSwapper/endpoints.ts index 0f7fc27ca29..81b65c6a1f3 100644 --- a/packages/swapper/src/swappers/OneInchSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/OneInchSwapper/endpoints.ts @@ -1,6 +1,6 @@ import { fromChainId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' -import { getFees } from '@shapeshiftoss/utils/dist/evm' +import { evm } from '@shapeshiftoss/chain-adapters' +import type { EvmChainId } from '@shapeshiftoss/types' import type { Result } from '@sniptt/monads/build' import { v4 as uuid } from 'uuid' @@ -66,7 +66,7 @@ export const oneInchApi: SwapperApi = { config, }) - const feeData = await getFees({ + const feeData = await evm.getFees({ adapter: assertGetEvmChainAdapter(chainId), data, to, diff --git a/packages/swapper/src/swappers/OneInchSwapper/getApprovalAddress/getApprovalAddress.tsx b/packages/swapper/src/swappers/OneInchSwapper/getApprovalAddress/getApprovalAddress.tsx index 399987e3e0a..212d729ce54 100644 --- a/packages/swapper/src/swappers/OneInchSwapper/getApprovalAddress/getApprovalAddress.tsx +++ b/packages/swapper/src/swappers/OneInchSwapper/getApprovalAddress/getApprovalAddress.tsx @@ -1,5 +1,5 @@ import { fromChainId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import type { EvmChainId } from '@shapeshiftoss/types' import type { Result } from '@sniptt/monads' import { Ok } from '@sniptt/monads' diff --git a/packages/swapper/src/swappers/OneInchSwapper/getTradeQuote/getTradeQuote.ts b/packages/swapper/src/swappers/OneInchSwapper/getTradeQuote/getTradeQuote.ts index 943b078556a..8572f400798 100644 --- a/packages/swapper/src/swappers/OneInchSwapper/getTradeQuote/getTradeQuote.ts +++ b/packages/swapper/src/swappers/OneInchSwapper/getTradeQuote/getTradeQuote.ts @@ -1,6 +1,6 @@ import { fromChainId } from '@shapeshiftoss/caip' +import { evm } from '@shapeshiftoss/chain-adapters' import { addBasisPointAmount, convertBasisPointsToPercentage } from '@shapeshiftoss/utils' -import { calcNetworkFeeCryptoBaseUnit } from '@shapeshiftoss/utils/dist/evm' import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' import { v4 as uuid } from 'uuid' @@ -89,7 +89,7 @@ export async function getTradeQuote( // assert get is allowed because we chain chainId is an EVM chainId above in assertValidTrade const adapter = deps.assertGetEvmChainAdapter(chainId) const { average } = await adapter.getGasFeeData() - const networkFeeCryptoBaseUnit = calcNetworkFeeCryptoBaseUnit({ + const networkFeeCryptoBaseUnit = evm.calcNetworkFeeCryptoBaseUnit({ ...average, supportsEIP1559, gasLimit: quote.estimatedGas, diff --git a/packages/swapper/src/swappers/PortalsSwapper/endpoints.ts b/packages/swapper/src/swappers/PortalsSwapper/endpoints.ts index fc78d3696f7..1ec83216171 100644 --- a/packages/swapper/src/swappers/PortalsSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/PortalsSwapper/endpoints.ts @@ -1,7 +1,7 @@ import { fromAssetId, fromChainId } from '@shapeshiftoss/caip' +import { evm } from '@shapeshiftoss/chain-adapters' import type { KnownChainIds } from '@shapeshiftoss/types' import { convertBasisPointsToDecimalPercentage } from '@shapeshiftoss/utils' -import { getFees } from '@shapeshiftoss/utils/dist/evm' import type { Result } from '@sniptt/monads/build' import BigNumber from 'bignumber.js' import { zeroAddress } from 'viem' @@ -89,7 +89,7 @@ export const portalsApi: SwapperApi = { gasLimit: estimatedGas, } = portalsTradeOrderResponse.tx - const { gasLimit, ...feeData } = await getFees({ + const { gasLimit, ...feeData } = await evm.getFees({ adapter: assertGetEvmChainAdapter(chainId), data, to, diff --git a/packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts b/packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts index 240b14fd6fb..a08b1fb22b3 100644 --- a/packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts +++ b/packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts @@ -1,9 +1,9 @@ import type { ChainId } from '@shapeshiftoss/caip' import { fromAssetId } from '@shapeshiftoss/caip' import type { EvmChainAdapter } from '@shapeshiftoss/chain-adapters' +import { evm } from '@shapeshiftoss/chain-adapters' import type { KnownChainIds } from '@shapeshiftoss/types' import { bnOrZero, convertBasisPointsToDecimalPercentage } from '@shapeshiftoss/utils' -import { calcNetworkFeeCryptoBaseUnit } from '@shapeshiftoss/utils/dist/evm' import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' import { zeroAddress } from 'viem' @@ -21,7 +21,7 @@ import { import { getRate, makeSwapErrorRight } from '../../../utils' import { getTreasuryAddressFromChainId, isNativeEvmAsset } from '../../utils/helpers/helpers' import { chainIdToPortalsNetwork } from '../constants' -import { fetchPortalsTradeOrder } from '../utils/fetchPortalsTradeOrder' +import { fetchPortalsTradeEstimate, fetchPortalsTradeOrder } from '../utils/fetchPortalsTradeOrder' import { getDummyQuoteParams, isSupportedChainId } from '../utils/helpers' export async function getPortalsTradeQuote( @@ -120,10 +120,24 @@ export async function getPortalsTradeQuote( validate: true, swapperConfig, }).catch(async e => { - // If validation fails, fire two more quotes: - // 1. a quote with validation enabled, but using a well-funded address to get a rough gasLimit estimate - // 2. another quote with validation disabled, to get an actual quote + // If validation fails, fire 3 more quotes: + // 1. a quote estimate (does not require approval) to get the optimal slippage tolerance + // 2. a quote with validation enabled, but using a well-funded address to get a rough gasLimit estimate + // 3. another quote with validation disabled, to get an actual quote (using the user slippage, or the optimal from the estimate) console.info('failed to get Portals quote with validation enabled', e) + + // Use the quote estimate endpoint to get the optimal slippage tolerance + const quoteEstimateResponse = await fetchPortalsTradeEstimate({ + sender: sendAddress, + inputToken, + outputToken, + inputAmount: sellAmountIncludingProtocolFeesCryptoBaseUnit, + swapperConfig, + }).catch(e => { + console.info('failed to get Portals quote estimate', e) + return undefined + }) + const dummyQuoteParams = getDummyQuoteParams(sellAsset.chainId) const dummySellAssetAddress = fromAssetId(dummyQuoteParams.sellAssetId).assetReference @@ -132,6 +146,7 @@ export async function getPortalsTradeQuote( const dummyInputToken = `${portalsNetwork}:${dummySellAssetAddress}` const dummyOutputToken = `${portalsNetwork}:${dummyBuyAssetAddress}` + // Use a dummy request to the portal endpoint to get a rough gasLimit estimate const dummyOrderResponse = await fetchPortalsTradeOrder({ sender: dummyQuoteParams.accountAddress, inputToken: dummyInputToken, @@ -158,6 +173,7 @@ export async function getPortalsTradeQuote( inputAmount: sellAmountIncludingProtocolFeesCryptoBaseUnit, slippageTolerancePercentage: userSlippageTolerancePercentageDecimalOrDefault ?? + quoteEstimateResponse?.context.slippageTolerancePercentage ?? bnOrZero(getDefaultSlippageDecimalPercentageForSwapper(SwapperName.Portals)) .times(100) .toNumber(), @@ -194,7 +210,7 @@ export async function getPortalsTradeQuote( const adapter = assertGetEvmChainAdapter(chainId) const { average } = await adapter.getGasFeeData() - const networkFeeCryptoBaseUnit = calcNetworkFeeCryptoBaseUnit({ + const networkFeeCryptoBaseUnit = evm.calcNetworkFeeCryptoBaseUnit({ ...average, supportsEIP1559, // times 1 isn't a mistake, it's just so we can write this comment above to mention that Portals already add a diff --git a/packages/swapper/src/swappers/PortalsSwapper/utils/fetchPortalsTradeOrder.ts b/packages/swapper/src/swappers/PortalsSwapper/utils/fetchPortalsTradeOrder.ts index e879193e0b7..63430aef232 100644 --- a/packages/swapper/src/swappers/PortalsSwapper/utils/fetchPortalsTradeOrder.ts +++ b/packages/swapper/src/swappers/PortalsSwapper/utils/fetchPortalsTradeOrder.ts @@ -17,6 +17,8 @@ type PortalsTradeOrderParams = { swapperConfig: SwapperConfig } +type PortalsTradeOrderEstimateParams = Omit + type PortalsTradeOrderResponse = { context: { orderId: string @@ -50,6 +52,25 @@ type PortalsTradeOrderResponse = { gasLimit: string } } + +type PortalsTradeOrderEstimateResponse = { + outputAmount: string + minOutputAmount: string + outputToken: string + outputTokenDecimals: number + context: { + slippageTolerancePercentage: number + inputAmount: string + inputAmountUsd: number + inputToken: string + outputToken: string + outputAmount: string + outputAmountUsd: number + minOutputAmountUsd: number + sender?: string + } +} + export const fetchPortalsTradeOrder = async ({ sender, inputToken, @@ -90,3 +111,35 @@ export const fetchPortalsTradeOrder = async ({ throw error } } + +export const fetchPortalsTradeEstimate = async ({ + sender, + inputToken, + inputAmount, + outputToken, + slippageTolerancePercentage, + swapperConfig, +}: PortalsTradeOrderEstimateParams): Promise => { + const url = `${swapperConfig.REACT_APP_PORTALS_BASE_URL}/v2/portal/estimate` + + const params = new URLSearchParams({ + sender, + inputToken, + inputAmount, + outputToken, + }) + + if (slippageTolerancePercentage !== undefined) { + params.append('slippageTolerancePercentage', slippageTolerancePercentage.toFixed(2)) // Portals API expects a string with at most 2 decimal places + } + + try { + const response = await axios.get(url, { params }) + return response.data + } catch (error) { + if (axios.isAxiosError(error)) { + throw new Error(`Failed to fetch Portals trade estimate: ${error.message}`) + } + throw error + } +} diff --git a/packages/swapper/src/swappers/PortalsSwapper/utils/helpers.ts b/packages/swapper/src/swappers/PortalsSwapper/utils/helpers.ts index 721e1c094b0..c7879d2a315 100644 --- a/packages/swapper/src/swappers/PortalsSwapper/utils/helpers.ts +++ b/packages/swapper/src/swappers/PortalsSwapper/utils/helpers.ts @@ -1,5 +1,5 @@ import { type AssetId, type ChainId, foxAssetId, toAccountId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import type { EvmChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import type { PortalsSupportedChainId } from '../types' diff --git a/packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts b/packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts index 9900b606a87..79ee7d4ea02 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts @@ -1,11 +1,10 @@ import type { StdSignDoc } from '@cosmjs/amino' import type { StdFee } from '@keplr-wallet/types' import { cosmosAssetId, fromAssetId, fromChainId, thorchainAssetId } from '@shapeshiftoss/caip' -import { cosmossdk as cosmossdkChainAdapter } from '@shapeshiftoss/chain-adapters' +import { cosmossdk as cosmossdkChainAdapter, evm } from '@shapeshiftoss/chain-adapters' import type { BTCSignTx } from '@shapeshiftoss/hdwallet-core' import { cosmossdk, TxStatus } from '@shapeshiftoss/unchained-client' import { assertUnreachable, BigNumber, bn, bnOrZero } from '@shapeshiftoss/utils' -import { getFees } from '@shapeshiftoss/utils/dist/evm' import { type Result } from '@sniptt/monads/build' import assert from 'assert' import axios from 'axios' @@ -92,7 +91,7 @@ export const thorchainApi: SwapperApi = { switch (tradeType) { case TradeType.L1ToL1: { - const feeData = await getFees({ + const feeData = await evm.getFees({ adapter: assertGetEvmChainAdapter(chainId), data, to: router, @@ -145,7 +144,7 @@ export const thorchainApi: SwapperApi = { args: params, }) - const feeData = await getFees({ + const feeData = await evm.getFees({ adapter: assertGetEvmChainAdapter(chainId), data: swapInData, to: aggregator, @@ -178,7 +177,7 @@ export const thorchainApi: SwapperApi = { assert(router, 'router required for l1 to thorchain longtail swaps') - const feeData = await getFees({ + const feeData = await evm.getFees({ adapter: assertGetEvmChainAdapter(chainId), data: dataWithAmountOut, to: updatedRouter, diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/getBestAggregator.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/getBestAggregator.ts index 63315410e69..33344783871 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/getBestAggregator.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/getBestAggregator.ts @@ -1,5 +1,5 @@ -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' -import type { Asset } from '@shapeshiftoss/types' +import { viemClientByChainId } from '@shapeshiftoss/contracts' +import type { Asset, EvmChainId } from '@shapeshiftoss/types' import { Err, Ok } from '@sniptt/monads' import type { Token } from '@uniswap/sdk-core' import type { FeeAmount } from '@uniswap/v3-sdk' @@ -7,7 +7,6 @@ import assert from 'assert' import type { Address, GetContractReturnType, PublicClient } from 'viem' import { getContract } from 'viem' -import type { SwapperDeps } from '../../../types' import { TradeQuoteError } from '../../../types' import { makeSwapErrorRight } from '../../../utils' import { @@ -24,13 +23,12 @@ import { } from './longTailHelpers' export const getBestAggregator = async ( - deps: SwapperDeps, buyAsset: Asset, sellToken: Token, buyToken: Token, sellAmountIncludingProtocolFeesCryptoBaseUnit: string, ) => { - const publicClient = deps.viemClientByChainId[buyAsset.chainId as EvmChainId] + const publicClient = viemClientByChainId[buyAsset.chainId as EvmChainId] assert(publicClient !== undefined, `no public client found for chainId '${buyAsset.chainId}'`) const poolAddresses: Map< diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailQuote.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailQuote.ts index 39643c80689..2fb9d34137c 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailQuote.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/getL1ToLongtailQuote.ts @@ -121,7 +121,6 @@ export const getL1ToLongtailQuote = async ( const onlyStep = getHopByIndex(quote, 0)! const maybeBestAggregator = await getBestAggregator( - deps, buyAssetFeeAsset, getWrappedToken(buyAssetFeeAsset), getTokenFromAsset(buyAsset), diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/getLongtailQuote.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/getLongtailQuote.ts index ac440e0bbf2..ddacda64e46 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/getLongtailQuote.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/getLongtailQuote.ts @@ -1,5 +1,6 @@ import { ethChainId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import { viemClientByChainId } from '@shapeshiftoss/contracts' +import type { EvmChainId } from '@shapeshiftoss/types' import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' import assert from 'assert' @@ -54,11 +55,10 @@ export const getLongtailToL1Quote = async ( } // TODO: use more than just UniswapV3, and also consider trianglar routes. - const publicClient = deps.viemClientByChainId[sellChainId as EvmChainId] + const publicClient = viemClientByChainId[sellChainId as EvmChainId] assert(publicClient !== undefined, `no public client found for chainId '${sellChainId}'`) const maybeBestAggregator = await getBestAggregator( - deps, buyAssetFeeAsset, getTokenFromAsset(sellAsset), getWrappedToken(buyAssetFeeAsset), diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/txFeeHelpers/evmTxFees/getEvmTxFees.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/txFeeHelpers/evmTxFees/getEvmTxFees.ts index a867c9f3805..712fc3407c5 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/txFeeHelpers/evmTxFees/getEvmTxFees.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/txFeeHelpers/evmTxFees/getEvmTxFees.ts @@ -1,5 +1,5 @@ import type { EvmChainAdapter } from '@shapeshiftoss/chain-adapters' -import { calcNetworkFeeCryptoBaseUnit } from '@shapeshiftoss/utils/dist/evm' +import { evm } from '@shapeshiftoss/chain-adapters' import { THOR_EVM_GAS_LIMIT } from '../../constants' @@ -17,7 +17,7 @@ export const getEvmTxFees = async (args: GetEvmTxFeesArgs): Promise = const { average } = await adapter.getGasFeeData() - const networkFeeCryptoBaseUnit = calcNetworkFeeCryptoBaseUnit({ + const networkFeeCryptoBaseUnit = evm.calcNetworkFeeCryptoBaseUnit({ ...average, supportsEIP1559, gasLimit: THOR_EVM_GAS_LIMIT, // hardcoded default for quote estimation (no wallet) diff --git a/packages/swapper/src/swappers/ThorchainSwapper/utils/txFeeHelpers/utxoTxFees/getUtxoTxFees.ts b/packages/swapper/src/swappers/ThorchainSwapper/utils/txFeeHelpers/utxoTxFees/getUtxoTxFees.ts index 8597a4473c7..84b6bdfbe7d 100644 --- a/packages/swapper/src/swappers/ThorchainSwapper/utils/txFeeHelpers/utxoTxFees/getUtxoTxFees.ts +++ b/packages/swapper/src/swappers/ThorchainSwapper/utils/txFeeHelpers/utxoTxFees/getUtxoTxFees.ts @@ -1,5 +1,6 @@ import type { AssetId } from '@shapeshiftoss/caip' -import type { GetFeeDataInput, UtxoBaseAdapter, UtxoChainId } from '@shapeshiftoss/chain-adapters' +import type { GetFeeDataInput, UtxoBaseAdapter } from '@shapeshiftoss/chain-adapters' +import type { UtxoChainId } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' import { bn } from '@shapeshiftoss/utils' diff --git a/packages/swapper/src/swappers/ZrxSwapper/endpoints.ts b/packages/swapper/src/swappers/ZrxSwapper/endpoints.ts index 44ad09cff80..389fb316544 100644 --- a/packages/swapper/src/swappers/ZrxSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/ZrxSwapper/endpoints.ts @@ -1,5 +1,5 @@ import { fromChainId } from '@shapeshiftoss/caip' -import { getFees } from '@shapeshiftoss/utils/dist/evm' +import { evm } from '@shapeshiftoss/chain-adapters' import type { Result } from '@sniptt/monads/build' import BigNumber from 'bignumber.js' @@ -60,7 +60,7 @@ export const zrxApi: SwapperApi = { const { value, to, data, estimatedGas } = zrxQuoteResponse.unwrap() - const { gasLimit, ...feeData } = await getFees({ + const { gasLimit, ...feeData } = await evm.getFees({ adapter: assertGetEvmChainAdapter(chainId), data, to, diff --git a/packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.test.ts b/packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.test.ts index 42ad04bb9ff..3acf8a9b00d 100644 --- a/packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.test.ts +++ b/packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.test.ts @@ -41,9 +41,13 @@ vi.mock('../utils/helpers/helpers', async () => { baseUrlFromChainId: vi.fn(() => 'https://0x.shapeshift.com/ethereum/'), } }) -vi.mock('@shapeshiftoss/chain-adapters', () => { +vi.mock('@shapeshiftoss/chain-adapters', async () => { const { KnownChainIds } = require('@shapeshiftoss/types') + + const actual = await vi.importActual('@shapeshiftoss/chain-adapters') + return { + ...actual, isEvmChainId: vi.fn(() => true), evmChainIds: [KnownChainIds.EthereumMainnet], optimism: { diff --git a/packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.ts b/packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.ts index 96cb1bfadb0..e32362348b0 100644 --- a/packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.ts +++ b/packages/swapper/src/swappers/ZrxSwapper/getZrxTradeQuote/getZrxTradeQuote.ts @@ -1,7 +1,7 @@ import type { ChainId } from '@shapeshiftoss/caip' import type { EvmChainAdapter } from '@shapeshiftoss/chain-adapters' +import { evm } from '@shapeshiftoss/chain-adapters' import { bn, bnOrZero } from '@shapeshiftoss/utils' -import { calcNetworkFeeCryptoBaseUnit } from '@shapeshiftoss/utils/dist/evm' import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' import { v4 as uuid } from 'uuid' @@ -102,7 +102,7 @@ export async function getZrxTradeQuote( try { const adapter = assertGetEvmChainAdapter(chainId) const { average } = await adapter.getGasFeeData() - const networkFeeCryptoBaseUnit = calcNetworkFeeCryptoBaseUnit({ + const networkFeeCryptoBaseUnit = evm.calcNetworkFeeCryptoBaseUnit({ ...average, supportsEIP1559, // add gas limit buffer to account for the fact we perform all of our validation on the trade quote estimations diff --git a/packages/swapper/src/swappers/utils/helpers/helpers.test.ts b/packages/swapper/src/swappers/utils/helpers/helpers.test.ts index 5636c309ad1..9b180844c97 100644 --- a/packages/swapper/src/swappers/utils/helpers/helpers.test.ts +++ b/packages/swapper/src/swappers/utils/helpers/helpers.test.ts @@ -1,5 +1,5 @@ import { thorchainChainId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import type { EvmChainId } from '@shapeshiftoss/types' import { describe, expect, it } from 'vitest' import { getTreasuryAddressFromChainId } from './helpers' diff --git a/packages/swapper/src/swappers/utils/test-data/fees.ts b/packages/swapper/src/swappers/utils/test-data/fees.ts index 6387217f981..266fc3339ab 100644 --- a/packages/swapper/src/swappers/utils/test-data/fees.ts +++ b/packages/swapper/src/swappers/utils/test-data/fees.ts @@ -1,4 +1,5 @@ -import type { evm, EvmChainId, FeeDataEstimate } from '@shapeshiftoss/chain-adapters' +import type { evm, FeeDataEstimate } from '@shapeshiftoss/chain-adapters' +import type { EvmChainId } from '@shapeshiftoss/types' export const gasFeeData: evm.GasFeeDataEstimate = { fast: { diff --git a/packages/swapper/src/types.ts b/packages/swapper/src/types.ts index 3f57f28cc74..77e9766bef0 100644 --- a/packages/swapper/src/types.ts +++ b/packages/swapper/src/types.ts @@ -3,27 +3,25 @@ import type { AssetId, ChainId, Nominal } from '@shapeshiftoss/caip' import type { ChainAdapter, CosmosSdkChainAdapter, - CosmosSdkChainId, EvmChainAdapter, - EvmChainId, UtxoChainAdapter, - UtxoChainId, } from '@shapeshiftoss/chain-adapters' import type { BTCSignTx, HDWallet } from '@shapeshiftoss/hdwallet-core' import type { AccountMetadata, Asset, AssetsByIdPartial, + CosmosSdkChainId, + EvmChainId, KnownChainIds, PartialRecord, UtxoAccountType, + UtxoChainId, } from '@shapeshiftoss/types' import type { evm, TxStatus } from '@shapeshiftoss/unchained-client' import type { Result } from '@sniptt/monads' import type { TypedData } from 'eip-712' -import type { ethers as ethersV5 } from 'ethers5' import type { InterpolationOptions } from 'node-polyglot' -import type { Chain, PublicClient, Transport } from 'viem' import type { makeSwapperAxiosServiceMonadic } from './utils' @@ -161,7 +159,6 @@ export type GetTradeQuoteInput = export type EvmSwapperDeps = { assertGetEvmChainAdapter: (chainId: ChainId) => EvmChainAdapter - getEthersV5Provider: (chainId: EvmChainId) => ethersV5.providers.JsonRpcProvider } export type UtxoSwapperDeps = { assertGetUtxoChainAdapter: (chainId: ChainId) => UtxoChainAdapter } export type CosmosSdkSwapperDeps = { @@ -170,7 +167,6 @@ export type CosmosSdkSwapperDeps = { export type SwapperDeps = { assetsById: AssetsByIdPartial - viemClientByChainId: Record> config: SwapperConfig assertGetChainAdapter: (chainId: ChainId) => ChainAdapter } & EvmSwapperDeps & diff --git a/packages/swapper/src/utils.ts b/packages/swapper/src/utils.ts index ec676ae0793..9b0bd735932 100644 --- a/packages/swapper/src/utils.ts +++ b/packages/swapper/src/utils.ts @@ -1,8 +1,7 @@ import type { AssetId, ChainId } from '@shapeshiftoss/caip' import type { EvmChainAdapter } from '@shapeshiftoss/chain-adapters' import type { Asset } from '@shapeshiftoss/types' -import { TxStatus } from '@shapeshiftoss/unchained-client' -import { getTxStatus } from '@shapeshiftoss/unchained-client/dist/evm' +import { evm, TxStatus } from '@shapeshiftoss/unchained-client' import { bn, fromBaseUnit } from '@shapeshiftoss/utils' import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' @@ -210,7 +209,7 @@ export const checkSafeTransactionStatus = async ({ if (transaction.transactionHash) { const adapter = assertGetEvmChainAdapter(chainId) const tx = await adapter.httpProvider.getTransaction({ txid: transaction.transactionHash }) - const status = getTxStatus(tx) + const status = evm.getTxStatus(tx) return { status, @@ -242,7 +241,7 @@ export const checkEvmSwapStatus = async ({ if (maybeSafeTransactionStatus) return maybeSafeTransactionStatus const adapter = assertGetEvmChainAdapter(chainId) const tx = await adapter.httpProvider.getTransaction({ txid: txHash }) - const status = getTxStatus(tx) + const status = evm.getTxStatus(tx) return { status, diff --git a/packages/types/src/base.ts b/packages/types/src/base.ts index b8d8763b0a4..6fe14b823a0 100644 --- a/packages/types/src/base.ts +++ b/packages/types/src/base.ts @@ -28,6 +28,25 @@ export enum KnownChainIds { ThorchainMainnet = 'cosmos:thorchain-mainnet-v1', } +export type EvmChainId = + | KnownChainIds.EthereumMainnet + | KnownChainIds.AvalancheMainnet + | KnownChainIds.OptimismMainnet + | KnownChainIds.BnbSmartChainMainnet + | KnownChainIds.PolygonMainnet + | KnownChainIds.GnosisMainnet + | KnownChainIds.ArbitrumMainnet + | KnownChainIds.ArbitrumNovaMainnet + | KnownChainIds.BaseMainnet + +export type CosmosSdkChainId = KnownChainIds.CosmosMainnet | KnownChainIds.ThorchainMainnet + +export type UtxoChainId = + | KnownChainIds.BitcoinMainnet + | KnownChainIds.BitcoinCashMainnet + | KnownChainIds.DogecoinMainnet + | KnownChainIds.LitecoinMainnet + export enum WithdrawType { DELAYED, INSTANT, diff --git a/packages/unchained-client/package.json b/packages/unchained-client/package.json index 3560acd7acb..b1a0aa2e3f6 100644 --- a/packages/unchained-client/package.json +++ b/packages/unchained-client/package.json @@ -20,6 +20,7 @@ "dependencies": { "@shapeshiftoss/caip": "workspace:^", "@shapeshiftoss/common-api": "^9.3.0", + "@shapeshiftoss/contracts": "workspace:^", "isomorphic-ws": "^4.0.1", "ws": "^8.17.1" }, diff --git a/packages/unchained-client/src/evm/arbitrum/parser/__tests__/arbitrum.test.ts b/packages/unchained-client/src/evm/arbitrum/parser/__tests__/arbitrum.test.ts index 53a7824a490..7fc1e28ca72 100644 --- a/packages/unchained-client/src/evm/arbitrum/parser/__tests__/arbitrum.test.ts +++ b/packages/unchained-client/src/evm/arbitrum/parser/__tests__/arbitrum.test.ts @@ -30,7 +30,6 @@ import zrxTradeUsdcToWbtc from './mockData/zrxTradeUsdcToWbtc' vi.hoisted(() => { vi.stubEnv('REACT_APP_FEATURE_NFT_METADATA', 'true') - vi.stubEnv('REACT_APP_RFOX_PROXY_CONTRACT_ADDRESS', '0xac2a4fd70bcd8bab0662960455c363735f0e2b56') }) const mockedApi = vi.mocked(new V1Api()) diff --git a/packages/unchained-client/src/evm/arbitrum/parser/index.ts b/packages/unchained-client/src/evm/arbitrum/parser/index.ts index 457c521d00b..52904dfd838 100644 --- a/packages/unchained-client/src/evm/arbitrum/parser/index.ts +++ b/packages/unchained-client/src/evm/arbitrum/parser/index.ts @@ -1,4 +1,5 @@ import { foxOnArbitrumOneAssetId } from '@shapeshiftoss/caip' +import { RFOX_PROXY_CONTRACT_ADDRESS } from '@shapeshiftoss/contracts' import type { Tx } from '../../../generated/arbitrum' import type { BaseTransactionParserArgs } from '../../parser' @@ -24,7 +25,7 @@ export class TransactionParser extends BaseTransactionParser { new erc20.Parser({ chainId: this.chainId, provider: this.provider }), new zrx.Parser({ proxyContract: ZRX_ARBITRUM_PROXY_CONTRACT }), new rfox.Parser({ - proxyContract: process.env.REACT_APP_RFOX_PROXY_CONTRACT_ADDRESS ?? '', + proxyContract: RFOX_PROXY_CONTRACT_ADDRESS, stakingAssetId: foxOnArbitrumOneAssetId, }), new arbitrumBridge.Parser({ diff --git a/packages/unchained-client/tsconfig.json b/packages/unchained-client/tsconfig.json index cbdb04322de..98880c27d7a 100644 --- a/packages/unchained-client/tsconfig.json +++ b/packages/unchained-client/tsconfig.json @@ -8,6 +8,6 @@ "exclude": ["dist"], "references": [ { "path": "../caip" }, - { "path": "../types" }, + { "path": "../contracts" } ] } \ No newline at end of file diff --git a/packages/utils/package.json b/packages/utils/package.json index 243519427f3..4fecf3e30cc 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -18,7 +18,6 @@ }, "dependencies": { "@shapeshiftoss/caip": "workspace:^", - "@shapeshiftoss/chain-adapters": "workspace:^", "@shapeshiftoss/types": "workspace:^", "crypto-browserify": "^3.12.0" } diff --git a/packages/utils/src/evm.ts b/packages/utils/src/evm.ts deleted file mode 100644 index fb920f2d92e..00000000000 --- a/packages/utils/src/evm.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { evm, EvmChainAdapter } from '@shapeshiftoss/chain-adapters' - -import { bn, bnOrZero } from './bignumber/bignumber' - -export type Fees = evm.Fees & { - gasLimit: string - networkFeeCryptoBaseUnit: string -} - -export type EvmFees = { - fees: Fees - txFeeFiat: string - networkFeeCryptoBaseUnit: string -} - -type CalcNetworkFeeCryptoBaseUnitArgs = evm.FeeData & { - supportsEIP1559: boolean -} - -export const calcNetworkFeeCryptoBaseUnit = (args: CalcNetworkFeeCryptoBaseUnitArgs) => { - const { - supportsEIP1559, - gasLimit, - gasPrice, - l1GasLimit, - l1GasPrice, - maxFeePerGas, - maxPriorityFeePerGas, - } = args - - // l1 fee if exists or 0 - const l1Fee = bnOrZero(l1GasPrice).times(bnOrZero(l1GasLimit)) - - // eip1559 fees - if (supportsEIP1559 && maxFeePerGas && maxPriorityFeePerGas) { - return bn(gasLimit).times(maxFeePerGas).plus(l1Fee).toFixed(0) - } - - // legacy fees - return bn(gasLimit).times(gasPrice).plus(l1Fee).toFixed(0) -} - -type GetFeesArgs = { - adapter: EvmChainAdapter - data: string - to: string - value: string - from: string - supportsEIP1559: boolean -} - -export const getFees = async (args: GetFeesArgs): Promise => { - const { adapter, data, to, value, from, supportsEIP1559 } = args - - const { - average: { chainSpecific: feeData }, - } = await adapter.getFeeData({ to, value, chainSpecific: { from, data } }) - - const networkFeeCryptoBaseUnit = calcNetworkFeeCryptoBaseUnit({ - ...feeData, - supportsEIP1559, - }) - - const { gasLimit, gasPrice, maxFeePerGas, maxPriorityFeePerGas } = feeData - - if (supportsEIP1559 && maxFeePerGas && maxPriorityFeePerGas) { - return { networkFeeCryptoBaseUnit, gasLimit, maxFeePerGas, maxPriorityFeePerGas } - } - - return { networkFeeCryptoBaseUnit, gasLimit, gasPrice } -} diff --git a/react-app-rewired/index.ts b/react-app-rewired/index.ts index ad672f29808..c879086fc4d 100644 --- a/react-app-rewired/index.ts +++ b/react-app-rewired/index.ts @@ -239,7 +239,15 @@ const reactAppRewireConfig = { // consuming them in packages will result in these being undefined [ 'REACT_APP_FEATURE_NFT_METADATA', - 'REACT_APP_RFOX_PROXY_CONTRACT_ADDRESS', + 'REACT_APP_AVALANCHE_NODE_URL', + 'REACT_APP_OPTIMISM_NODE_URL', + 'REACT_APP_BNBSMARTCHAIN_NODE_URL', + 'REACT_APP_POLYGON_NODE_URL', + 'REACT_APP_GNOSIS_NODE_URL', + 'REACT_APP_ETHEREUM_NODE_URL', + 'REACT_APP_ARBITRUM_NODE_URL', + 'REACT_APP_ARBITRUM_NOVA_NODE_URL', + 'REACT_APP_BASE_NODE_URL', ].includes(k) || !k.startsWith('REACT_APP_'), ) .sort((a, b) => { diff --git a/src/assets/translations/en/main.json b/src/assets/translations/en/main.json index 51ac50f1dd5..1a90f16c11e 100644 --- a/src/assets/translations/en/main.json +++ b/src/assets/translations/en/main.json @@ -1469,7 +1469,8 @@ "connect": "Connect" }, "verify": { - "confirmOnDevice": "Confirm on device" + "confirmOnDevice": "Confirm on device", + "addressMismatch": "Address mismatch. Please check you have the correct device connected and try again." }, "success": { "header": "Ledger Connected", diff --git a/src/components/AccountDropdown/AccountSegement.tsx b/src/components/AccountDropdown/AccountSegement.tsx index d65deb35d36..1298840cbb3 100644 --- a/src/components/AccountDropdown/AccountSegement.tsx +++ b/src/components/AccountDropdown/AccountSegement.tsx @@ -14,6 +14,7 @@ export const AccountSegment: FC = ({ title, subtitle }) => ( py={2} color='text.subtle' fontSize='sm' + alignItems='center' justifyContent='space-between' > {title} diff --git a/src/components/InlineCopyButton.tsx b/src/components/InlineCopyButton.tsx index d3938e1cafc..784eb5d1ce8 100644 --- a/src/components/InlineCopyButton.tsx +++ b/src/components/InlineCopyButton.tsx @@ -1,6 +1,6 @@ import { CheckIcon, CopyIcon } from '@chakra-ui/icons' import { Flex, IconButton } from '@chakra-ui/react' -import type { PropsWithChildren } from 'react' +import type { MouseEvent, PropsWithChildren } from 'react' import React, { useCallback } from 'react' import { useTranslate } from 'react-polyglot' import { useCopyToClipboard } from 'hooks/useCopyToClipboard' @@ -23,9 +23,13 @@ export const InlineCopyButton: React.FC = ({ const translate = useTranslate() const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 }) - const handleCopyClick = useCallback(() => { - copyToClipboard(value) - }, [copyToClipboard, value]) + const handleCopyClick = useCallback( + (e: MouseEvent) => { + e.stopPropagation() + copyToClipboard(value) + }, + [copyToClipboard, value], + ) // Hide the copy button if it is disabled if (isDisabled) return <>{children} diff --git a/src/components/Layout/Header/NavBar/UserMenu.tsx b/src/components/Layout/Header/NavBar/UserMenu.tsx index 5065701a117..158a0f12055 100644 --- a/src/components/Layout/Header/NavBar/UserMenu.tsx +++ b/src/components/Layout/Header/NavBar/UserMenu.tsx @@ -12,6 +12,7 @@ import { MenuList, useColorModeValue, } from '@chakra-ui/react' +import { viemEthMainnetClient } from '@shapeshiftoss/contracts' import type { FC } from 'react' import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { FaWallet } from 'react-icons/fa' @@ -26,7 +27,6 @@ import { RawText, Text } from 'components/Text' import { WalletActions } from 'context/WalletProvider/actions' import type { InitialState } from 'context/WalletProvider/WalletProvider' import { useWallet } from 'hooks/useWallet/useWallet' -import { viemEthMainnetClient } from 'lib/viem-client' export const entries = [WalletConnectedRoutes.Connected] diff --git a/src/components/Modals/FiatRamps/views/Overview.tsx b/src/components/Modals/FiatRamps/views/Overview.tsx index b9fd5f71a80..ee7249b1da0 100644 --- a/src/components/Modals/FiatRamps/views/Overview.tsx +++ b/src/components/Modals/FiatRamps/views/Overview.tsx @@ -35,6 +35,7 @@ import { getMaybeCompositeAssetSymbol } from 'lib/mixpanel/helpers' import { getMixPanel } from 'lib/mixpanel/mixPanelSingleton' import { MixPanelEvent } from 'lib/mixpanel/types' import { isKeepKeyHDWallet } from 'lib/utils' +import { isUtxoAccountId } from 'lib/utils/utxo' import { useGetFiatRampsQuery } from 'state/apis/fiatRamps/fiatRamps' import { isAssetSupportedByWallet } from 'state/slices/portfolioSlice/utils' import { @@ -340,46 +341,48 @@ export const Overview: React.FC = ({ buttonProps={accountDropdownButtonProps} boxProps={accountDropdownBoxProps} /> - - - {!address && } - {address && ( - - - {supportsAddressVerification && address && ( + {accountId && !isUtxoAccountId(accountId) ? ( + + + {!address && } + {address && ( + : } - onClick={handleVerify} - aria-label={translate('common.verify')} + icon={copyIcon} + aria-label={translate('common.copy')} size='sm' - color={ - shownOnDisplay - ? 'green.500' - : shownOnDisplay === false - ? 'red.500' - : 'text.subtle' - } isRound variant='ghost' + onClick={handleCopyClick} /> - )} - - )} - + {supportsAddressVerification && address && ( + : } + onClick={handleVerify} + aria-label={translate('common.verify')} + size='sm' + color={ + shownOnDisplay + ? 'green.500' + : shownOnDisplay === false + ? 'red.500' + : 'text.subtle' + } + isRound + variant='ghost' + /> + )} + + )} + + ) : null} )} diff --git a/src/components/Modals/Nfts/components/NftOverview.tsx b/src/components/Modals/Nfts/components/NftOverview.tsx index 7a246fb561b..d18f7c96834 100644 --- a/src/components/Modals/Nfts/components/NftOverview.tsx +++ b/src/components/Modals/Nfts/components/NftOverview.tsx @@ -5,6 +5,7 @@ import { CopyButton } from 'plugins/walletConnectToDapps/components/modals/CopyB import { useCallback } from 'react' import { useTranslate } from 'react-polyglot' import { AssetIcon } from 'components/AssetIcon' +import { InlineCopyButton } from 'components/InlineCopyButton' import { MiddleEllipsis } from 'components/MiddleEllipsis/MiddleEllipsis' import { Row } from 'components/Row/Row' import { SanitizedHtml } from 'components/SanitizedHtml/SanitizedHtml' @@ -66,26 +67,28 @@ export const NftOverview: React.FC = ({ nftItem }) => { {translate('nft.address')} - + + + )} diff --git a/src/components/Modals/Receive/ReceiveInfo.tsx b/src/components/Modals/Receive/ReceiveInfo.tsx index c0ec22a0859..a89085928fe 100644 --- a/src/components/Modals/Receive/ReceiveInfo.tsx +++ b/src/components/Modals/Receive/ReceiveInfo.tsx @@ -17,6 +17,7 @@ import { } from '@chakra-ui/react' import type { AccountId } from '@shapeshiftoss/caip' import { CHAIN_NAMESPACE, fromAccountId, fromChainId } from '@shapeshiftoss/caip' +import { viemEthMainnetClient } from '@shapeshiftoss/contracts' import { isLedger } from '@shapeshiftoss/hdwallet-ledger' import type { Asset } from '@shapeshiftoss/types' import { KnownChainIds } from '@shapeshiftoss/types' @@ -37,7 +38,6 @@ import { Text } from 'components/Text' import type { TextPropTypes } from 'components/Text/Text' import { getChainAdapterManager } from 'context/PluginProvider/chainAdapterSingleton' import { useWallet } from 'hooks/useWallet/useWallet' -import { viemEthMainnetClient } from 'lib/viem-client' import { selectPortfolioAccountMetadataByAccountId } from 'state/slices/selectors' import { useAppSelector } from 'state/store' diff --git a/src/components/Modals/Send/utils.ts b/src/components/Modals/Send/utils.ts index ad10b9843a0..99acc88ca4a 100644 --- a/src/components/Modals/Send/utils.ts +++ b/src/components/Modals/Send/utils.ts @@ -1,17 +1,10 @@ import type { AccountId, AssetId, ChainId } from '@shapeshiftoss/caip' import { CHAIN_NAMESPACE, fromAccountId, fromChainId } from '@shapeshiftoss/caip' -import type { - CosmosSdkChainId, - EvmChainId, - FeeData, - FeeDataEstimate, - GetFeeDataInput, - UtxoChainId, -} from '@shapeshiftoss/chain-adapters' +import type { FeeData, FeeDataEstimate, GetFeeDataInput } from '@shapeshiftoss/chain-adapters' import { utxoChainIds } from '@shapeshiftoss/chain-adapters' import type { HDWallet } from '@shapeshiftoss/hdwallet-core' import { supportsETH } from '@shapeshiftoss/hdwallet-core' -import type { KnownChainIds } from '@shapeshiftoss/types' +import type { CosmosSdkChainId, EvmChainId, KnownChainIds, UtxoChainId } from '@shapeshiftoss/types' import { checkIsMetaMaskDesktop, checkIsMetaMaskImpersonator, diff --git a/src/components/Modals/Send/views/Confirm.tsx b/src/components/Modals/Send/views/Confirm.tsx index c35a525636f..f149c044b47 100644 --- a/src/components/Modals/Send/views/Confirm.tsx +++ b/src/components/Modals/Send/views/Confirm.tsx @@ -8,6 +8,7 @@ import { Stack, useColorModeValue, } from '@chakra-ui/react' +import { fromAccountId } from '@shapeshiftoss/caip' import { fromAssetId } from '@shapeshiftoss/caip/dist/assetId/assetId' import { CHAIN_NAMESPACE } from '@shapeshiftoss/caip/dist/constants' import type { FeeDataKey } from '@shapeshiftoss/chain-adapters' @@ -19,6 +20,7 @@ import { useTranslate } from 'react-polyglot' import { useHistory } from 'react-router-dom' import { AccountDropdown } from 'components/AccountDropdown/AccountDropdown' import { Amount } from 'components/Amount/Amount' +import { InlineCopyButton } from 'components/InlineCopyButton' import { MiddleEllipsis } from 'components/MiddleEllipsis/MiddleEllipsis' import { DialogBackButton } from 'components/Modal/components/DialogBackButton' import { DialogBody } from 'components/Modal/components/DialogBody' @@ -30,6 +32,7 @@ import { SlideTransition } from 'components/SlideTransition' import { RawText, Text } from 'components/Text' import type { TextPropTypes } from 'components/Text/Text' import { bnOrZero } from 'lib/bignumber/bignumber' +import { isUtxoAccountId } from 'lib/utils/utxo' import { selectAssetById, selectFeeAssetById } from 'state/slices/selectors' import { useAppSelector } from 'state/store' @@ -142,20 +145,29 @@ export const Confirm = () => { - + + + - {vanityAddress ? vanityAddress : } + + + {vanityAddress ? vanityAddress : } + + {allowCustomSendNonce && ( diff --git a/src/components/Modals/Send/views/Details.tsx b/src/components/Modals/Send/views/Details.tsx index 52710f9f51c..0fa92581ae4 100644 --- a/src/components/Modals/Send/views/Details.tsx +++ b/src/components/Modals/Send/views/Details.tsx @@ -11,7 +11,7 @@ import { usePrevious, } from '@chakra-ui/react' import type { AccountId } from '@shapeshiftoss/caip' -import { fromAssetId } from '@shapeshiftoss/caip' +import { fromAccountId, fromAssetId } from '@shapeshiftoss/caip' import { CHAIN_NAMESPACE } from '@shapeshiftoss/caip/dist/constants' import isNil from 'lodash/isNil' import React, { useCallback, useEffect, useMemo } from 'react' @@ -23,6 +23,7 @@ import { useHistory } from 'react-router-dom' import { AccountCard } from 'components/AccountCard' import { AccountDropdown } from 'components/AccountDropdown/AccountDropdown' import { Amount } from 'components/Amount/Amount' +import { InlineCopyButton } from 'components/InlineCopyButton' import { DialogBackButton } from 'components/Modal/components/DialogBackButton' import { DialogBody } from 'components/Modal/components/DialogBody' import { DialogFooter } from 'components/Modal/components/DialogFooter' @@ -35,6 +36,7 @@ import { TokenRow } from 'components/TokenRow/TokenRow' import { useModal } from 'hooks/useModal/useModal' import { useWallet } from 'hooks/useWallet/useWallet' import { bnOrZero } from 'lib/bignumber/bignumber' +import { isUtxoAccountId } from 'lib/utils/utxo' import { selectAssetById } from 'state/slices/selectors' import { useAppSelector } from 'state/store' @@ -54,7 +56,8 @@ const MAX_COSMOS_SDK_MEMO_LENGTH = 256 const controllerRules = { required: true, } -const accountDropdownButtonProps = { width: 'full', mb: 2, variant: 'solid' } +const accountDropdownBoxProps = { width: '100%', my: 0, pl: 0 } +const accountDropdownButtonProps = { width: 'full', variant: 'solid' } const formHelperTextHoverStyle = { color: 'gray.400', transition: '.2s color ease' } export const Details = () => { @@ -220,12 +223,20 @@ export const Details = () => { - + + + + + { return ( - + + + ) } + export const StepperStep = ({ title, stepIndicator, diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowanceApproval.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowanceApproval.tsx index 9421faead96..552289ada06 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowanceApproval.tsx +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowanceApproval.tsx @@ -1,4 +1,5 @@ import { fromAccountId } from '@shapeshiftoss/caip' +import { assertGetViemClient } from '@shapeshiftoss/contracts' import type { TradeQuote, TradeQuoteStep } from '@shapeshiftoss/swapper' import { useMutation } from '@tanstack/react-query' import { useEffect, useMemo } from 'react' @@ -11,7 +12,6 @@ import { } from 'hooks/queries/useApprovalFees' import { useErrorHandler } from 'hooks/useErrorToast/useErrorToast' import { useWallet } from 'hooks/useWallet/useWallet' -import { assertGetViemClient } from 'lib/viem-client' import { selectHopSellAccountId } from 'state/slices/tradeQuoteSlice/selectors' import { tradeQuoteSlice } from 'state/slices/tradeQuoteSlice/tradeQuoteSlice' import { useAppDispatch, useAppSelector } from 'state/store' diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useBlockNumber.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useBlockNumber.tsx index 39d3e55b595..e1862740227 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useBlockNumber.tsx +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useBlockNumber.tsx @@ -1,6 +1,6 @@ -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import { assertGetViemClient } from '@shapeshiftoss/contracts' +import type { EvmChainId } from '@shapeshiftoss/types' import { useEffect, useState } from 'react' -import { assertGetViemClient } from 'lib/viem-client' export const useEvmBlockNumber = (chainId: EvmChainId | undefined, enabled: boolean) => { const [blockNumber, setBlockNumber] = useState() diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useTradeExecution.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useTradeExecution.tsx index 8877e32673a..0d65b441757 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useTradeExecution.tsx +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useTradeExecution.tsx @@ -1,6 +1,6 @@ import type { StdSignDoc } from '@keplr-wallet/types' import { bchAssetId, CHAIN_NAMESPACE, fromChainId } from '@shapeshiftoss/caip' -import type { CosmosSdkChainId, SignTx, SignTypedDataInput } from '@shapeshiftoss/chain-adapters' +import type { SignTx, SignTypedDataInput } from '@shapeshiftoss/chain-adapters' import { toAddressNList } from '@shapeshiftoss/chain-adapters' import type { BuildCustomTxInput } from '@shapeshiftoss/chain-adapters/src/evm/types' import type { BTCSignTx, ETHSignTypedData, ThorchainSignTx } from '@shapeshiftoss/hdwallet-core' @@ -12,6 +12,7 @@ import type { } from '@shapeshiftoss/swapper' import { getHopByIndex, SwapperName, TradeExecutionEvent } from '@shapeshiftoss/swapper' import { LIFI_TRADE_POLL_INTERVAL_MILLISECONDS } from '@shapeshiftoss/swapper/dist/swappers/LifiSwapper/LifiSwapper' +import type { CosmosSdkChainId } from '@shapeshiftoss/types' import type { TypedData } from 'eip-712' import { useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslate } from 'react-polyglot' diff --git a/src/components/MultiHopTrade/components/TradeInput/components/Claim/hooks/useArbitrumClaimTx.tsx b/src/components/MultiHopTrade/components/TradeInput/components/Claim/hooks/useArbitrumClaimTx.tsx index 1f1cad37a66..b2010527703 100644 --- a/src/components/MultiHopTrade/components/TradeInput/components/Claim/hooks/useArbitrumClaimTx.tsx +++ b/src/components/MultiHopTrade/components/TradeInput/components/Claim/hooks/useArbitrumClaimTx.tsx @@ -1,21 +1,19 @@ import { type AccountId, fromAccountId } from '@shapeshiftoss/caip' import { CONTRACT_INTERACTION } from '@shapeshiftoss/chain-adapters' +import { assertGetViemClient, getEthersV5Provider, outboxAbi } from '@shapeshiftoss/contracts' import { KnownChainIds } from '@shapeshiftoss/types' import { TxStatus } from '@shapeshiftoss/unchained-client' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { outboxAbi } from 'contracts/abis/Outbox' import { useMemo } from 'react' import type { Hash } from 'viem' import { encodeFunctionData, getAddress } from 'viem' import { useEvmFees } from 'hooks/queries/useEvmFees' import { useWallet } from 'hooks/useWallet/useWallet' -import { getEthersV5Provider } from 'lib/ethersProviderSingleton' import { assertGetEvmChainAdapter, buildAndBroadcast, createBuildCustomTxInput, } from 'lib/utils/evm' -import { assertGetViemClient } from 'lib/viem-client' import { selectBIP44ParamsByAccountId } from 'state/slices/selectors' import { useAppSelector } from 'state/store' diff --git a/src/components/MultiHopTrade/components/TradeInput/components/Claim/hooks/useArbitrumClaimsByStatus.tsx b/src/components/MultiHopTrade/components/TradeInput/components/Claim/hooks/useArbitrumClaimsByStatus.tsx index e128d07796f..6fbe91b3bc0 100644 --- a/src/components/MultiHopTrade/components/TradeInput/components/Claim/hooks/useArbitrumClaimsByStatus.tsx +++ b/src/components/MultiHopTrade/components/TradeInput/components/Claim/hooks/useArbitrumClaimsByStatus.tsx @@ -2,12 +2,12 @@ import type { ChildToParentMessageReader, ChildToParentTransactionEvent } from ' import { ChildToParentMessageStatus, ChildTransactionReceipt } from '@arbitrum/sdk' import type { AssetId, ChainId } from '@shapeshiftoss/caip' import { ethAssetId, ethChainId } from '@shapeshiftoss/caip' +import { getEthersV5Provider } from '@shapeshiftoss/contracts' import { KnownChainIds } from '@shapeshiftoss/types' import { useQueries } from '@tanstack/react-query' import { useMemo } from 'react' import { useTranslate } from 'react-polyglot' import { ClaimStatus } from 'components/ClaimRow/types' -import { getEthersV5Provider } from 'lib/ethersProviderSingleton' import { assertUnreachable } from 'lib/utils' import { selectAssetById } from 'state/slices/selectors' import type { Tx } from 'state/slices/txHistorySlice/txHistorySlice' diff --git a/src/components/MultiHopTrade/components/VerifyAddresses/VerifyAddresses.tsx b/src/components/MultiHopTrade/components/VerifyAddresses/VerifyAddresses.tsx index f306067d558..cda7afda58e 100644 --- a/src/components/MultiHopTrade/components/VerifyAddresses/VerifyAddresses.tsx +++ b/src/components/MultiHopTrade/components/VerifyAddresses/VerifyAddresses.tsx @@ -1,4 +1,4 @@ -import { CheckCircleIcon } from '@chakra-ui/icons' +import { CheckCircleIcon, WarningIcon } from '@chakra-ui/icons' import { Alert, AlertDescription, @@ -40,6 +40,18 @@ import { useAppSelector } from 'state/store' import { WithBackButton } from '../WithBackButton' +enum AddressVerificationType { + Sell = 'sell', + Buy = 'buy', +} + +enum AddressVerificationStatus { + Pending = 'pending', + Verified = 'verified', + Error = 'error', + NotRequired = 'notRequired', +} + export const VerifyAddresses = () => { const wallet = useWallet().state.wallet const history = useHistory() @@ -50,7 +62,16 @@ export const VerifyAddresses = () => { const [isSellVerifying, setIsSellVerifying] = useState(false) const [isBuyVerifying, setIsBuyVerifying] = useState(false) - const [verifiedAddresses, setVerifiedAddresses] = useState(new Set()) + const [buyAddressVerificationStatus, setBuyAddressVerificationStatus] = + useState(AddressVerificationStatus.Pending) + const [sellAddressVerificationStatus, setSellAddressVerificationStatus] = + useState(AddressVerificationStatus.Pending) + const [buyAddressVerificationErrorMessage, setBuyAddressVerificationErrorMessage] = useState< + string | undefined + >() + const [sellAddressVerificationErrorMessage, setSellAddressVerificationErrorMessage] = useState< + string | undefined + >() const buyAsset = useAppSelector(selectInputBuyAsset) const sellAsset = useAppSelector(selectInputSellAsset) @@ -105,19 +126,11 @@ export const VerifyAddresses = () => { ], ) - const isAddressVerified = useCallback( - (address: string) => verifiedAddresses.has(address.toLowerCase()), - [verifiedAddresses], - ) - const sellVerified = useMemo( - () => isAddressVerified(sellAddress ?? ''), - [isAddressVerified, sellAddress], - ) - const buyVerified = useMemo( - () => Boolean(maybeManualReceiveAddress) || isAddressVerified(buyAddress ?? ''), - - [buyAddress, isAddressVerified, maybeManualReceiveAddress], - ) + useEffect(() => { + if (!shouldVerifyBuyAddress) { + setBuyAddressVerificationStatus(AddressVerificationStatus.NotRequired) + } + }, [shouldVerifyBuyAddress]) const handleContinue = useCallback(() => { history.push({ pathname: TradeRoutePaths.Confirm }) @@ -163,18 +176,68 @@ export const VerifyAddresses = () => { fetchAddresses() }, [fetchAddresses]) - const handleVerify = useCallback( - async (type: 'sell' | 'buy') => { - if (type === 'sell') { - setIsSellVerifying(true) - } else if (type === 'buy') { - setIsBuyVerifying(true) + const isVerifying = useMemo( + () => isSellVerifying || isBuyVerifying, + [isSellVerifying, isBuyVerifying], + ) + + const setIsVerifying = useCallback( + (type: AddressVerificationType, isVerifying: boolean) => { + if (type === AddressVerificationType.Sell) { + setIsSellVerifying(isVerifying) + } else if (type === AddressVerificationType.Buy) { + setIsBuyVerifying(isVerifying) + } + }, + [setIsSellVerifying, setIsBuyVerifying], + ) + + const setErrorMessage = useCallback((type: AddressVerificationType, errorMessage: string) => { + if (type === AddressVerificationType.Sell) { + setSellAddressVerificationErrorMessage(errorMessage) + } else if (type === AddressVerificationType.Buy) { + setBuyAddressVerificationErrorMessage(errorMessage) + } + }, []) + + const clearErrorMessage = useCallback((type: AddressVerificationType) => { + if (type === AddressVerificationType.Sell) { + setSellAddressVerificationErrorMessage(undefined) + } else if (type === AddressVerificationType.Buy) { + setBuyAddressVerificationErrorMessage(undefined) + } + }, []) + + const setStatus = useCallback( + (type: AddressVerificationType, status: AddressVerificationStatus) => { + // if the sell and buy addresses are the same, we can skip the verification for the other address + if (status === AddressVerificationStatus.Verified && sellAddress === buyAddress) { + setBuyAddressVerificationStatus(AddressVerificationStatus.Verified) + setSellAddressVerificationStatus(AddressVerificationStatus.Verified) + return } + if (type === AddressVerificationType.Sell) { + setSellAddressVerificationStatus(status) + } else if (type === AddressVerificationType.Buy) { + setBuyAddressVerificationStatus(status) + } + }, + [sellAddress, buyAddress], + ) + + const handleVerify = useCallback( + async (type: AddressVerificationType) => { + if (isVerifying) return + + setIsVerifying(type, true) + clearErrorMessage(type) + try { - const asset = type === 'sell' ? sellAsset : buyAsset - const accountMetadata = type === 'sell' ? sellAccountMetadata : buyAccountMetadata - const _address = type === 'sell' ? sellAddress : buyAddress + const asset = type === AddressVerificationType.Sell ? sellAsset : buyAsset + const accountMetadata = + type === AddressVerificationType.Sell ? sellAccountMetadata : buyAccountMetadata + const _address = type === AddressVerificationType.Sell ? sellAddress : buyAddress if (!asset || !accountMetadata || !_address || !wallet) return @@ -196,20 +259,21 @@ export const VerifyAddresses = () => { }) if (deviceAddress && deviceAddress.toLowerCase() === _address.toLowerCase()) { - setVerifiedAddresses(new Set([...verifiedAddresses, _address.toLowerCase()])) + setStatus(type, AddressVerificationStatus.Verified) + } else { + setStatus(type, AddressVerificationStatus.Error) + setErrorMessage(type, translate('walletProvider.ledger.verify.addressMismatch')) } } catch (e) { console.error(e) } finally { - if (type === 'sell') { - setIsSellVerifying(false) - } else if (type === 'buy') { - setIsBuyVerifying(false) - } + setIsVerifying(type, false) } }, [ - verifiedAddresses, + isVerifying, + setIsVerifying, + clearErrorMessage, sellAsset, buyAsset, sellAccountMetadata, @@ -217,6 +281,9 @@ export const VerifyAddresses = () => { sellAddress, buyAddress, wallet, + setStatus, + setErrorMessage, + translate, ], ) @@ -226,7 +293,7 @@ export const VerifyAddresses = () => { // Only proceed to verify the buy address if the promise is resolved, i.e the user has opened // the Ledger app without cancelling await checkLedgerAppOpenIfLedgerConnected(buyAsset.chainId) - .then(() => handleVerify('buy')) + .then(() => handleVerify(AddressVerificationType.Buy)) .catch(console.error) }, [checkLedgerAppOpenIfLedgerConnected, handleVerify, buyAsset.chainId]) @@ -234,7 +301,7 @@ export const VerifyAddresses = () => { // Only proceed to verify the sell address if the promise is resolved, i.e the user has opened // the Ledger app without cancelling await checkLedgerAppOpenIfLedgerConnected(sellAsset.chainId) - .then(() => handleVerify('sell')) + .then(() => handleVerify(AddressVerificationType.Sell)) .catch(console.error) }, [checkLedgerAppOpenIfLedgerConnected, handleVerify, sellAsset.chainId]) @@ -258,13 +325,17 @@ export const VerifyAddresses = () => { ) const renderButton = useMemo(() => { - if (!buyVerified) { + if ( + buyAddressVerificationStatus === AddressVerificationStatus.Pending || + buyAddressVerificationStatus === AddressVerificationStatus.Error + ) { return ( ) } + return ( - ) }, [ - buyVerified, - handleBuyVerify, + buyAddressVerificationStatus, + sellAddressVerificationStatus, handleContinue, - handleSellVerify, + handleBuyVerify, isBuyVerifying, - isSellVerifying, - sellVerified, - shouldVerifyBuyAddress, translate, verifyBuyAssetTranslation, + handleSellVerify, + isSellVerifying, verifySellAssetTranslation, ]) @@ -316,7 +385,7 @@ export const VerifyAddresses = () => { return ( - + @@ -341,7 +410,12 @@ export const VerifyAddresses = () => { {isBuyVerifying && } - {buyVerified && } + {buyAddressVerificationStatus === AddressVerificationStatus.Verified && ( + + )} + {buyAddressVerificationStatus === AddressVerificationStatus.Error && ( + + )} @@ -361,7 +435,12 @@ export const VerifyAddresses = () => { {isSellVerifying && } - {sellVerified && } + {sellAddressVerificationStatus === 'verified' && ( + + )} + {sellAddressVerificationStatus === 'error' && ( + + )} @@ -371,7 +450,15 @@ export const VerifyAddresses = () => { - + {Boolean( + buyAddressVerificationErrorMessage || sellAddressVerificationErrorMessage, + ) ? ( + + {buyAddressVerificationErrorMessage ?? sellAddressVerificationErrorMessage} + + ) : ( + + )} {renderButton} diff --git a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteInput.ts b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteInput.ts index 9486cf56670..22e23868c8f 100644 --- a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteInput.ts +++ b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteInput.ts @@ -1,9 +1,14 @@ import { CHAIN_NAMESPACE, fromChainId } from '@shapeshiftoss/caip' -import type { CosmosSdkChainId, EvmChainId, UtxoChainId } from '@shapeshiftoss/chain-adapters' import type { HDWallet } from '@shapeshiftoss/hdwallet-core' import { supportsETH } from '@shapeshiftoss/hdwallet-core' import type { GetTradeQuoteInput } from '@shapeshiftoss/swapper' -import type { Asset, UtxoAccountType } from '@shapeshiftoss/types' +import type { + Asset, + CosmosSdkChainId, + EvmChainId, + UtxoAccountType, + UtxoChainId, +} from '@shapeshiftoss/types' import type { TradeQuoteInputCommonArgs } from 'components/MultiHopTrade/types' import { toBaseUnit } from 'lib/math' import { assertUnreachable } from 'lib/utils' diff --git a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/typeGuards.ts b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/typeGuards.ts index eae306c6201..3eb5cbd7f27 100644 --- a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/typeGuards.ts +++ b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/typeGuards.ts @@ -1,6 +1,6 @@ import type { ChainId } from '@shapeshiftoss/caip' import { CHAIN_NAMESPACE, fromChainId } from '@shapeshiftoss/caip' -import type { CosmosSdkChainId, EvmChainId, UtxoChainId } from '@shapeshiftoss/chain-adapters' +import type { CosmosSdkChainId, EvmChainId, UtxoChainId } from '@shapeshiftoss/types' export const isUtxoSwap = (chainId: ChainId): chainId is UtxoChainId => { const { chainNamespace } = fromChainId(chainId) diff --git a/src/components/TransactionHistoryRows/TransactionDetails/Address.tsx b/src/components/TransactionHistoryRows/TransactionDetails/Address.tsx index 75990c91e12..76501fc247b 100644 --- a/src/components/TransactionHistoryRows/TransactionDetails/Address.tsx +++ b/src/components/TransactionHistoryRows/TransactionDetails/Address.tsx @@ -1,5 +1,6 @@ import { Button, Link } from '@chakra-ui/react' import { useCallback } from 'react' +import { InlineCopyButton } from 'components/InlineCopyButton' import { MiddleEllipsis } from 'components/MiddleEllipsis/MiddleEllipsis' const buttonHover = { bg: 'transparent' } @@ -18,24 +19,28 @@ export const Address = ({ [], ) return explorerAddressLink ? ( - + + + ) : ( - + + + ) } diff --git a/src/config.ts b/src/config.ts index 827d13162b1..712f00b16e4 100644 --- a/src/config.ts +++ b/src/config.ts @@ -2,7 +2,6 @@ import * as envalid from 'envalid' import { bool } from 'envalid' import forEach from 'lodash/forEach' import memoize from 'lodash/memoize' -import type { Address } from 'viem' import env from './env' @@ -135,7 +134,6 @@ const validators = { default: 'https://api-shapeshift.1inch.io/v5.0', }), REACT_APP_SENTRY_DSN_URL: url(), - REACT_APP_RFOX_PROXY_CONTRACT_ADDRESS: str
(), REACT_APP_FEATURE_COVALENT_JAYPEGS: bool({ default: false }), REACT_APP_ALCHEMY_POLYGON_JAYPEGS_API_KEY: str(), REACT_APP_ALCHEMY_OPTIMISM_JAYPEGS_API_KEY: str(), diff --git a/src/context/TransactionsProvider/TransactionsProvider.tsx b/src/context/TransactionsProvider/TransactionsProvider.tsx index 4f3c9fe7287..878ea4aa5d0 100644 --- a/src/context/TransactionsProvider/TransactionsProvider.tsx +++ b/src/context/TransactionsProvider/TransactionsProvider.tsx @@ -6,7 +6,8 @@ import { fromAccountId, fromAssetId, } from '@shapeshiftoss/caip' -import { isEvmChainId, type Transaction } from '@shapeshiftoss/chain-adapters' +import type { Transaction } from '@shapeshiftoss/chain-adapters' +import { isEvmChainId } from '@shapeshiftoss/chain-adapters' import { isLedger } from '@shapeshiftoss/hdwallet-ledger' import { TxStatus } from '@shapeshiftoss/unchained-client' import React, { useCallback, useEffect, useState } from 'react' diff --git a/src/contracts/constants.ts b/src/contracts/constants.ts deleted file mode 100644 index 9d8691935b6..00000000000 --- a/src/contracts/constants.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { getConfig } from 'config' - -export const WETH_TOKEN_CONTRACT_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' -export const FOX_TOKEN_CONTRACT_ADDRESS = '0xc770EEfAd204B5180dF6a14Ee197D99d808ee52d' - -// Staking contract addresses as string literals -export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V1 = - '0xdd80e21669a664bce83e3ad9a0d74f8dad5d9e72' as const -export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V2 = - '0xc54b9f82c1c54e9d4d274d633c7523f2299c42a0' as const -export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V3 = - '0x212ebf9fd3c10f371557b08e993eaab385c3932b' as const -export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V4 = - '0x24fd7fb95dc742e23dc3829d3e656feeb5f67fa0' as const -export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V5 = - '0xc14eaa8284feff79edc118e06cadbf3813a7e555' as const -export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V6 = - '0xebb1761ad43034fd7faa64d84e5bbd8cb5c40b68' as const -export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V7 = - '0x5939783dbf3e9f453a69bc9ddc1e492efac1fbcb' as const -export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V8 = - '0x662da6c777a258382f08b979d9489c3fbbbd8ac3' as const -export const ETH_FOX_STAKING_CONTRACT_ADDRESS_V9 = - '0x721720784b76265aa3e34c1c7ba02a6027bcd3e5' as const - -// Exported as a string literal for contract address discrimination purposes -export const ETH_FOX_POOL_CONTRACT_ADDRESS = '0x470e8de2eBaef52014A47Cb5E6aF86884947F08c' as const - -// Exported as a string literal for contract address discrimination purposes -export const UNISWAP_V2_ROUTER_02_CONTRACT_ADDRESS = - '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' as const - -// Checksummed addresses - used to check against unchained Txs - -export const THOR_ROUTER_CONTRACT_ADDRESS_ETHEREUM = '0xd37bbe5744d730a1d98d8dc97c42f0ca46ad7146' - -// RFOX on Arbitrum ERC1967Proxy contract address -export const RFOX_PROXY_CONTRACT_ADDRESS = getConfig().REACT_APP_RFOX_PROXY_CONTRACT_ADDRESS -export const RFOX_REWARD_RATE = 1n * 10n ** 27n -export const RFOX_WAD = 1n * 10n ** 18n diff --git a/src/contracts/contractManager.ts b/src/contracts/contractManager.ts deleted file mode 100644 index ca34a0c3d26..00000000000 --- a/src/contracts/contractManager.ts +++ /dev/null @@ -1,153 +0,0 @@ -import type { AssetId, ChainId } from '@shapeshiftoss/caip' -import { fromAssetId, toAssetId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' -import { KnownChainIds } from '@shapeshiftoss/types' -import type { Token } from '@uniswap/sdk' -import { Fetcher } from '@uniswap/sdk' -import assert from 'assert' -import { erc20ABI } from 'contracts/abis/ERC20ABI' -import { FarmingABI } from 'contracts/abis/farmingAbi' -import { IUniswapV2Pair } from 'contracts/abis/IUniswapV2Pair' -import { IUniswapV2Router02 } from 'contracts/abis/IUniswapV2Router02' -import { THORChain_RouterABI } from 'contracts/abis/THORCHAIN_RouterABI' -import memoize from 'lodash/memoize' -import type { Address } from 'viem' -import { getAddress, getContract } from 'viem' -import { getEthersV5Provider } from 'lib/ethersProviderSingleton' -import { viemClientByChainId, viemEthMainnetClient } from 'lib/viem-client' - -import { - ETH_FOX_POOL_CONTRACT_ADDRESS, - ETH_FOX_STAKING_CONTRACT_ADDRESS_V1, - ETH_FOX_STAKING_CONTRACT_ADDRESS_V2, - ETH_FOX_STAKING_CONTRACT_ADDRESS_V3, - ETH_FOX_STAKING_CONTRACT_ADDRESS_V4, - ETH_FOX_STAKING_CONTRACT_ADDRESS_V5, - ETH_FOX_STAKING_CONTRACT_ADDRESS_V6, - ETH_FOX_STAKING_CONTRACT_ADDRESS_V7, - ETH_FOX_STAKING_CONTRACT_ADDRESS_V8, - ETH_FOX_STAKING_CONTRACT_ADDRESS_V9, - FOX_TOKEN_CONTRACT_ADDRESS, - THOR_ROUTER_CONTRACT_ADDRESS_ETHEREUM, - UNISWAP_V2_ROUTER_02_CONTRACT_ADDRESS, -} from './constants' -import type { - DefinedContract, - KnownContractAddress, - KnownContractByAddress, - KnownContractByType, -} from './types' -import { ContractType } from './types' - -const definedContracts: DefinedContract[] = [] - -export const CONTRACT_ADDRESS_TO_ABI = { - [ETH_FOX_POOL_CONTRACT_ADDRESS]: IUniswapV2Pair, - [ETH_FOX_STAKING_CONTRACT_ADDRESS_V1]: FarmingABI, - [ETH_FOX_STAKING_CONTRACT_ADDRESS_V2]: FarmingABI, - [ETH_FOX_STAKING_CONTRACT_ADDRESS_V3]: FarmingABI, - [ETH_FOX_STAKING_CONTRACT_ADDRESS_V4]: FarmingABI, - [ETH_FOX_STAKING_CONTRACT_ADDRESS_V5]: FarmingABI, - [ETH_FOX_STAKING_CONTRACT_ADDRESS_V6]: FarmingABI, - [ETH_FOX_STAKING_CONTRACT_ADDRESS_V7]: FarmingABI, - [ETH_FOX_STAKING_CONTRACT_ADDRESS_V8]: FarmingABI, - [ETH_FOX_STAKING_CONTRACT_ADDRESS_V9]: FarmingABI, - [FOX_TOKEN_CONTRACT_ADDRESS]: erc20ABI, - [UNISWAP_V2_ROUTER_02_CONTRACT_ADDRESS]: IUniswapV2Router02, - // THOR Router Mainnet - [THOR_ROUTER_CONTRACT_ADDRESS_ETHEREUM]: THORChain_RouterABI, -} as const - -export const CONTRACT_TYPE_TO_ABI = { - [ContractType.UniV2Pair]: IUniswapV2Pair, - [ContractType.ERC20]: erc20ABI, - [ContractType.ThorRouter]: THORChain_RouterABI, -} as const - -export const getOrCreateContractByAddress = ( - address: T, -): KnownContractByAddress => { - const definedContract = definedContracts.find(contract => contract.address === address) - if (definedContract && definedContract.contract) - return definedContract.contract as unknown as KnownContractByAddress - const contractAbi = CONTRACT_ADDRESS_TO_ABI[address] - - const contract = getContract({ - abi: contractAbi, - address, - client: viemEthMainnetClient, - }) as KnownContractByAddress - definedContracts.push({ contract, address } as unknown as DefinedContract) - return contract -} - -export const getOrCreateContractByType = ({ - address, - type, - chainId, -}: { - address: string | `0x${string}` - type: T - chainId: ChainId -}): KnownContractByType => { - const definedContract = definedContracts.find(contract => contract.address === address) - if (definedContract && definedContract.contract) - return definedContract.contract as unknown as KnownContractByType - - const publicClient = viemClientByChainId[chainId as EvmChainId] - assert(publicClient !== undefined, `no public client found for chainId '${chainId}'`) - - const contract = getContract({ - abi: CONTRACT_TYPE_TO_ABI[type], - address: address as Address, - client: publicClient, - }) - definedContracts.push({ - contract, - address: getAddress(address), - } as unknown as DefinedContract) - return contract as KnownContractByType -} - -export const fetchUniV2PairData = memoize(async (pairAssetId: AssetId) => { - const { assetReference, chainId } = fromAssetId(pairAssetId) - // Checksum - const contractAddress = getAddress(assetReference) - const pair = getOrCreateContractByType({ - address: contractAddress, - type: ContractType.UniV2Pair, - chainId: KnownChainIds.EthereumMainnet, - }) - const ethersV5Provider = getEthersV5Provider() - - const token0Address = await pair.read.token0() - const token1Address = await pair.read.token1() - const token0AssetId = toAssetId({ - chainId, - assetNamespace: 'erc20', - assetReference: token0Address, - }) - const token1AssetId = toAssetId({ - chainId, - assetNamespace: 'erc20', - assetReference: token1Address, - }) - - const { chainReference: asset0EvmChainId, assetReference: asset0Address } = - fromAssetId(token0AssetId) - const { chainReference: asset1EvmChainId, assetReference: asset1Address } = - fromAssetId(token1AssetId) - - const token0: Token = await Fetcher.fetchTokenData( - Number(asset0EvmChainId), - asset0Address, - ethersV5Provider, - ) - const token1: Token = await Fetcher.fetchTokenData( - Number(asset1EvmChainId), - asset1Address, - ethersV5Provider, - ) - - return Fetcher.fetchPairData(token0, token1, ethersV5Provider) -}) diff --git a/src/contracts/types.ts b/src/contracts/types.ts deleted file mode 100644 index 648ed98ace3..00000000000 --- a/src/contracts/types.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { Address, GetContractReturnType, PublicClient } from 'viem' -import type { FoxEthStakingContractAddress } from 'state/slices/opportunitiesSlice/constants' - -import type { - ETH_FOX_POOL_CONTRACT_ADDRESS, - FOX_TOKEN_CONTRACT_ADDRESS, - THOR_ROUTER_CONTRACT_ADDRESS_ETHEREUM, - UNISWAP_V2_ROUTER_02_CONTRACT_ADDRESS, -} from './constants' -import type { CONTRACT_ADDRESS_TO_ABI, CONTRACT_TYPE_TO_ABI } from './contractManager' - -export enum ContractType { - UniV2Pair = 'UniV2Pair', - ERC20 = 'ERC20', - ThorRouter = 'ThorRouter', -} - -export type KnownContractByAddress = GetContractReturnType< - (typeof CONTRACT_ADDRESS_TO_ABI)[T], - PublicClient, - Address -> - -export type KnownContractByType = GetContractReturnType< - (typeof CONTRACT_TYPE_TO_ABI)[T], - PublicClient, - Address -> - -export type KnownContractAddress = - | typeof ETH_FOX_POOL_CONTRACT_ADDRESS - | FoxEthStakingContractAddress - | typeof FOX_TOKEN_CONTRACT_ADDRESS - | typeof UNISWAP_V2_ROUTER_02_CONTRACT_ADDRESS - | typeof THOR_ROUTER_CONTRACT_ADDRESS_ETHEREUM - -export type DefinedContract = { - contract: KnownContractByAddress - address: KnownContractAddress | `0x${string}` -} diff --git a/src/features/defi/components/Overview/Overview.tsx b/src/features/defi/components/Overview/Overview.tsx index 2664faf2b09..1823260f465 100644 --- a/src/features/defi/components/Overview/Overview.tsx +++ b/src/features/defi/components/Overview/Overview.tsx @@ -18,6 +18,7 @@ import { Amount } from 'components/Amount/Amount' import type { AssetDescriptionTeaserProps } from 'components/AssetDescriptionTeaser' import { AssetDescriptionTeaser } from 'components/AssetDescriptionTeaser' import { AssetIcon } from 'components/AssetIcon' +import { InlineCopyButton } from 'components/InlineCopyButton' import { RawText, Text } from 'components/Text' import type { DefiActionButtonProps } from '../DefiActionButtons' @@ -35,6 +36,7 @@ export type AssetWithBalance = { type OverviewProps = { accountId?: AccountId | undefined onAccountIdChange?: (accountId: AccountId) => void + positionAddress?: string | undefined // The LP asset this opportunity represents lpAsset?: AssetWithBalance // The assets underlying the LP one @@ -69,6 +71,7 @@ const accountDropdownBoxProps = { px: 0, my: 0, width: 'full' } export const Overview: React.FC = ({ accountId, onAccountIdChange, + positionAddress, lpAsset, underlyingAssetsCryptoPrecision, rewardAssetsCryptoPrecision, @@ -109,7 +112,7 @@ export const Overview: React.FC = ({ {onAccountIdChange && ( <> - + = ({ buttonProps={accountDropdownButtonProps} boxProps={accountDropdownBoxProps} /> - + )} diff --git a/src/features/defi/providers/cosmos/components/CosmosManager/Claim/components/Confirm.tsx b/src/features/defi/providers/cosmos/components/CosmosManager/Claim/components/Confirm.tsx index 662c17d0653..9c71a472b13 100644 --- a/src/features/defi/providers/cosmos/components/CosmosManager/Claim/components/Confirm.tsx +++ b/src/features/defi/providers/cosmos/components/CosmosManager/Claim/components/Confirm.tsx @@ -14,6 +14,7 @@ import { useTranslate } from 'react-polyglot' import { Amount } from 'components/Amount/Amount' import { AssetIcon } from 'components/AssetIcon' import type { StepComponentProps } from 'components/DeFi/components/Steps' +import { InlineCopyButton } from 'components/InlineCopyButton' import { MiddleEllipsis } from 'components/MiddleEllipsis/MiddleEllipsis' import { Row } from 'components/Row/Row' import { Text } from 'components/Text' @@ -193,9 +194,15 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { - - {userAddress && } - + + + {userAddress && } + + diff --git a/src/features/defi/providers/cosmos/components/CosmosManager/Claim/components/Status.tsx b/src/features/defi/providers/cosmos/components/CosmosManager/Claim/components/Status.tsx index a33c0c42b87..1905192a0ab 100644 --- a/src/features/defi/providers/cosmos/components/CosmosManager/Claim/components/Status.tsx +++ b/src/features/defi/providers/cosmos/components/CosmosManager/Claim/components/Status.tsx @@ -12,6 +12,7 @@ import { Amount } from 'components/Amount/Amount' import { AssetIcon } from 'components/AssetIcon' import { CircularProgress } from 'components/CircularProgress/CircularProgress' import { IconCircle } from 'components/IconCircle' +import { InlineCopyButton } from 'components/InlineCopyButton' import { MiddleEllipsis } from 'components/MiddleEllipsis/MiddleEllipsis' import { Row } from 'components/Row/Row' import { RawText } from 'components/Text' @@ -151,9 +152,15 @@ export const Status: React.FC = ({ accountId }) => { {translate('defi.modals.claim.claimToAddress')} - - {userAddress && } - + + + {userAddress && } + +