From e463bbe2385be126575ec96beca0062ceef2614b Mon Sep 17 00:00:00 2001 From: Matthew Russell Date: Tue, 7 Jun 2022 15:08:40 -0700 Subject: [PATCH] feat(#495): get smart contracts addresses from network params * feat: unhardcode contract addresses * fix: linting and tests * feat: switch contract usage in token app to use unhardcoded addresses * chore: remove other usage of hard coded contract addresses * feat: convert contracts to classes, update claim contract to fix circular dependency * feat: add hard coded contract addresses to contracts page * fix: misc tidy up * chore: rename ethers big num conversion func * fix: remove pending transactions modal * chore: add single toBigNum function that can accept number string or EthersBignNumber * chore: delete unused tranche helpers and decimals functions from smart contracts lib * feat: add faucetable token class * fix: reset tx state after early exit from approve tx * feat: re add transaction modal using zustand store * fix: loader colors for eth wallet * fix: pass ethereum config to gurantee existence before tx execution * chore: lint smart contracts lib * chore: fix web3container to use children and not render prop * chore: lint * fix: use background to mock ethereum wallet to avoid mocking globally for every test * chore: move web3 mock to common steps and call from withdrawals feature tests --- apps/token/src/app-loader.tsx | 10 +- .../add-locked-token/add-locked-token.tsx | 4 +- .../balance-manager/balance-manager.tsx | 36 ++-- .../src/components/eth-wallet/eth-wallet.tsx | 14 +- .../transactions-modal/transactions-modal.tsx | 8 +- apps/token/src/config/vega.ts | 2 +- .../contexts/contracts/contracts-context.ts | 22 +-- .../contexts/contracts/contracts-provider.tsx | 73 +++---- .../src/hooks/use-add-asset-to-wallet.ts | 3 +- apps/token/src/hooks/use-ethereum-config.ts | 36 ---- .../hooks/use-get-association-breakdown.ts | 75 +++++++- .../hooks/use-get-user-tranche-balances.ts | 22 ++- .../src/hooks/use-pending-transactions.ts | 5 +- .../hooks/use-refresh-associated-balances.ts | 4 +- apps/token/src/hooks/use-refresh-balances.ts | 48 +++-- apps/token/src/hooks/use-tranches.ts | 3 +- apps/token/src/hooks/use-transaction.ts | 10 +- apps/token/src/lib/ascii-to-hex.ts | 17 -- apps/token/src/lib/decimals.ts | 1 + apps/token/src/routes/claim/hooks.ts | 7 + apps/token/src/routes/contracts/index.tsx | 52 ++++- .../home/token-details/token-details.tsx | 9 +- .../associate/associate-page-container.tsx | 48 ++--- .../staking/associate/associate-page.tsx | 10 +- .../src/routes/staking/associate/hooks.ts | 10 +- .../staking/associate/wallet-associate.tsx | 23 ++- .../src/routes/staking/disassociate/hooks.ts | 8 +- ...-label.spex.tsx => tranche-label.spec.tsx} | 20 +- .../src/routes/tranches/tranche-label.tsx | 16 +- apps/token/src/routes/tranches/tranche.tsx | 17 +- apps/token/src/routes/tranches/tranches.tsx | 16 +- apps/token/src/stores/transactions.ts | 42 ++++ .../src/integration/deposits.feature | 3 +- .../src/integration/withdrawals.feature | 3 +- .../support/step_definitions/common.step.ts | 4 + .../support/step_definitions/deposits.step.ts | 4 - .../web3-container/web3-container.spec.tsx | 45 +---- .../web3-container/web3-container.tsx | 110 +++-------- .../portfolio/deposit/deposit-container.tsx | 9 +- .../pages/portfolio/deposit/index.page.tsx | 14 +- apps/trading/pages/portfolio/index.page.tsx | 112 ++++++----- .../pages/portfolio/withdraw/index.page.tsx | 14 +- .../portfolio/withdrawals/index.page.tsx | 24 ++- libs/deposits/src/lib/deposit-manager.tsx | 38 ++-- libs/deposits/src/lib/use-get-allowance.ts | 27 ++- .../src/lib/use-get-balance-of-erc20-token.ts | 16 +- .../src/lib/use-get-deposit-limits.ts | 32 ++- libs/deposits/src/lib/use-submit-approval.ts | 18 +- libs/deposits/src/lib/use-submit-deposit.ts | 16 +- libs/deposits/src/lib/use-submit-faucet.ts | 7 +- .../src/hooks/use-environment.tsx | 46 ++++- libs/react-helpers/src/index.ts | 1 + .../src/lib/environment.ts} | 0 libs/react-helpers/src/lib/format/number.ts | 16 +- libs/smart-contracts/src/config/ethereum.ts | 126 ------------ libs/smart-contracts/src/config/index.ts | 2 - .../src/contracts/base-contract.ts | 73 ------- .../src/contracts/{vega-claim.ts => claim.ts} | 109 ++++------- .../src/contracts/collateral-bridge.ts | 51 +++++ .../src/contracts/erc20-token.ts | 111 ----------- libs/smart-contracts/src/contracts/index.ts | 14 +- .../src/contracts/stake-helpers.ts | 40 ---- .../src/contracts/staking-bridge.ts | 32 +++ .../src/contracts/token-faucetable.ts | 37 ++++ .../src/contracts/token-vesting.ts | 41 ++++ libs/smart-contracts/src/contracts/token.ts | 30 +++ .../src/contracts/tranche-helpers.ts | 165 ---------------- .../src/contracts/vega-erc20-bridge.ts | 109 ----------- .../src/contracts/vega-staking.ts | 131 ------------- .../src/contracts/vega-vesting.ts | 182 ------------------ .../src/contracts/vega-web3-types.ts | 8 - libs/smart-contracts/src/index.ts | 1 - .../src/utils/decimals.test.ts | 22 --- libs/smart-contracts/src/utils/decimals.ts | 10 - libs/smart-contracts/src/utils/index.ts | 1 - .../src/components/loader/loader.tsx | 14 +- libs/web3/src/index.ts | 7 +- .../lib}/__generated__/NetworkParamsQuery.ts | 0 libs/web3/src/lib/use-bridge-contract.ts | 18 +- libs/web3/src/lib/use-ethereum-config.ts | 64 ++++++ libs/web3/src/lib/use-token-contract.ts | 19 +- libs/web3/src/lib/use-token-decimals.ts | 20 ++ .../src/lib/use-complete-withdraw.ts | 8 +- libs/withdraws/src/lib/use-withdraw.ts | 8 +- 84 files changed, 1011 insertions(+), 1672 deletions(-) delete mode 100644 apps/token/src/hooks/use-ethereum-config.ts delete mode 100644 apps/token/src/lib/ascii-to-hex.ts rename apps/token/src/routes/tranches/{tranche-label.spex.tsx => tranche-label.spec.tsx} (65%) create mode 100644 apps/token/src/stores/transactions.ts rename libs/{smart-contracts/src/config/vega.ts => react-helpers/src/lib/environment.ts} (100%) delete mode 100644 libs/smart-contracts/src/config/ethereum.ts delete mode 100644 libs/smart-contracts/src/config/index.ts delete mode 100644 libs/smart-contracts/src/contracts/base-contract.ts rename libs/smart-contracts/src/contracts/{vega-claim.ts => claim.ts} (51%) create mode 100644 libs/smart-contracts/src/contracts/collateral-bridge.ts delete mode 100644 libs/smart-contracts/src/contracts/erc20-token.ts delete mode 100644 libs/smart-contracts/src/contracts/stake-helpers.ts create mode 100644 libs/smart-contracts/src/contracts/staking-bridge.ts create mode 100644 libs/smart-contracts/src/contracts/token-faucetable.ts create mode 100644 libs/smart-contracts/src/contracts/token-vesting.ts create mode 100644 libs/smart-contracts/src/contracts/token.ts delete mode 100644 libs/smart-contracts/src/contracts/tranche-helpers.ts delete mode 100644 libs/smart-contracts/src/contracts/vega-erc20-bridge.ts delete mode 100644 libs/smart-contracts/src/contracts/vega-staking.ts delete mode 100644 libs/smart-contracts/src/contracts/vega-vesting.ts delete mode 100644 libs/smart-contracts/src/utils/decimals.test.ts delete mode 100644 libs/smart-contracts/src/utils/decimals.ts rename {apps/trading/components/web3-container => libs/web3/src/lib}/__generated__/NetworkParamsQuery.ts (100%) create mode 100644 libs/web3/src/lib/use-ethereum-config.ts create mode 100644 libs/web3/src/lib/use-token-decimals.ts diff --git a/apps/token/src/app-loader.tsx b/apps/token/src/app-loader.tsx index 2c06ea95f..3ea4ceea8 100644 --- a/apps/token/src/app-loader.tsx +++ b/apps/token/src/app-loader.tsx @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/react'; +import { toBigNum } from '@vegaprotocol/react-helpers'; import { Splash } from '@vegaprotocol/ui-toolkit'; import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet'; import { useWeb3React } from '@web3-react/core'; @@ -43,11 +44,16 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => { vesting.totalStaked(), token.decimals(), ]); + + const totalSupply = toBigNum(supply, decimals); + const totalWallet = toBigNum(totalAssociatedWallet, decimals); + const totalVesting = toBigNum(totalAssociatedVesting, decimals); + appDispatch({ type: AppStateActionType.SET_TOKEN, decimals, - totalSupply: supply, - totalAssociated: totalAssociatedWallet.plus(totalAssociatedVesting), + totalSupply, + totalAssociated: totalWallet.plus(totalVesting), }); setBalancesLoaded(true); } catch (err) { diff --git a/apps/token/src/components/add-locked-token/add-locked-token.tsx b/apps/token/src/components/add-locked-token/add-locked-token.tsx index 275e0aa76..429498516 100644 --- a/apps/token/src/components/add-locked-token/add-locked-token.tsx +++ b/apps/token/src/components/add-locked-token/add-locked-token.tsx @@ -1,15 +1,15 @@ import { useTranslation } from 'react-i18next'; -import { useEnvironment } from '@vegaprotocol/react-helpers'; import { useAddAssetSupported } from '../../hooks/use-add-asset-to-wallet'; import vegaVesting from '../../images/vega_vesting.png'; import { AddTokenButtonLink } from '../add-token-button/add-token-button'; import { Callout } from '@vegaprotocol/ui-toolkit'; +import { useEnvironment } from '@vegaprotocol/react-helpers'; export const AddLockedTokenAddress = () => { - const { ADDRESSES } = useEnvironment(); const { t } = useTranslation(); const addSupported = useAddAssetSupported(); + const { ADDRESSES } = useEnvironment(); return ( { - const { ADDRESSES } = useEnvironment(); const contracts = useContracts(); const { account } = useWeb3React(); - const { appDispatch } = useAppState(); + const { + appState: { decimals }, + appDispatch, + } = useAppState(); + const { config } = useEthereumConfig(); const getUserTrancheBalances = useGetUserTrancheBalances( account || '', @@ -35,17 +37,26 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => { // update balances on connect to Ethereum React.useEffect(() => { const updateBalances = async () => { - if (!account) return; + if (!account || !config) return; try { - const [balance, walletBalance, lien, allowance] = await Promise.all([ - contracts.vesting.getUserBalanceAllTranches(account), + const [b, w, stats, a] = await Promise.all([ + contracts.vesting.userTotalAllTranches(account), contracts.token.balanceOf(account), - contracts.vesting.getLien(account), - contracts.token.allowance(account, ADDRESSES.stakingBridge), + contracts.vesting.userStats(account), + contracts.token.allowance( + account, + config.staking_bridge_contract.address + ), ]); + + const balance = toBigNum(b, decimals); + const walletBalance = toBigNum(w, decimals); + const lien = toBigNum(stats.lien, decimals); + const allowance = toBigNum(a, decimals); + appDispatch({ type: AppStateActionType.UPDATE_ACCOUNT_BALANCES, - balance: new BigNumber(balance), + balance, walletBalance, lien, allowance, @@ -57,11 +68,12 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => { updateBalances(); }, [ + decimals, appDispatch, contracts?.token, contracts?.vesting, account, - ADDRESSES.stakingBridge, + config, ]); // This use effect hook is very expensive and is kept separate to prevent expensive reloading of data. diff --git a/apps/token/src/components/eth-wallet/eth-wallet.tsx b/apps/token/src/components/eth-wallet/eth-wallet.tsx index 5e70d1589..014ec8f91 100644 --- a/apps/token/src/components/eth-wallet/eth-wallet.tsx +++ b/apps/token/src/components/eth-wallet/eth-wallet.tsx @@ -22,7 +22,7 @@ import { WalletCardHeader, WalletCardRow, } from '../wallet-card'; -import { Button, Loader } from '@vegaprotocol/ui-toolkit'; +import { Loader } from '@vegaprotocol/ui-toolkit'; import { theme } from '@vegaprotocol/tailwindcss-config'; const Colors = theme.colors; @@ -189,12 +189,12 @@ export const EthWallet = () => {

{t('ethereumKey')}

{account && ( -
-
{truncateMiddle(account)}
+
+
{truncateMiddle(account)}
{pendingTxs && (
- +
)}
diff --git a/apps/token/src/components/transactions-modal/transactions-modal.tsx b/apps/token/src/components/transactions-modal/transactions-modal.tsx index c886afb5f..c1d8fc67a 100644 --- a/apps/token/src/components/transactions-modal/transactions-modal.tsx +++ b/apps/token/src/components/transactions-modal/transactions-modal.tsx @@ -1,4 +1,3 @@ -import type { TxData } from '@vegaprotocol/smart-contracts'; import { Dialog, Link } from '@vegaprotocol/ui-toolkit'; import { useEnvironment } from '@vegaprotocol/react-helpers'; import React from 'react'; @@ -8,9 +7,10 @@ import { AppStateActionType, useAppState, } from '../../contexts/app-state/app-state-context'; -import { useContracts } from '../../contexts/contracts/contracts-context'; import { truncateMiddle } from '../../lib/truncate-middle'; import { Tick } from '../icons'; +import type { TxData } from '../../stores/transactions'; +import { useTransactionStore } from '../../stores/transactions'; const TransactionModalTh = ({ children }: { children: React.ReactNode }) => ( @@ -31,7 +31,7 @@ const TransactionModalStatus = ({ export const TransactionModal = () => { const { ETHERSCAN_URL } = useEnvironment(); const { t } = useTranslation(); - const { transactions } = useContracts(); + const { transactions } = useTransactionStore(); const { appState, appDispatch } = useAppState(); const renderStatus = (txObj: TxData) => { @@ -84,8 +84,8 @@ export const TransactionModal = () => { {truncateMiddle(transaction.tx.hash)} diff --git a/apps/token/src/config/vega.ts b/apps/token/src/config/vega.ts index c5bd9f4d9..ac584cde6 100644 --- a/apps/token/src/config/vega.ts +++ b/apps/token/src/config/vega.ts @@ -1,4 +1,4 @@ -import { Networks } from '@vegaprotocol/smart-contracts'; +import { Networks } from '@vegaprotocol/react-helpers'; interface VegaNode { url: string; diff --git a/apps/token/src/contexts/contracts/contracts-context.ts b/apps/token/src/contexts/contracts/contracts-context.ts index d5ec9bc67..a415f68c8 100644 --- a/apps/token/src/contexts/contracts/contracts-context.ts +++ b/apps/token/src/contexts/contracts/contracts-context.ts @@ -1,20 +1,18 @@ import type { - TxData, - VegaClaim, - VegaErc20Bridge, - VegaStaking, - ERC20Token, - VegaVesting, + Claim, + CollateralBridge, + Token, + TokenVesting, + StakingBridge, } from '@vegaprotocol/smart-contracts'; import React from 'react'; export interface ContractsContextShape { - token: ERC20Token; - staking: VegaStaking; - vesting: VegaVesting; - claim: VegaClaim; - erc20Bridge: VegaErc20Bridge; - transactions: TxData[]; + token: Token; + staking: StakingBridge; + vesting: TokenVesting; + claim: Claim; + erc20Bridge: CollateralBridge; } export const ContractsContext = React.createContext< diff --git a/apps/token/src/contexts/contracts/contracts-provider.tsx b/apps/token/src/contexts/contracts/contracts-provider.tsx index df74c5459..8569bbd71 100644 --- a/apps/token/src/contexts/contracts/contracts-provider.tsx +++ b/apps/token/src/contexts/contracts/contracts-provider.tsx @@ -1,29 +1,28 @@ -import type { TxData } from '@vegaprotocol/smart-contracts'; import { - VegaClaim, - VegaErc20Bridge, - VegaStaking, - ERC20Token, - VegaVesting, + Token, + TokenVesting, + Claim, + CollateralBridge, + StakingBridge, } from '@vegaprotocol/smart-contracts'; import { Splash } from '@vegaprotocol/ui-toolkit'; import { useWeb3React } from '@web3-react/core'; -import uniqBy from 'lodash/uniqBy'; import React from 'react'; -import { useEnvironment } from '@vegaprotocol/react-helpers'; import { SplashLoader } from '../../components/splash-loader'; import type { ContractsContextShape } from './contracts-context'; import { ContractsContext } from './contracts-context'; import { defaultProvider } from '../../lib/web3-connectors'; +import { useEthereumConfig } from '@vegaprotocol/web3'; +import { useEnvironment } from '@vegaprotocol/react-helpers'; /** * Provides Vega Ethereum contract instances to its children. */ export const ContractsProvider = ({ children }: { children: JSX.Element }) => { - const { ADDRESSES, VEGA_ENV } = useEnvironment(); const { provider: activeProvider, account } = useWeb3React(); - const [txs, setTxs] = React.useState([]); + const { config } = useEthereumConfig(); + const { VEGA_ENV, ADDRESSES } = useEnvironment(); const [contracts, setContracts] = React.useState { signer = provider.getSigner(); } - if (provider) { + if (provider && config) { setContracts({ - token: new ERC20Token( - ADDRESSES.vegaTokenAddress, - // @ts-ignore Cant accept JsonRpcProvider provider - provider, - signer + token: new Token(ADDRESSES.vegaTokenAddress, signer || provider), + staking: new StakingBridge( + config.staking_bridge_contract.address, + signer || provider ), - // @ts-ignore Cant accept JsonRpcProvider provider - staking: new VegaStaking(VEGA_ENV, provider, signer), - // @ts-ignore Cant accept JsonRpcProvider provider - vesting: new VegaVesting(VEGA_ENV, provider, signer), - // @ts-ignore Cant accept JsonRpcProvider provider - claim: new VegaClaim(VEGA_ENV, provider, signer), - erc20Bridge: new VegaErc20Bridge( - VEGA_ENV, - // @ts-ignore Cant accept JsonRpcProvider provider - provider, - signer + vesting: new TokenVesting( + config.token_vesting_contract.address, + signer || provider + ), + claim: new Claim(ADDRESSES.claimAddress, signer || provider), + erc20Bridge: new CollateralBridge( + config.collateral_bridge_contract.address, + signer || provider ), }); } - }, [activeProvider, account, ADDRESSES.vegaTokenAddress, VEGA_ENV]); - - React.useEffect(() => { - if (!contracts) return; - - const mergeTxs = (existing: TxData[], incoming: TxData[]) => { - return uniqBy([...incoming, ...existing], 'tx.hash'); - }; - - contracts.staking.listen((txs) => { - setTxs((curr) => mergeTxs(curr, txs)); - }); - - contracts.vesting.listen((txs) => { - setTxs((curr) => mergeTxs(curr, txs)); - }); - }, [contracts]); - - React.useEffect(() => { - setTxs([]); - }, [account]); + }, [activeProvider, account, config, ADDRESSES, VEGA_ENV]); if (!contracts) { return ( @@ -98,7 +73,7 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => { } return ( - + {children} ); diff --git a/apps/token/src/hooks/use-add-asset-to-wallet.ts b/apps/token/src/hooks/use-add-asset-to-wallet.ts index 886fc50ef..2b9b5f505 100644 --- a/apps/token/src/hooks/use-add-asset-to-wallet.ts +++ b/apps/token/src/hooks/use-add-asset-to-wallet.ts @@ -1,9 +1,8 @@ import React from 'react'; import * as Sentry from '@sentry/react'; +import { Networks, useEnvironment } from '@vegaprotocol/react-helpers'; import { useWeb3React } from '@web3-react/core'; import { MetaMask } from '@web3-react/metamask'; -import { useEnvironment } from '@vegaprotocol/react-helpers'; -import { Networks } from '@vegaprotocol/smart-contracts'; export const useAddAssetSupported = () => { const { connector } = useWeb3React(); diff --git a/apps/token/src/hooks/use-ethereum-config.ts b/apps/token/src/hooks/use-ethereum-config.ts deleted file mode 100644 index 16ea2b05c..000000000 --- a/apps/token/src/hooks/use-ethereum-config.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as Sentry from '@sentry/react'; -import React from 'react'; - -import { NetworkParams } from '../config'; -import { useNetworkParam } from './use-network-param'; - -export const useEthereumConfig = () => { - const { data: ethereumConfigJSON, loading } = useNetworkParam([ - NetworkParams.ETHEREUM_CONFIG, - ]); - const ethereumConfig = React.useMemo(() => { - if (!ethereumConfigJSON && !loading) { - Sentry.captureMessage( - `No ETH config found for network param ${NetworkParams.ETHEREUM_CONFIG}` - ); - return null; - } else if (!ethereumConfigJSON) { - return null; - } - try { - const [configJson] = ethereumConfigJSON; - return JSON.parse(configJson); - } catch { - Sentry.captureMessage('Ethereum config JSON is invalid'); - return null; - } - }, [ethereumConfigJSON, loading]); - - if (!ethereumConfig) { - return null; - } - - return { - confirmations: ethereumConfig.confirmations, - }; -}; diff --git a/apps/token/src/hooks/use-get-association-breakdown.ts b/apps/token/src/hooks/use-get-association-breakdown.ts index ae12a6419..40af8a5b1 100644 --- a/apps/token/src/hooks/use-get-association-breakdown.ts +++ b/apps/token/src/hooks/use-get-association-breakdown.ts @@ -1,24 +1,33 @@ import React from 'react'; +import type { ethers } from 'ethers'; import * as Sentry from '@sentry/react'; -import type { VegaStaking, VegaVesting } from '@vegaprotocol/smart-contracts'; +import { addDecimal } from '@vegaprotocol/react-helpers'; +import type { + StakingBridge, + TokenVesting, +} from '@vegaprotocol/smart-contracts'; import { AppStateActionType, useAppState, } from '../contexts/app-state/app-state-context'; +import BigNumber from 'bignumber.js'; export function useGetAssociationBreakdown( ethAddress: string, - staking: VegaStaking, - vesting: VegaVesting + staking: StakingBridge, + vesting: TokenVesting ): () => Promise { - const { appDispatch } = useAppState(); + const { + appState: { decimals }, + appDispatch, + } = useAppState(); const getAssociationBreakdown = React.useCallback(async () => { try { const [stakingAssociations, vestingAssociations] = await Promise.all([ - staking.userTotalStakedByVegaKey(ethAddress), - vesting.userTotalStakedByVegaKey(ethAddress), + userTotalStakedByVegaKey(staking, ethAddress, decimals), + userTotalStakedByVegaKey(vesting, ethAddress, decimals), ]); appDispatch({ @@ -31,7 +40,59 @@ export function useGetAssociationBreakdown( } catch (err) { Sentry.captureException(err); } - }, [ethAddress, staking, vesting, appDispatch]); + }, [ethAddress, staking, vesting, decimals, appDispatch]); return getAssociationBreakdown; } + +async function userTotalStakedByVegaKey( + contract: StakingBridge | TokenVesting, + ethereumAccount: string, + decimals: number +): Promise<{ [vegaKey: string]: BigNumber }> { + const addFilter = contract.contract.filters.Stake_Deposited(ethereumAccount); + const removeFilter = contract.contract.filters.Stake_Removed(ethereumAccount); + const addEvents = await contract.contract.queryFilter(addFilter); + const removeEvents = await contract.contract.queryFilter(removeFilter); + const res = combineStakeEventsByVegaKey( + [...addEvents, ...removeEvents], + decimals + ); + return res; +} + +function combineStakeEventsByVegaKey( + events: ethers.Event[], + decimals: number +): { [vegaKey: string]: BigNumber } { + const res = events.reduce((obj, e) => { + const vegaKey = e.args?.vega_public_key; + const amount = parseEventAmount(e, decimals); + const isDeposit = e.event === 'Stake_Deposited'; + const isRemove = e.event === 'Stake_Removed'; + + if (!isDeposit && !isRemove) return obj; + + if (Object.prototype.hasOwnProperty.call(obj, vegaKey)) { + if (isDeposit) { + obj[vegaKey] = obj[vegaKey].plus(amount); + } else { + obj[vegaKey] = obj[vegaKey].minus(amount); + } + } else { + if (isDeposit) { + obj[vegaKey] = amount; + } else { + obj[vegaKey] = new BigNumber(0); + } + } + return obj; + }, {} as { [vegaKey: string]: BigNumber }); + + return res; +} + +function parseEventAmount(e: ethers.Event, decimals: number) { + const rawAmount = new BigNumber(e.args?.amount.toString() || 0); + return new BigNumber(addDecimal(rawAmount.toString(), decimals)); +} diff --git a/apps/token/src/hooks/use-get-user-tranche-balances.ts b/apps/token/src/hooks/use-get-user-tranche-balances.ts index 65d996d28..5e58bcb1d 100644 --- a/apps/token/src/hooks/use-get-user-tranche-balances.ts +++ b/apps/token/src/hooks/use-get-user-tranche-balances.ts @@ -1,6 +1,6 @@ import React from 'react'; import * as Sentry from '@sentry/react'; -import type { VegaVesting } from '@vegaprotocol/smart-contracts'; +import type { TokenVesting } from '@vegaprotocol/smart-contracts'; import { AppStateActionType, @@ -8,12 +8,16 @@ import { } from '../contexts/app-state/app-state-context'; import { BigNumber } from '../lib/bignumber'; import { useTranches } from './use-tranches'; +import { toBigNum } from '@vegaprotocol/react-helpers'; export const useGetUserTrancheBalances = ( address: string, - vesting: VegaVesting + vesting: TokenVesting ) => { - const { appDispatch } = useAppState(); + const { + appState: { decimals }, + appDispatch, + } = useAppState(); const { tranches } = useTranches(); return React.useCallback(async () => { appDispatch({ @@ -32,10 +36,14 @@ export const useGetUserTrancheBalances = ( ); const trancheIds = [0, ...userTranches.map((t) => t.tranche_id)]; const promises = trancheIds.map(async (tId) => { - const [total, vested] = await Promise.all([ - vesting.userTrancheTotalBalance(address, tId), - vesting.userTrancheVestedBalance(address, tId), + const [t, v] = await Promise.all([ + vesting.getTrancheBalance(address, tId), + vesting.getVestedForTranche(address, tId), ]); + + const total = toBigNum(t, decimals); + const vested = toBigNum(v, decimals); + return { id: tId, locked: tId === 0 ? total : total.minus(vested), @@ -56,5 +64,5 @@ export const useGetUserTrancheBalances = ( error: e as Error, }); } - }, [address, appDispatch, tranches, vesting]); + }, [address, decimals, appDispatch, tranches, vesting]); }; diff --git a/apps/token/src/hooks/use-pending-transactions.ts b/apps/token/src/hooks/use-pending-transactions.ts index 8c66b03e4..128809c55 100644 --- a/apps/token/src/hooks/use-pending-transactions.ts +++ b/apps/token/src/hooks/use-pending-transactions.ts @@ -1,9 +1,8 @@ import React from 'react'; - -import { useContracts } from '../contexts/contracts/contracts-context'; +import { useTransactionStore } from '../stores/transactions'; export const usePendingTransactions = () => { - const { transactions } = useContracts(); + const { transactions } = useTransactionStore(); return React.useMemo(() => { return transactions.some((tx) => tx.pending); diff --git a/apps/token/src/hooks/use-refresh-associated-balances.ts b/apps/token/src/hooks/use-refresh-associated-balances.ts index 5f709e249..6f0831f89 100644 --- a/apps/token/src/hooks/use-refresh-associated-balances.ts +++ b/apps/token/src/hooks/use-refresh-associated-balances.ts @@ -14,8 +14,8 @@ export function useRefreshAssociatedBalances() { async (ethAddress: string, vegaKey: string) => { const [walletAssociatedBalance, vestingAssociatedBalance] = await Promise.all([ - staking.stakeBalance(ethAddress, vegaKey), - vesting.stakeBalance(ethAddress, vegaKey), + staking.stakeBalance(ethAddress, `0x${vegaKey}`), + vesting.stakeBalance(ethAddress, `0x${vegaKey}`), ]); appDispatch({ diff --git a/apps/token/src/hooks/use-refresh-balances.ts b/apps/token/src/hooks/use-refresh-balances.ts index 79887e6ae..fe24de33a 100644 --- a/apps/token/src/hooks/use-refresh-balances.ts +++ b/apps/token/src/hooks/use-refresh-balances.ts @@ -1,8 +1,9 @@ import * as Sentry from '@sentry/react'; +import { toBigNum } from '@vegaprotocol/react-helpers'; import { useVegaWallet } from '@vegaprotocol/wallet'; +import { useEthereumConfig } from '@vegaprotocol/web3'; import React from 'react'; -import { useEnvironment } from '@vegaprotocol/react-helpers'; import { AppStateActionType, useAppState, @@ -10,29 +11,35 @@ import { import { useContracts } from '../contexts/contracts/contracts-context'; export const useRefreshBalances = (address: string) => { - const { ADDRESSES } = useEnvironment(); - const { appDispatch } = useAppState(); + const { + appState: { decimals }, + appDispatch, + } = useAppState(); const { keypair } = useVegaWallet(); const { token, staking, vesting } = useContracts(); + const { config } = useEthereumConfig(); return React.useCallback(async () => { + if (!config) return; try { - const [ - balance, - walletBalance, - lien, - allowance, - walletAssociatedBalance, - vestingAssociatedBalance, - ] = await Promise.all([ - vesting.getUserBalanceAllTranches(address), - token.balanceOf(address), - vesting.getLien(address), - token.allowance(address, ADDRESSES.stakingBridge), - // Refresh connected vega key balances as well if we are connected to a vega key - keypair?.pub ? staking.stakeBalance(address, keypair.pub) : null, - keypair?.pub ? vesting.stakeBalance(address, keypair.pub) : null, - ]); + const [b, w, stats, a, walletStakeBalance, vestingStakeBalance] = + await Promise.all([ + vesting.userTotalAllTranches(address), + token.balanceOf(address), + vesting.userStats(address), + token.allowance(address, config.staking_bridge_contract.address), + // Refresh connected vega key balances as well if we are connected to a vega key + keypair?.pub ? staking.stakeBalance(address, keypair.pub) : null, + keypair?.pub ? vesting.stakeBalance(address, keypair.pub) : null, + ]); + + const balance = toBigNum(b, decimals); + const walletBalance = toBigNum(w, decimals); + const lien = toBigNum(stats.lien, decimals); + const allowance = toBigNum(a, decimals); + const walletAssociatedBalance = toBigNum(walletStakeBalance, decimals); + const vestingAssociatedBalance = toBigNum(vestingStakeBalance, decimals); + appDispatch({ type: AppStateActionType.REFRESH_BALANCES, balance, @@ -47,11 +54,12 @@ export const useRefreshBalances = (address: string) => { } }, [ address, + decimals, appDispatch, keypair?.pub, staking, token, vesting, - ADDRESSES.stakingBridge, + config, ]); }; diff --git a/apps/token/src/hooks/use-tranches.ts b/apps/token/src/hooks/use-tranches.ts index ba379dbf4..76ad242f1 100644 --- a/apps/token/src/hooks/use-tranches.ts +++ b/apps/token/src/hooks/use-tranches.ts @@ -1,5 +1,6 @@ +import type { Networks } from '@vegaprotocol/react-helpers'; import { useFetch } from '@vegaprotocol/react-helpers'; -import type { Networks, Tranche } from '@vegaprotocol/smart-contracts'; +import type { Tranche } from '@vegaprotocol/smart-contracts'; import React, { useEffect } from 'react'; import { useEnvironment } from '@vegaprotocol/react-helpers'; diff --git a/apps/token/src/hooks/use-transaction.ts b/apps/token/src/hooks/use-transaction.ts index 8d1bbd28c..0aa63fcb8 100644 --- a/apps/token/src/hooks/use-transaction.ts +++ b/apps/token/src/hooks/use-transaction.ts @@ -2,19 +2,20 @@ import React from 'react'; import * as Sentry from '@sentry/react'; import { useTranslation } from 'react-i18next'; import type { ethers } from 'ethers'; - import { isUnexpectedError, isUserRejection } from '../lib/web3-utils'; import { initialState, TransactionActionType, transactionReducer, } from './transaction-reducer'; +import { useTransactionStore } from '../stores/transactions'; export const useTransaction = ( performTransaction: () => Promise, requiredConfirmations = 1 ) => { const { t } = useTranslation(); + const store = useTransactionStore(); const [state, dispatch] = React.useReducer(transactionReducer, { ...initialState, requiredConfirmations, @@ -62,10 +63,12 @@ export const useTransaction = ( try { const tx = await performTransaction(); + store.add({ tx, receipt: null, pending: true, requiredConfirmations }); dispatch({ type: TransactionActionType.TX_SUBMITTED, txHash: tx.hash, }); + Sentry.addBreadcrumb({ type: 'Transaction', level: Sentry.Severity.Log, @@ -83,6 +86,7 @@ export const useTransaction = ( for (let i = 1; i <= requiredConfirmations; i++) { receipt = await tx.wait(i); + store.update({ tx, receipt, pending: true, requiredConfirmations }); dispatch({ type: TransactionActionType.TX_CONFIRMATION, confirmations: receipt.confirmations, @@ -93,11 +97,13 @@ export const useTransaction = ( throw new Error('No receipt after confirmations are met'); } + store.update({ tx, receipt, pending: false, requiredConfirmations }); dispatch({ type: TransactionActionType.TX_COMPLETE, receipt, confirmations: receipt.confirmations, }); + Sentry.addBreadcrumb({ type: 'Transaction', level: Sentry.Severity.Log, @@ -114,7 +120,7 @@ export const useTransaction = ( } catch (err) { handleError(err as Error); } - }, [performTransaction, requiredConfirmations, handleError]); + }, [performTransaction, requiredConfirmations, handleError, store]); const reset = () => { dispatch({ type: TransactionActionType.TX_RESET }); diff --git a/apps/token/src/lib/ascii-to-hex.ts b/apps/token/src/lib/ascii-to-hex.ts deleted file mode 100644 index 606a6b2d4..000000000 --- a/apps/token/src/lib/ascii-to-hex.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * From: - * https://github.com/ChainSafe/web3.js/blob/436e77a8eaa061fbaa183a9f73ca590c2e1d7697/packages/web3-utils/src/index.js - */ -export const asciiToHex = (str: string) => { - if (!str) return '0x00'; - - let hex = ''; - - for (let i = 0; i < str.length; i++) { - const code = str.charCodeAt(i); - const n = code.toString(16); - hex += n.length < 2 ? '0' + n : n; - } - - return '0x' + hex; -}; diff --git a/apps/token/src/lib/decimals.ts b/apps/token/src/lib/decimals.ts index b68fc8ad4..7f46611ce 100644 --- a/apps/token/src/lib/decimals.ts +++ b/apps/token/src/lib/decimals.ts @@ -6,6 +6,7 @@ export function addDecimal(value: BigNumber, decimals: number): string { .decimalPlaces(decimals) .toString(); } + export function removeDecimal(value: BigNumber, decimals: number): string { return value.times(Math.pow(10, decimals)).toFixed(0); } diff --git a/apps/token/src/routes/claim/hooks.ts b/apps/token/src/routes/claim/hooks.ts index 6ea6eee60..b446175af 100644 --- a/apps/token/src/routes/claim/hooks.ts +++ b/apps/token/src/routes/claim/hooks.ts @@ -1,8 +1,14 @@ import { useContracts } from '../../contexts/contracts/contracts-context'; import { useTransaction } from '../../hooks/use-transaction'; import type { IClaimTokenParams } from '@vegaprotocol/smart-contracts'; +import { removeDecimal } from '@vegaprotocol/react-helpers'; +import { useAppState } from '../../contexts/app-state/app-state-context'; export const useClaim = (claimData: IClaimTokenParams, address: string) => { + const { + appState: { decimals }, + } = useAppState(); + const claimArgs = { ...claimData, ...claimData.signature, @@ -10,6 +16,7 @@ export const useClaim = (claimData: IClaimTokenParams, address: string) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion country: claimData.country!, account: address, + amount: removeDecimal(claimData.claim.amount.toString(), decimals), }; const { claim } = useContracts(); return useTransaction(() => claim.claim(claimArgs)); diff --git a/apps/token/src/routes/contracts/index.tsx b/apps/token/src/routes/contracts/index.tsx index 8f4782509..ef36f137c 100644 --- a/apps/token/src/routes/contracts/index.tsx +++ b/apps/token/src/routes/contracts/index.tsx @@ -1,22 +1,60 @@ -import { useTranslation } from 'react-i18next'; -import { Link } from '@vegaprotocol/ui-toolkit'; -import { useEnvironment } from '@vegaprotocol/react-helpers'; +import { t, useEnvironment } from '@vegaprotocol/react-helpers'; +import { Link, Splash } from '@vegaprotocol/ui-toolkit'; +import type { EthereumConfig } from '@vegaprotocol/web3'; +import { useEthereumConfig } from '@vegaprotocol/web3'; import { Heading } from '../../components/heading'; +import { SplashLoader } from '../../components/splash-loader'; const Contracts = () => { + const { config } = useEthereumConfig(); const { ADDRESSES, ETHERSCAN_URL } = useEnvironment(); - const { t } = useTranslation(); + + if (!config) { + return ( + + + + ); + } + return (

+ {[ + 'collateral_bridge_contract', + 'multisig_control_contract', + 'staking_bridge_contract', + 'token_vesting_contract', + ].map((key) => { + const contract = config[key as keyof EthereumConfig] as { + address: string; + }; + + return ( +
+
{key}:
+ + {config.collateral_bridge_contract.address} + +
+ ); + })} {Object.entries(ADDRESSES).map(([key, value]) => ( -
+
{key}:
{value} diff --git a/apps/token/src/routes/home/token-details/token-details.tsx b/apps/token/src/routes/home/token-details/token-details.tsx index fa9d6a1ed..44a9b7b79 100644 --- a/apps/token/src/routes/home/token-details/token-details.tsx +++ b/apps/token/src/routes/home/token-details/token-details.tsx @@ -8,6 +8,7 @@ import type { BigNumber } from '../../../lib/bignumber'; import { formatNumber } from '../../../lib/format-number'; import { TokenDetailsCirculating } from './token-details-circulating'; import { SplashLoader } from '../../../components/splash-loader'; +import { useEthereumConfig } from '@vegaprotocol/web3'; export const TokenDetails = ({ totalSupply, @@ -20,6 +21,7 @@ export const TokenDetails = ({ const { t } = useTranslation(); const { tranches, loading, error } = useTranches(); + const { config } = useEthereumConfig(); if (error) { return ( @@ -29,7 +31,7 @@ export const TokenDetails = ({ ); } - if (!tranches || loading) { + if (!tranches || loading || !config) { return ( @@ -57,10 +59,9 @@ export const TokenDetails = ({ data-testid="token-contract" title={t('View on Etherscan (opens in a new tab)')} className="font-mono" - href={`${ETHERSCAN_URL}/address/${ADDRESSES.vestingAddress}`} - target="_blank" + href={`${ETHERSCAN_URL}/address/${config.token_vesting_contract.address}`} > - {ADDRESSES.vestingAddress} + {config.token_vesting_contract.address} diff --git a/apps/token/src/routes/staking/associate/associate-page-container.tsx b/apps/token/src/routes/staking/associate/associate-page-container.tsx index de9bb0d07..d1e0d6eb0 100644 --- a/apps/token/src/routes/staking/associate/associate-page-container.tsx +++ b/apps/token/src/routes/staking/associate/associate-page-container.tsx @@ -1,44 +1,28 @@ -import React from 'react'; - -import { useEthereumConfig } from '../../../hooks/use-ethereum-config'; +import { useEthereumConfig } from '@vegaprotocol/web3'; import { StakingWalletsContainer } from '../staking-wallets-container'; import { AssociatePage } from './associate-page'; import { AssociatePageNoVega } from './associate-page-no-vega'; -export const NetworkParamsContainer = ({ - children, -}: { - children: (data: { confirmations: number }) => React.ReactElement; -}) => { - const config = useEthereumConfig(); +export const AssociateContainer = () => { + const { config } = useEthereumConfig(); if (!config) { return null; } - return children({ - confirmations: config.confirmations, - }); -}; - -export const AssociateContainer = () => { return ( - - {({ confirmations }) => ( - - {({ address, currVegaKey }) => - currVegaKey ? ( - - ) : ( - - ) - } - - )} - + + {({ address, currVegaKey }) => + currVegaKey ? ( + + ) : ( + + ) + } + ); }; diff --git a/apps/token/src/routes/staking/associate/associate-page.tsx b/apps/token/src/routes/staking/associate/associate-page.tsx index 1104d0ee8..933647208 100644 --- a/apps/token/src/routes/staking/associate/associate-page.tsx +++ b/apps/token/src/routes/staking/associate/associate-page.tsx @@ -1,5 +1,6 @@ import { Callout, Intent } from '@vegaprotocol/ui-toolkit'; import type { VegaKeyExtended } from '@vegaprotocol/wallet'; +import type { EthereumConfig } from '@vegaprotocol/web3'; import React from 'react'; import { useTranslation } from 'react-i18next'; @@ -18,11 +19,11 @@ import { WalletAssociate } from './wallet-associate'; export const AssociatePage = ({ address, vegaKey, - requiredConfirmations, + ethereumConfig, }: { address: string; vegaKey: VegaKeyExtended; - requiredConfirmations: number; + ethereumConfig: EthereumConfig; }) => { const { t } = useTranslation(); const params = useSearchParams(); @@ -45,7 +46,7 @@ export const AssociatePage = ({ amount, vegaKey.pub, selectedStakingMethod, - requiredConfirmations + ethereumConfig.confirmations ); const linking = usePollForStakeLinking(vegaKey.pub, txState.txData.hash); @@ -81,7 +82,7 @@ export const AssociatePage = ({ vegaKey={vegaKey.pub} state={txState} dispatch={txDispatch} - requiredConfirmations={requiredConfirmations} + requiredConfirmations={ethereumConfig.confirmations} linking={linking} /> ); @@ -128,6 +129,7 @@ export const AssociatePage = ({ perform={txPerform} amount={amount} setAmount={setAmount} + ethereumConfig={ethereumConfig} /> ))}
diff --git a/apps/token/src/routes/staking/associate/hooks.ts b/apps/token/src/routes/staking/associate/hooks.ts index c85c8e644..e819167ef 100644 --- a/apps/token/src/routes/staking/associate/hooks.ts +++ b/apps/token/src/routes/staking/associate/hooks.ts @@ -1,6 +1,5 @@ import { gql, useApolloClient } from '@apollo/client'; import * as Sentry from '@sentry/react'; -import BigNumber from 'bignumber.js'; import React from 'react'; import { StakeLinkingStatus } from '../../../__generated__/globalTypes'; @@ -15,6 +14,8 @@ import type { PartyStakeLinkings_party_stake_linkings, PartyStakeLinkingsVariables, } from './__generated__/PartyStakeLinkings'; +import { useAppState } from '../../../contexts/app-state/app-state-context'; +import { removeDecimal } from '@vegaprotocol/react-helpers'; export const useAddStake = ( address: string, @@ -24,12 +25,15 @@ export const useAddStake = ( confirmations: number ) => { const { staking, vesting } = useContracts(); + const { + appState: { decimals }, + } = useAppState(); const contractAdd = useTransaction( - () => vesting.addStake(new BigNumber(amount), vegaKey, confirmations), + () => vesting.stakeTokens(removeDecimal(amount, decimals), vegaKey), confirmations ); const walletAdd = useTransaction( - () => staking.addStake(new BigNumber(amount), vegaKey, confirmations), + () => staking.stake(removeDecimal(amount, decimals), vegaKey), confirmations ); const refreshBalances = useRefreshBalances(address); diff --git a/apps/token/src/routes/staking/associate/wallet-associate.tsx b/apps/token/src/routes/staking/associate/wallet-associate.tsx index 099cd2c27..bc6a4f974 100644 --- a/apps/token/src/routes/staking/associate/wallet-associate.tsx +++ b/apps/token/src/routes/staking/associate/wallet-associate.tsx @@ -12,7 +12,8 @@ import { useTransaction } from '../../../hooks/use-transaction'; import { BigNumber } from '../../../lib/bignumber'; import { AssociateInfo } from './associate-info'; import type { VegaKeyExtended } from '@vegaprotocol/wallet'; -import { useEnvironment } from '@vegaprotocol/react-helpers'; +import { toBigNum } from '@vegaprotocol/react-helpers'; +import type { EthereumConfig } from '@vegaprotocol/web3'; export const WalletAssociate = ({ perform, @@ -20,18 +21,19 @@ export const WalletAssociate = ({ amount, setAmount, address, + ethereumConfig, }: { perform: () => void; amount: string; setAmount: React.Dispatch>; vegaKey: VegaKeyExtended; address: string; + ethereumConfig: EthereumConfig; }) => { - const { ADDRESSES } = useEnvironment(); const { t } = useTranslation(); const { appDispatch, - appState: { walletBalance, allowance, walletAssociatedBalance }, + appState: { walletBalance, allowance, walletAssociatedBalance, decimals }, } = useAppState(); const { token } = useContracts(); @@ -40,16 +42,22 @@ export const WalletAssociate = ({ state: approveState, perform: approve, dispatch: approveDispatch, - } = useTransaction(() => token.approve(ADDRESSES.stakingBridge)); + } = useTransaction(() => + token.approve( + ethereumConfig.staking_bridge_contract.address, + Number.MAX_SAFE_INTEGER.toString() + ) + ); // Once they have approved deposits then we need to refresh their allowance React.useEffect(() => { const run = async () => { if (approveState.txState === TxState.Complete) { - const allowance = await token.allowance( + const a = await token.allowance( address, - ADDRESSES.stakingBridge + ethereumConfig.staking_bridge_contract.address ); + const allowance = toBigNum(a, decimals); appDispatch({ type: AppStateActionType.SET_ALLOWANCE, allowance, @@ -62,7 +70,8 @@ export const WalletAssociate = ({ appDispatch, approveState.txState, token, - ADDRESSES.stakingBridge, + decimals, + ethereumConfig, ]); let pageContent = null; diff --git a/apps/token/src/routes/staking/disassociate/hooks.ts b/apps/token/src/routes/staking/disassociate/hooks.ts index ab15a072b..574651000 100644 --- a/apps/token/src/routes/staking/disassociate/hooks.ts +++ b/apps/token/src/routes/staking/disassociate/hooks.ts @@ -1,4 +1,5 @@ -import BigNumber from 'bignumber.js'; +import { removeDecimal } from '@vegaprotocol/react-helpers'; +import { useAppState } from '../../../contexts/app-state/app-state-context'; import React from 'react'; import { StakingMethod } from '../../../components/staking-method-radio'; @@ -14,15 +15,16 @@ export const useRemoveStake = ( vegaKey: string, stakingMethod: StakingMethod | null ) => { + const { appState } = useAppState(); const { staking, vesting } = useContracts(); // Cannot use call on these as they check wallet balance // which if staked > wallet balance means you cannot unstaked // even worse if you stake everything then you can't unstake anything! const contractRemove = useTransaction(() => - vesting.removeStake(new BigNumber(amount), vegaKey) + vesting.removeStake(removeDecimal(amount, appState.decimals), vegaKey) ); const walletRemove = useTransaction(() => - staking.removeStake(new BigNumber(amount), vegaKey) + staking.removeStake(removeDecimal(amount, appState.decimals), vegaKey) ); const refreshBalances = useRefreshBalances(address); const getAssociationBreakdown = useGetAssociationBreakdown( diff --git a/apps/token/src/routes/tranches/tranche-label.spex.tsx b/apps/token/src/routes/tranches/tranche-label.spec.tsx similarity index 65% rename from apps/token/src/routes/tranches/tranche-label.spex.tsx rename to apps/token/src/routes/tranches/tranche-label.spec.tsx index e391cca1c..cc429f8af 100644 --- a/apps/token/src/routes/tranches/tranche-label.spex.tsx +++ b/apps/token/src/routes/tranches/tranche-label.spec.tsx @@ -1,10 +1,5 @@ import { render } from '@testing-library/react'; -import { - EthereumChainIds, - EnvironmentConfig, - Networks, -} from '@vegaprotocol/smart-contracts'; import type { TrancheLabelProps } from './tranche-label'; import { TrancheLabel } from './tranche-label'; @@ -12,14 +7,13 @@ let props: TrancheLabelProps; beforeEach(() => { props = { - chainId: EthereumChainIds.Mainnet, - contract: EnvironmentConfig[Networks.MAINNET].vestingAddress, + chainId: 1, id: 5, }; }); it('Renders null for right contract address, wrong network', () => { - const WRONG_CHAIN = EthereumChainIds.Goerli; + const WRONG_CHAIN = 3; const { container } = render( ); @@ -27,16 +21,6 @@ it('Renders null for right contract address, wrong network', () => { expect(container).toBeEmptyDOMElement(); }); -it('Renders null for right network, wrong contract address', () => { - const WRONG_ADDRESS = '0x0'; - - const { container } = render( - - ); - - expect(container).toBeEmptyDOMElement(); -}); - it('Renders null for right network, right contract address, tranche without a name', () => { const UNNAMED_TRANCHE = 0; diff --git a/apps/token/src/routes/tranches/tranche-label.tsx b/apps/token/src/routes/tranches/tranche-label.tsx index 54154e7f5..7a12ed1e2 100644 --- a/apps/token/src/routes/tranches/tranche-label.tsx +++ b/apps/token/src/routes/tranches/tranche-label.tsx @@ -1,7 +1,3 @@ -import type { EthereumChainId } from '@vegaprotocol/smart-contracts'; -import { EthereumChainIds } from '@vegaprotocol/smart-contracts'; -import { useEnvironment } from '@vegaprotocol/react-helpers'; - const TRANCHE_LABELS: Record = { '5': ['Coinlist Option 1', 'Community Whitelist'], '6': ['Coinlist Option 2'], @@ -11,8 +7,7 @@ const TRANCHE_LABELS: Record = { }; export interface TrancheLabelProps { - chainId: EthereumChainId | null; - contract: string; + chainId: number | undefined; id: number; } @@ -26,14 +21,9 @@ export interface TrancheLabelProps { * @param chainId The ID of the chain this contract is on * @param id The tranche ID on this contract */ -export const TrancheLabel = ({ contract, chainId, id }: TrancheLabelProps) => { - const { ADDRESSES } = useEnvironment(); +export const TrancheLabel = ({ chainId, id }: TrancheLabelProps) => { // Only mainnet tranches on the known vesting contract have useful name - if ( - chainId && - chainId === EthereumChainIds.Mainnet && - contract === ADDRESSES.vestingAddress - ) { + if (chainId === 1) { // Only some tranches have titles worth showing if (TRANCHE_LABELS[id]) { return ( diff --git a/apps/token/src/routes/tranches/tranche.tsx b/apps/token/src/routes/tranches/tranche.tsx index 307287869..39b844c8a 100644 --- a/apps/token/src/routes/tranches/tranche.tsx +++ b/apps/token/src/routes/tranches/tranche.tsx @@ -1,7 +1,6 @@ -import type { - Tranche as ITranche, - EthereumChainId, -} from '@vegaprotocol/smart-contracts'; +import { useEnvironment } from '@vegaprotocol/react-helpers'; +import type { Tranche as ITranche } from '@vegaprotocol/smart-contracts'; +import { Link } from '@vegaprotocol/ui-toolkit'; import { useWeb3React } from '@web3-react/core'; import React from 'react'; import { useTranslation } from 'react-i18next'; @@ -9,8 +8,6 @@ import { useParams } from 'react-router'; import { Navigate } from 'react-router-dom'; import { useOutletContext } from 'react-router-dom'; -import { Link } from '@vegaprotocol/ui-toolkit'; -import { useEnvironment } from '@vegaprotocol/react-helpers'; import { BigNumber } from '../../lib/bignumber'; import { formatNumber } from '../../lib/format-number'; import { TrancheItem } from '../redemption/tranche-item'; @@ -29,7 +26,7 @@ const TrancheProgressContents = ({ export const Tranche = () => { const tranches = useOutletContext(); - const { ADDRESSES, ETHERSCAN_URL } = useEnvironment(); + const { ETHERSCAN_URL } = useEnvironment(); const { t } = useTranslation(); const { trancheId } = useParams<{ trancheId: string }>(); const { chainId } = useWeb3React(); @@ -58,11 +55,7 @@ export const Tranche = () => { unlocked={tranche.total_added.minus(tranche.locked_amount)} total={tranche.total_added} secondaryHeader={ - + } />
!t.total_added.isLessThanOrEqualTo(trancheMinimum); export const Tranches = () => { - const { ADDRESSES } = useEnvironment(); const tranches = useOutletContext(); const [showAll, setShowAll] = React.useState(false); const { t } = useTranslation(); const { chainId } = useWeb3React(); + const { config } = useEthereumConfig(); const filteredTranches = tranches?.filter(shouldShowTranche) || []; + if (!config) { + return null; + } + return (

{t('chartTitle')}

@@ -41,11 +45,7 @@ export const Tranches = () => { unlocked={tranche.total_added.minus(tranche.locked_amount)} total={tranche.total_added} secondaryHeader={ - + } /> diff --git a/apps/token/src/stores/transactions.ts b/apps/token/src/stores/transactions.ts new file mode 100644 index 000000000..e6eefe4fa --- /dev/null +++ b/apps/token/src/stores/transactions.ts @@ -0,0 +1,42 @@ +import type ethers from 'ethers'; +import type { GetState, SetState } from 'zustand'; +import create from 'zustand'; + +export interface TxData { + tx: ethers.ContractTransaction; + receipt: ethers.ContractReceipt | null; + pending: boolean; + requiredConfirmations: number; +} + +interface TransactionStore { + transactions: Array; + add: (tx: TxData) => void; + update: (tx: TxData) => void; + remove: (tx: TxData) => void; +} + +export const useTransactionStore = create( + (set: SetState, get: GetState) => ({ + transactions: [], + add: (tx) => { + const { transactions } = get(); + set({ transactions: [...transactions, tx] }); + }, + update: (tx) => { + const { transactions } = get(); + set({ + transactions: [ + ...transactions.filter((t) => t.tx.hash !== tx.tx.hash), + tx, + ], + }); + }, + remove: (tx) => { + const { transactions } = get(); + set({ + transactions: transactions.filter((t) => t.tx.hash !== tx.tx.hash), + }); + }, + }) +); diff --git a/apps/trading-e2e/src/integration/deposits.feature b/apps/trading-e2e/src/integration/deposits.feature index b1025da0d..ae8c62401 100644 --- a/apps/trading-e2e/src/integration/deposits.feature +++ b/apps/trading-e2e/src/integration/deposits.feature @@ -1,7 +1,8 @@ Feature: Deposits to vega wallet Background: - Given I navigate to deposits page + Given I can connect to Ethereum + And I navigate to deposits page # wallet is already connected before tests start and doesn't prompt the disconnected state @ignore diff --git a/apps/trading-e2e/src/integration/withdrawals.feature b/apps/trading-e2e/src/integration/withdrawals.feature index 8a16cb629..1ec7ff5e7 100644 --- a/apps/trading-e2e/src/integration/withdrawals.feature +++ b/apps/trading-e2e/src/integration/withdrawals.feature @@ -1,7 +1,8 @@ Feature: Withdrawals to eth wallet Background: - Given I navigate to withdrawal page + Given I can connect to Ethereum + And I navigate to withdrawal page And I connect to Vega Wallet Scenario: Succesfull withdrawal diff --git a/apps/trading-e2e/src/support/step_definitions/common.step.ts b/apps/trading-e2e/src/support/step_definitions/common.step.ts index 5cf95f90f..8e163ad43 100644 --- a/apps/trading-e2e/src/support/step_definitions/common.step.ts +++ b/apps/trading-e2e/src/support/step_definitions/common.step.ts @@ -19,3 +19,7 @@ Given('I am on the homepage', () => { basePage.closeDialog(); marketPage.validateMarketsAreDisplayed(); }); + +Given('I can connect to Ethereum', () => { + cy.mockWeb3Provider(); +}); diff --git a/apps/trading-e2e/src/support/step_definitions/deposits.step.ts b/apps/trading-e2e/src/support/step_definitions/deposits.step.ts index 025264a13..4f28bd189 100644 --- a/apps/trading-e2e/src/support/step_definitions/deposits.step.ts +++ b/apps/trading-e2e/src/support/step_definitions/deposits.step.ts @@ -5,10 +5,6 @@ import DepositsPage from '../pages/deposits-page'; const depositsPage = new DepositsPage(); const ethWallet = new EthereumWallet(); -beforeEach(() => { - cy.mockWeb3Provider(); -}); - Then('I navigate to deposits page', () => { depositsPage.navigateToDeposits(); }); diff --git a/apps/trading/components/web3-container/web3-container.spec.tsx b/apps/trading/components/web3-container/web3-container.spec.tsx index 04028f99e..1d2a20f63 100644 --- a/apps/trading/components/web3-container/web3-container.spec.tsx +++ b/apps/trading/components/web3-container/web3-container.spec.tsx @@ -1,9 +1,10 @@ import { fireEvent, render, screen } from '@testing-library/react'; import type { MockedResponse } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing'; -import { NETWORK_PARAMS_QUERY, Web3Container } from './web3-container'; -import type { NetworkParamsQuery } from './__generated__/NetworkParamsQuery'; +import { Web3Container } from './web3-container'; import type { useWeb3React } from '@web3-react/core'; +import type { NetworkParamsQuery } from '@vegaprotocol/web3'; +import { NETWORK_PARAMS_QUERY } from '@vegaprotocol/web3'; const defaultHookValue = { isActive: false, @@ -50,14 +51,12 @@ jest.mock('@web3-react/core', () => { function setup(mock = networkParamsQueryMock) { return render( - ( -
-
Child
-
{ethereumConfig.collateral_bridge_contract.address}
-
- )} - /> + +
+
Child
+
{mockEthereumConfig.collateral_bridge_contract.address}
+
+
); } @@ -140,29 +139,5 @@ it('Shows no config found message if the network parameter doesnt exist', async }; setup(mock); expect(screen.getByText('Loading...')).toBeInTheDocument(); - expect( - await screen.findByText('No ethereum config found') - ).toBeInTheDocument(); -}); - -it('Shows message if ethereum config could not be parsed', async () => { - const mock: MockedResponse = { - request: { - query: NETWORK_PARAMS_QUERY, - }, - result: { - data: { - networkParameters: [ - { - __typename: 'NetworkParameter', - key: 'blockchains.ethereumConfig', - value: '"something invalid }', - }, - ], - }, - }, - }; - setup(mock); - expect(screen.getByText('Loading...')).toBeInTheDocument(); - expect(await screen.findByText('Could not parse config')).toBeInTheDocument(); + expect(await screen.findByText('No data')).toBeInTheDocument(); }); diff --git a/apps/trading/components/web3-container/web3-container.tsx b/apps/trading/components/web3-container/web3-container.tsx index 2b777c8b2..9ce54ca95 100644 --- a/apps/trading/components/web3-container/web3-container.tsx +++ b/apps/trading/components/web3-container/web3-container.tsx @@ -1,96 +1,42 @@ -import { gql } from '@apollo/client'; -import type { NetworkParamsQuery } from './__generated__/NetworkParamsQuery'; -import { Button, Splash } from '@vegaprotocol/ui-toolkit'; -import { Web3Provider, Web3ConnectDialog } from '@vegaprotocol/web3'; +import { AsyncRenderer, Button, Splash } from '@vegaprotocol/ui-toolkit'; +import { + Web3Provider, + Web3ConnectDialog, + useEthereumConfig, +} from '@vegaprotocol/web3'; import { useWeb3React } from '@web3-react/core'; import type { ReactNode } from 'react'; import { useEffect, useState } from 'react'; import { Connectors } from '../../lib/web3-connectors'; -import { PageQueryContainer } from '../page-query-container'; import { t } from '@vegaprotocol/react-helpers'; -export interface EthereumConfig { - network_id: string; - chain_id: string; - confirmations: number; - collateral_bridge_contract: { - address: string; - }; - multisig_control_contract: { - address: string; - deployment_block_height: number; - }; - staking_bridge_contract: { - address: string; - deployment_block_height: number; - }; - token_vesting_contract: { - address: string; - deployment_block_height: number; - }; -} - -export const NETWORK_PARAMS_QUERY = gql` - query NetworkParamsQuery { - networkParameters { - key - value - } - } -`; - interface Web3ContainerProps { - render: (params: { ethereumConfig: EthereumConfig }) => ReactNode; + children: ReactNode; } -export const Web3Container = ({ render }: Web3ContainerProps) => { +export const Web3Container = ({ children }: Web3ContainerProps) => { const [dialogOpen, setDialogOpen] = useState(false); + const { config, loading, error } = useEthereumConfig(); + return ( - - query={NETWORK_PARAMS_QUERY} - render={(data) => { - const ethereumConfigParam = data.networkParameters?.find( - (np) => np.key === 'blockchains.ethereumConfig' - ); - - if (!ethereumConfigParam) { - return ( - -

{t('No ethereum config found')}

-
- ); - } - - let ethereumConfig: EthereumConfig; - - try { - ethereumConfig = JSON.parse(ethereumConfigParam.value); - } catch { - return ( - -

{t('Could not parse config')}

-
- ); - } - - return ( - - - {render({ ethereumConfig })} - - - - ); - }} - /> + + {config ? ( + + + {children} + + + + ) : null} + ); }; diff --git a/apps/trading/pages/portfolio/deposit/deposit-container.tsx b/apps/trading/pages/portfolio/deposit/deposit-container.tsx index 81672dc62..27c11f2ff 100644 --- a/apps/trading/pages/portfolio/deposit/deposit-container.tsx +++ b/apps/trading/pages/portfolio/deposit/deposit-container.tsx @@ -1,4 +1,3 @@ -import type { EthereumConfig } from '../../../components/web3-container/web3-container'; import { gql } from '@apollo/client'; import { PageQueryContainer } from '../../../components/page-query-container'; import type { DepositPage } from './__generated__/DepositPage'; @@ -17,17 +16,13 @@ const DEPOSIT_PAGE_QUERY = gql` `; interface DepositContainerProps { - ethereumConfig: EthereumConfig; assetId?: string; } /** * Fetches data required for the Deposit page */ -export const DepositContainer = ({ - ethereumConfig, - assetId, -}: DepositContainerProps) => { +export const DepositContainer = ({ assetId }: DepositContainerProps) => { const { VEGA_ENV } = useEnvironment(); return ( @@ -44,8 +39,6 @@ export const DepositContainer = ({ return ( { }, [query]); return ( - ( -
-

Deposit

- -
- )} - /> + +
+

Deposit

+ +
+
); }; diff --git a/apps/trading/pages/portfolio/index.page.tsx b/apps/trading/pages/portfolio/index.page.tsx index fd1618e6e..a362bebea 100644 --- a/apps/trading/pages/portfolio/index.page.tsx +++ b/apps/trading/pages/portfolio/index.page.tsx @@ -12,68 +12,66 @@ const Portfolio = () => { const tabClassName = 'p-[16px] pl-[316px]'; return ( - ( -
-
- -
- - -
-

- {t('Positions')} -

- -
-
- -
-

- {t('Orders')} -

- -
-
- -
-

- {t('Fills')} -

-
-
- -
-

- {t('History')} -

-
-
-
-
-
-
- - - + +
+
+ +
+ + +
+

+ {t('Positions')} +

+ +
- - - {t('Deposit')} - + +
+

+ {t('Orders')} +

+ +
- - + +
+

+ {t('Fills')} +

+
+
+ +
+

+ {t('History')} +

+
-
- )} - /> + +
+ + + + + + + {t('Deposit')} + + + + + + +
+
+
); }; diff --git a/apps/trading/pages/portfolio/withdraw/index.page.tsx b/apps/trading/pages/portfolio/withdraw/index.page.tsx index 99ed4b17b..ef7c41dbf 100644 --- a/apps/trading/pages/portfolio/withdraw/index.page.tsx +++ b/apps/trading/pages/portfolio/withdraw/index.page.tsx @@ -23,14 +23,12 @@ const Withdraw = () => { return ( - ( -
-

{t('Withdraw')}

- -
- )} - /> + +
+

{t('Withdraw')}

+ +
+
); }; diff --git a/apps/trading/pages/portfolio/withdrawals/index.page.tsx b/apps/trading/pages/portfolio/withdrawals/index.page.tsx index 2c6af9058..69e205818 100644 --- a/apps/trading/pages/portfolio/withdrawals/index.page.tsx +++ b/apps/trading/pages/portfolio/withdrawals/index.page.tsx @@ -7,19 +7,17 @@ import { WithdrawalsContainer } from './withdrawals-container'; const Withdrawals = () => { return ( - ( -
-
-

{t('Withdrawals')}

- - {t('Start withdrawal')} - -
- -
- )} - /> + +
+
+

{t('Withdrawals')}

+ + {t('Start withdrawal')} + +
+ +
+
); }; diff --git a/libs/deposits/src/lib/deposit-manager.tsx b/libs/deposits/src/lib/deposit-manager.tsx index 9d879eec8..21efb1cb8 100644 --- a/libs/deposits/src/lib/deposit-manager.tsx +++ b/libs/deposits/src/lib/deposit-manager.tsx @@ -7,8 +7,13 @@ import { useSubmitApproval } from './use-submit-approval'; import { useGetDepositLimits } from './use-get-deposit-limits'; import { useGetAllowance } from './use-get-allowance'; import { useSubmitFaucet } from './use-submit-faucet'; -import { EthTxStatus, TransactionDialog } from '@vegaprotocol/web3'; -import { useTokenContract, useBridgeContract } from '@vegaprotocol/web3'; +import { + EthTxStatus, + TransactionDialog, + useEthereumConfig, + useTokenDecimals, +} from '@vegaprotocol/web3'; +import { useTokenContract } from '@vegaprotocol/web3'; interface ERC20AssetSource { __typename: 'ERC20'; @@ -30,16 +35,12 @@ export interface Asset { } interface DepositManagerProps { - requiredConfirmations: number; - bridgeAddress: string; assets: Asset[]; initialAssetId?: string; isFaucetable?: boolean; } export const DepositManager = ({ - requiredConfirmations, - bridgeAddress, assets, initialAssetId, isFaucetable, @@ -52,31 +53,34 @@ export const DepositManager = ({ return asset; }, [assets, assetId]); + const { config } = useEthereumConfig(); + const tokenContract = useTokenContract( asset?.source.__typename === 'ERC20' ? asset.source.contractAddress : undefined, isFaucetable ); - const bridgeContract = useBridgeContract(); + + const decimals = useTokenDecimals(tokenContract); // Get users balance of the erc20 token selected - const { balanceOf, refetch } = useGetBalanceOfERC20Token(tokenContract); + const { balance, refetch } = useGetBalanceOfERC20Token( + tokenContract, + decimals + ); // Get temporary deposit limits - const limits = useGetDepositLimits(bridgeContract, asset); + const limits = useGetDepositLimits(asset, decimals); // Get allowance (approved spending limit of brdige contract) for the selected asset - const allowance = useGetAllowance(tokenContract, bridgeAddress); + const allowance = useGetAllowance(tokenContract, decimals); // Set up approve transaction - const approve = useSubmitApproval(tokenContract, bridgeAddress); + const approve = useSubmitApproval(tokenContract); // Set up deposit transaction - const { confirmationEvent, ...deposit } = useSubmitDeposit( - bridgeContract, - requiredConfirmations - ); + const { confirmationEvent, ...deposit } = useSubmitDeposit(); // Set up faucet transaction const faucet = useSubmitFaucet(tokenContract); @@ -94,7 +98,7 @@ export const DepositManager = ({ return ( <> setAssetId(id)} assets={sortBy(assets, 'name')} @@ -112,7 +116,7 @@ export const DepositManager = ({ name="deposit" confirmed={Boolean(confirmationEvent)} // Must wait for additional confirmations for Vega to pick up the Ethereum transaction - requiredConfirmations={requiredConfirmations} + requiredConfirmations={config?.confirmations} /> ); diff --git a/libs/deposits/src/lib/use-get-allowance.ts b/libs/deposits/src/lib/use-get-allowance.ts index 842bcdac4..73a2203bc 100644 --- a/libs/deposits/src/lib/use-get-allowance.ts +++ b/libs/deposits/src/lib/use-get-allowance.ts @@ -1,24 +1,31 @@ -import type { ERC20Token } from '@vegaprotocol/smart-contracts'; +import type { Token } from '@vegaprotocol/smart-contracts'; import { useWeb3React } from '@web3-react/core'; import { useCallback } from 'react'; -import { useEthereumReadContract } from '@vegaprotocol/web3'; +import { useEthereumConfig, useEthereumReadContract } from '@vegaprotocol/web3'; +import BigNumber from 'bignumber.js'; +import { addDecimal } from '@vegaprotocol/react-helpers'; -export const useGetAllowance = ( - contract: ERC20Token | null, - bridgeAddress: string -) => { +export const useGetAllowance = (contract: Token | null, decimals?: number) => { const { account } = useWeb3React(); + const { config } = useEthereumConfig(); const getAllowance = useCallback(() => { - if (!contract || !account) { + if (!contract || !account || !config) { return; } - return contract.allowance(account, bridgeAddress); - }, [contract, account, bridgeAddress]); + return contract.allowance( + account, + config.collateral_bridge_contract.address + ); + }, [contract, account, config]); const { state: { data }, } = useEthereumReadContract(getAllowance); - return data; + if (!data || !decimals) return; + + const allowance = new BigNumber(addDecimal(data.toString(), decimals)); + + return allowance; }; diff --git a/libs/deposits/src/lib/use-get-balance-of-erc20-token.ts b/libs/deposits/src/lib/use-get-balance-of-erc20-token.ts index cf6ba27d1..0d507c33d 100644 --- a/libs/deposits/src/lib/use-get-balance-of-erc20-token.ts +++ b/libs/deposits/src/lib/use-get-balance-of-erc20-token.ts @@ -1,9 +1,14 @@ import { useEthereumReadContract } from '@vegaprotocol/web3'; -import type { ERC20Token } from '@vegaprotocol/smart-contracts'; +import type { Token } from '@vegaprotocol/smart-contracts'; import { useWeb3React } from '@web3-react/core'; import { useCallback } from 'react'; +import BigNumber from 'bignumber.js'; +import { addDecimal } from '@vegaprotocol/react-helpers'; -export const useGetBalanceOfERC20Token = (contract: ERC20Token | null) => { +export const useGetBalanceOfERC20Token = ( + contract: Token | null, + decimals: number | undefined +) => { const { account } = useWeb3React(); const getBalance = useCallback(() => { @@ -16,5 +21,10 @@ export const useGetBalanceOfERC20Token = (contract: ERC20Token | null) => { const { state, refetch } = useEthereumReadContract(getBalance); - return { balanceOf: state.data, refetch }; + const balance = + state.data && decimals + ? new BigNumber(addDecimal(state.data?.toString(), decimals)) + : undefined; + + return { balance, refetch }; }; diff --git a/libs/deposits/src/lib/use-get-deposit-limits.ts b/libs/deposits/src/lib/use-get-deposit-limits.ts index dcc8b4332..c6c7c130c 100644 --- a/libs/deposits/src/lib/use-get-deposit-limits.ts +++ b/libs/deposits/src/lib/use-get-deposit-limits.ts @@ -1,37 +1,33 @@ -import type BigNumber from 'bignumber.js'; import { useCallback } from 'react'; -import type { VegaErc20Bridge } from '@vegaprotocol/smart-contracts'; import type { Asset } from './deposit-manager'; -import { useEthereumReadContract } from '@vegaprotocol/web3'; +import { useBridgeContract, useEthereumReadContract } from '@vegaprotocol/web3'; +import BigNumber from 'bignumber.js'; +import { addDecimal } from '@vegaprotocol/react-helpers'; -interface Limits { - min: BigNumber; - max: BigNumber; -} - -export const useGetDepositLimits = ( - contract: VegaErc20Bridge | null, - asset?: Asset -): Limits | null => { +export const useGetDepositLimits = (asset?: Asset, decimals?: number) => { + const contract = useBridgeContract(); const getLimits = useCallback(async () => { if (!contract || !asset || asset.source.__typename !== 'ERC20') { return; } return Promise.all([ - contract.getDepositMinimum(asset.source.contractAddress, asset.decimals), - contract.getDepositMaximum(asset.source.contractAddress, asset.decimals), + contract.getDepositMinimum(asset.source.contractAddress), + contract.getDepositMaximum(asset.source.contractAddress), ]); }, [asset, contract]); const { state: { data }, - } = useEthereumReadContract<[BigNumber, BigNumber] | undefined>(getLimits); + } = useEthereumReadContract(getLimits); - if (!data) return null; + if (!data || !decimals) return null; + + const min = new BigNumber(addDecimal(data[0].toString(), decimals)); + const max = new BigNumber(addDecimal(data[1].toString(), decimals)); return { - min: data[0], - max: data[1], + min, + max: max.isEqualTo(0) ? new BigNumber(Infinity) : max, }; }; diff --git a/libs/deposits/src/lib/use-submit-approval.ts b/libs/deposits/src/lib/use-submit-approval.ts index 9dc1f6054..2ad905071 100644 --- a/libs/deposits/src/lib/use-submit-approval.ts +++ b/libs/deposits/src/lib/use-submit-approval.ts @@ -1,15 +1,17 @@ -import type { ERC20Token } from '@vegaprotocol/smart-contracts'; -import { useEthereumTransaction } from '@vegaprotocol/web3'; +import type { Token } from '@vegaprotocol/smart-contracts'; +import { useEthereumConfig, useEthereumTransaction } from '@vegaprotocol/web3'; + +export const useSubmitApproval = (contract: Token | null) => { + const { config } = useEthereumConfig(); -export const useSubmitApproval = ( - contract: ERC20Token | null, - bridgeAddress: string -) => { const transaction = useEthereumTransaction(() => { - if (!contract) { + if (!contract || !config) { return null; } - return contract.approve(bridgeAddress); + return contract.approve( + config.collateral_bridge_contract.address, + Number.MAX_SAFE_INTEGER.toString() + ); }); return transaction; diff --git a/libs/deposits/src/lib/use-submit-deposit.ts b/libs/deposits/src/lib/use-submit-deposit.ts index 5ae56d7bf..9a65a6fda 100644 --- a/libs/deposits/src/lib/use-submit-deposit.ts +++ b/libs/deposits/src/lib/use-submit-deposit.ts @@ -7,8 +7,11 @@ import type { import { DepositStatus } from '@vegaprotocol/types'; import { useState } from 'react'; import { remove0x } from '@vegaprotocol/react-helpers'; -import { useEthereumTransaction } from '@vegaprotocol/web3'; -import type { VegaErc20Bridge } from '@vegaprotocol/smart-contracts'; +import { + useBridgeContract, + useEthereumConfig, + useEthereumTransaction, +} from '@vegaprotocol/web3'; const DEPOSIT_EVENT_SUB = gql` subscription DepositEvent($partyId: ID!) { @@ -24,10 +27,9 @@ const DEPOSIT_EVENT_SUB = gql` } `; -export const useSubmitDeposit = ( - contract: VegaErc20Bridge | null, - confirmations: number -) => { +export const useSubmitDeposit = () => { + const { config } = useEthereumConfig(); + const contract = useBridgeContract(); const [confirmationEvent, setConfirmationEvent] = useState(null); // Store public key from contract arguments for use in the subscription, @@ -52,7 +54,7 @@ export const useSubmitDeposit = ( args.amount, args.vegaPublicKey ); - }, confirmations); + }, config?.confirmations); useSubscription(DEPOSIT_EVENT_SUB, { variables: { partyId: partyId ? remove0x(partyId) : '' }, diff --git a/libs/deposits/src/lib/use-submit-faucet.ts b/libs/deposits/src/lib/use-submit-faucet.ts index 73b53e76c..d0bf94ced 100644 --- a/libs/deposits/src/lib/use-submit-faucet.ts +++ b/libs/deposits/src/lib/use-submit-faucet.ts @@ -1,9 +1,10 @@ -import type { ERC20Token } from '@vegaprotocol/smart-contracts'; +import { Token } from '@vegaprotocol/smart-contracts'; +import type { TokenFaucetable } from '@vegaprotocol/smart-contracts'; import { useEthereumTransaction } from '@vegaprotocol/web3'; -export const useSubmitFaucet = (contract: ERC20Token | null) => { +export const useSubmitFaucet = (contract: Token | TokenFaucetable | null) => { const transaction = useEthereumTransaction(() => { - if (!contract) { + if (!contract || contract instanceof Token) { return null; } return contract.faucet(); diff --git a/libs/react-helpers/src/hooks/use-environment.tsx b/libs/react-helpers/src/hooks/use-environment.tsx index dc00c57fe..fdf77f7f5 100644 --- a/libs/react-helpers/src/hooks/use-environment.tsx +++ b/libs/react-helpers/src/hooks/use-environment.tsx @@ -1,15 +1,53 @@ import type { ReactNode } from 'react'; -import type { Networks } from '@vegaprotocol/smart-contracts'; -import { EnvironmentConfig } from '@vegaprotocol/smart-contracts'; import { createContext, useContext } from 'react'; +import type { Networks } from '../lib/environment'; declare global { interface Window { _ENV?: RawEnvironment; } } +const customVegaTokenAddress = process.env['CUSTOM_TOKEN_ADDRESS'] as string; +const customClaimAddress = process.env['CUSTOM_CLAIM_ADDRESS'] as string; +const customLockedAddress = process.env['CUSTOM_LOCKED_ADDRESS'] as string; +interface VegaContracts { + vegaTokenAddress: string; + claimAddress: string; + lockedAddress: string; +} -type VegaContracts = typeof EnvironmentConfig[Networks]; +export const ContractAddresses: { [key in Networks]: VegaContracts } = { + CUSTOM: { + vegaTokenAddress: customVegaTokenAddress, + claimAddress: customClaimAddress, + lockedAddress: customLockedAddress, + }, + DEVNET: { + vegaTokenAddress: '0xc93137f9F4B820Ca85FfA3C7e84cCa6Ebc7bB517', + claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', + lockedAddress: '0x0', + }, + STAGNET: { + vegaTokenAddress: '0x547cbA83a7eb82b546ee5C7ff0527F258Ba4546D', + claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error + lockedAddress: '0x0', // TODO not deployed to this env + }, + STAGNET2: { + vegaTokenAddress: '0xd8fa193B93a179DdCf51FFFDe5320E0872cdcf44', + claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error + lockedAddress: '0x0', // TODO not deployed to this env + }, + TESTNET: { + vegaTokenAddress: '0xDc335304979D378255015c33AbFf09B60c31EBAb', + claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error + lockedAddress: '0x0', // TODO not deployed to this env + }, + MAINNET: { + vegaTokenAddress: '0xcB84d72e61e383767C4DFEb2d8ff7f4FB89abc6e', + claimAddress: '0x0ee1fb382caf98e86e97e51f9f42f8b4654020f3', + lockedAddress: '0x78344c7305d73a7a0ac3c94cd9960f4449a1814e', + }, +}; type EnvironmentProviderProps = { definintions?: Partial; @@ -106,7 +144,7 @@ export const EnvironmentProvider = ({ {children} diff --git a/libs/react-helpers/src/index.ts b/libs/react-helpers/src/index.ts index 87c27d7b9..1ed3710ae 100644 --- a/libs/react-helpers/src/index.ts +++ b/libs/react-helpers/src/index.ts @@ -1,5 +1,6 @@ export * from './hooks'; export * from './lib/context'; +export * from './lib/environment'; export * from './lib/format'; export * from './lib/generic-data-provider'; export * from './lib/grid'; diff --git a/libs/smart-contracts/src/config/vega.ts b/libs/react-helpers/src/lib/environment.ts similarity index 100% rename from libs/smart-contracts/src/config/vega.ts rename to libs/react-helpers/src/lib/environment.ts diff --git a/libs/react-helpers/src/lib/format/number.ts b/libs/react-helpers/src/lib/format/number.ts index 7504ee2ac..1a5b5559b 100644 --- a/libs/react-helpers/src/lib/format/number.ts +++ b/libs/react-helpers/src/lib/format/number.ts @@ -1,4 +1,5 @@ import { BigNumber } from 'bignumber.js'; +import { BigNumber as EthersBigNumber } from 'ethers'; import memoize from 'lodash/memoize'; import { getUserLocale } from './utils'; @@ -6,15 +7,22 @@ export function toDecimal(numberOfDecimals: number) { return Math.pow(10, -numberOfDecimals); } +export function toBigNum( + rawValue: string | number | EthersBigNumber, + decimals: number +): BigNumber { + return new BigNumber( + rawValue instanceof EthersBigNumber ? rawValue.toString() : rawValue || 0 + ).dividedBy(Math.pow(10, decimals)); +} + export function addDecimal( - value: string | number, + value: string | number | EthersBigNumber, decimals: number, decimalPrecision = decimals ): string { if (!decimals) return value.toString(); - return new BigNumber(value || 0) - .dividedBy(Math.pow(10, decimals)) - .toFixed(decimalPrecision); + return toBigNum(value, decimals).toFixed(decimalPrecision); } export function removeDecimal(value: string, decimals: number): string { diff --git a/libs/smart-contracts/src/config/ethereum.ts b/libs/smart-contracts/src/config/ethereum.ts deleted file mode 100644 index 49675ba05..000000000 --- a/libs/smart-contracts/src/config/ethereum.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Networks } from './vega'; - -const customVegaTokenAddress = process.env.CUSTOM_TOKEN_ADDRESS as string; -const customClaimAddress = process.env.CUSTOM_CLAIM_ADDRESS as string; -const customLockedAddress = process.env.CUSTOM_LOCKED_ADDRESS as string; -const customVestingAddress = process.env.CUSTOM_VESTING_ADDRESS as string; -const customStakingBridge = process.env.CUSTOM_STAKING_BRIDGE as string; -const customErc20Bridge = process.env.CUSTOM_ERC20_BRIDGE as string; - -export type EthereumChainId = '0x1' | '0x3' | '0x4' | '0x5' | '0x2a'; - -export type EthereumChainName = - | 'Mainnet' - | 'Ropsten' - | 'Rinkeby' - | 'Goerli' - | 'Kovan'; - -export const EthereumChainNames: Record = { - '0x1': 'Mainnet', - '0x3': 'Ropsten', - '0x4': 'Rinkeby', - '0x5': 'Goerli', - '0x2a': 'Kovan', -}; - -export const EthereumChainIds: Record = { - Mainnet: '0x1', - Ropsten: '0x3', - Rinkeby: '0x4', - Goerli: '0x5', - Kovan: '0x2a', -}; - -export const ChainIdMap: Record = { - '0x1': 1, - '0x3': 3, - '0x4': 4, - '0x5': 5, - '0x2a': 42, -}; -interface VegaContracts { - vestingAddress: string; - vegaTokenAddress: string; - claimAddress: string; - lockedAddress: string; - stakingBridge: string; - erc20Bridge: string; -} - -export const EnvironmentConfig: { [key in Networks]: VegaContracts } = { - [Networks.CUSTOM]: { - vegaTokenAddress: customVegaTokenAddress, - claimAddress: customClaimAddress, - lockedAddress: customLockedAddress, - vestingAddress: customVestingAddress, - stakingBridge: customStakingBridge, - erc20Bridge: customErc20Bridge, - }, - [Networks.DEVNET]: { - vegaTokenAddress: '0xc93137f9F4B820Ca85FfA3C7e84cCa6Ebc7bB517', - claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', - lockedAddress: '0x0', - vestingAddress: '0xd1216AAb948f5FC706Df73df6d71c64CcaA8550a', - stakingBridge: '0xf2cD5C8b8c52f96293338A0AF463a0Bfc602D5bc', - erc20Bridge: '0xE43013C3c2A134AB3782ADEb258669A8566DAD57', - }, - [Networks.STAGNET]: { - vegaTokenAddress: '0x547cbA83a7eb82b546ee5C7ff0527F258Ba4546D', - claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error - lockedAddress: '0x0', // TODO not deployed to this env - vestingAddress: '0xfCe6eB272D3d4146A96bC28de71212b327F575fa', - stakingBridge: '0x7D88CD817227D599815d407D929af18Bb8D57176', - erc20Bridge: '0xc0835e6dEf177F8ba2561C4e4216827A3798c6B9', - }, - [Networks.STAGNET2]: { - vegaTokenAddress: '0xd8fa193B93a179DdCf51FFFDe5320E0872cdcf44', - claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error - lockedAddress: '0x0', // TODO not deployed to this env - vestingAddress: '0x9F10cBeEf03A564Fb914c2010c0Cd55E9BB11406', - stakingBridge: '0x7896C9491962D5839783CB6e0492ECebd34Bb35F', - erc20Bridge: '0xffC1eb64e22fd5E29816c633eE84088EEEe879E5', - }, - [Networks.TESTNET]: { - vegaTokenAddress: '0xDc335304979D378255015c33AbFf09B60c31EBAb', - claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error - lockedAddress: '0x0', // TODO not deployed to this env - vestingAddress: '0xe2deBB240b43EDfEBc9c38B67c0894B9A92Bf07c', - stakingBridge: '0xF5A3830F002BE78dd801214F5316b677E0355c60', - erc20Bridge: '0xF009C66c6afC9661143fD7cE1eDb02c1961a6510', - }, - [Networks.MAINNET]: { - vegaTokenAddress: '0xcB84d72e61e383767C4DFEb2d8ff7f4FB89abc6e', - claimAddress: '0x0ee1fb382caf98e86e97e51f9f42f8b4654020f3', - lockedAddress: '0x78344c7305d73a7a0ac3c94cd9960f4449a1814e', - vestingAddress: '0x23d1bFE8fA50a167816fBD79D7932577c06011f4', - stakingBridge: '0x195064D33f09e0c42cF98E665D9506e0dC17de68', - erc20Bridge: '0xCd403f722b76366f7d609842C589906ca051310f', - }, -}; - -// No concept of dev/staging/test for these right now. -export const RewardsAddresses = { - [EthereumChainIds.Mainnet]: { - 'SushiSwap VEGA/ETH': '0x285de24077440c53b1661287D170e3ae22de0a44', - 'SushiSwap VEGA/USDC': '0x49407c243c26f109b3c77c41dd83742164c20b5f', - } as { [key: string]: string }, - [EthereumChainIds.Ropsten]: { - 'SushiSwap VEGA/ETH': '0xa93dd6912897c5fe8503a82234d829bc7905714b', - 'SushiSwap VEGA/USDC': '0xa93dd6912897c5fe8503a82234d829bc7905714b', - } as { [key: string]: string }, -}; - -export const RewardsPoolAddresses = { - [EthereumChainIds.Mainnet]: { - '0x285de24077440c53b1661287D170e3ae22de0a44': - '0x29c827ce49accf68a1a278c67c9d30c52fbbc348', - '0x49407c243c26f109b3c77c41dd83742164c20b5f': - '0x42b7B8f8F83fA5cbf0176f8c24Ad51EbcD4B5F17', - } as { [key: string]: string }, - [EthereumChainIds.Ropsten]: { - // Only one deployed to this environment - '0xa93dd6912897c5fe8503a82234d829bc7905714b': - '0x29c827ce49accf68a1a278c67c9d30c52fbbc348', - } as { [key: string]: string }, -}; diff --git a/libs/smart-contracts/src/config/index.ts b/libs/smart-contracts/src/config/index.ts deleted file mode 100644 index 51cb66024..000000000 --- a/libs/smart-contracts/src/config/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ethereum'; -export * from './vega'; diff --git a/libs/smart-contracts/src/contracts/base-contract.ts b/libs/smart-contracts/src/contracts/base-contract.ts deleted file mode 100644 index 954aaa452..000000000 --- a/libs/smart-contracts/src/contracts/base-contract.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { ethers } from 'ethers'; -import type { TxData } from '.'; - -export class BaseContract { - public signer: ethers.Signer | null = null; - public provider: ethers.providers.Provider; - public transactions: TxData[] = []; - public listeners: ((transactions: TxData[]) => void)[] = []; - - constructor(provider: ethers.providers.Provider, signer?: ethers.Signer) { - this.provider = provider; - this.signer = signer || null; - } - - async handleEvent(event: ethers.Event, requiredConfirmations = 1) { - const tx = await event.getTransaction(); - // start tracking transaction if its not already in the transactions array - const existing = this.transactions.find((t) => t.tx.hash === tx.hash); - if (!existing) { - this.trackTransaction(tx, requiredConfirmations); - } - } - - async trackTransaction( - tx: ethers.providers.TransactionResponse, - requiredConfirmations = 1 - ) { - this.mergeTransaction({ - tx, - receipt: null, - pending: true, - requiredConfirmations, - }); - - let receipt = null; - - for (let i = 1; i <= requiredConfirmations; i++) { - receipt = await tx.wait(i); - this.mergeTransaction({ - tx, - receipt, - pending: true, - requiredConfirmations, - }); - } - - this.mergeTransaction({ - tx, - receipt, - pending: false, - requiredConfirmations, - }); - } - - private mergeTransaction(tx: TxData) { - this.transactions = [ - // Replace any existing transaction in the array with this one - ...this.transactions.filter((t) => t.tx.hash !== tx.tx.hash), - tx, - ]; - this.emit(); - } - - emit() { - this.listeners.forEach((ln) => { - ln(this.transactions); - }); - } - - listen(cb: (txs: TxData[]) => void) { - this.listeners.push(cb); - } -} diff --git a/libs/smart-contracts/src/contracts/vega-claim.ts b/libs/smart-contracts/src/contracts/claim.ts similarity index 51% rename from libs/smart-contracts/src/contracts/vega-claim.ts rename to libs/smart-contracts/src/contracts/claim.ts index 6ca34ec7c..998e0e530 100644 --- a/libs/smart-contracts/src/contracts/vega-claim.ts +++ b/libs/smart-contracts/src/contracts/claim.ts @@ -1,11 +1,6 @@ -import type BigNumber from 'bignumber.js'; import { ethers } from 'ethers'; -import { EnvironmentConfig } from '../config/ethereum'; -import type { Networks } from '../config/vega'; -import claimAbi from '../abis/claim_abi.json'; -import tokenAbi from '../abis/vega_token_abi.json'; -import { asciiToHex, removeDecimal } from '../utils'; -import { BaseContract } from './base-contract'; +import abi from '../abis/claim_abi.json'; +import { asciiToHex } from '../utils'; export const UNSPENT_CODE = '0x0000000000000000000000000000000000000000'; export const SPENT_CODE = '0x0000000000000000000000000000000000000001'; @@ -16,55 +11,27 @@ export const SPENT_CODE = '0x0000000000000000000000000000000000000001'; * const provider = new Web3.providers.HttpProvider( * "https://ropsten.infura.io/v3/5aff9e61ad844bcf982d0d0c3f1d29f1" * ); - * const web3 = new Web3(provider); - * + * const web3 = new Web3(provider) + * // Ropsten address * const contract = new VegaClaim(web3, "0xAf5dC1772714b2F4fae3b65eb83100f1Ea677b21") * contract.isCountryBlocked("US").then(console.log) * contract.isClaimValid({ claimCode: "0x...", expiry: 0, nonce: "0x00", account: "0x00" }) * ``` */ -export class VegaClaim extends BaseContract { +export class Claim { public contract: ethers.Contract; - public tokenContract: ethers.Contract; - public dp: Promise; constructor( - network: Networks, - provider: ethers.providers.Web3Provider, - signer?: ethers.Signer + address: string, + signerOrProvider: ethers.Signer | ethers.providers.Provider ) { - super(provider, signer); - - this.contract = new ethers.Contract( - EnvironmentConfig[network].claimAddress, - claimAbi, - this.signer || this.provider - ); - - const tokenContract = new ethers.Contract( - EnvironmentConfig[network].vegaTokenAddress, - tokenAbi, - this.signer || this.provider - ); - this.tokenContract = tokenContract; - - this.dp = (async () => { - const val = await tokenContract.decimals(); - return Number(val); - })(); + this.contract = new ethers.Contract(address, abi, signerOrProvider); } /** Execute contracts commit_untargeted function */ - async commit( - s: string, - confirmations = 1 - ): Promise { - const tx = await this.contract.commit_untargeted(s); - - this.trackTransaction(tx, confirmations); - - return tx; + public commit(s: string): Promise { + return this.contract.commit_untargeted(s); } /** @@ -73,36 +40,32 @@ export class VegaClaim extends BaseContract { * was performed and mined beforehand * @return {Promise} */ - public async claim( - { - amount, - tranche, - expiry, - target, - country, - v, - r, - s, - }: { - amount: BigNumber; - tranche: number; - expiry: number; - target?: string; - country: string; - v: number; - r: string; - s: string; - }, - confirmations = 1 - ): Promise { - const convertedAmount = removeDecimal(amount, await this.dp).toString(); - const tx = await this.contract[ + public claim({ + amount, + tranche, + expiry, + target, + country, + v, + r, + s, + }: { + amount: string; + tranche: number; + expiry: number; + target?: string; + country: string; + v: number; + r: string; + s: string; + }): Promise { + return this.contract[ target != null ? 'claim_targeted' : 'claim_untargeted' ]( ...[ { r, s, v }, { - amount: convertedAmount, + amount, tranche, expiry, }, @@ -110,18 +73,14 @@ export class VegaClaim extends BaseContract { target, ].filter(Boolean) ); - - this.trackTransaction(tx, confirmations); - - return tx; } /** * Check if this code was already committed to by this account * @return {Promise} */ - async isCommitted({ s }: { s: string }): Promise { - return await this.contract.commitments(s); + isCommitted({ s }: { s: string }): Promise { + return this.contract.commitments(s); } /** @@ -130,7 +89,7 @@ export class VegaClaim extends BaseContract { * @returns Promise */ async isExpired(expiry: number): Promise { - return expiry < (await this.provider.getBlock('latest')).timestamp; + return expiry < (await this.contract.provider.getBlock('latest')).timestamp; } /** diff --git a/libs/smart-contracts/src/contracts/collateral-bridge.ts b/libs/smart-contracts/src/contracts/collateral-bridge.ts new file mode 100644 index 000000000..3824491a8 --- /dev/null +++ b/libs/smart-contracts/src/contracts/collateral-bridge.ts @@ -0,0 +1,51 @@ +import type { BigNumber } from 'ethers'; +import { ethers } from 'ethers'; +import abi from '../abis/erc20_bridge_abi.json'; + +export class CollateralBridge { + public contract: ethers.Contract; + + constructor( + address: string, + signerOrProvider: ethers.Signer | ethers.providers.Provider + ) { + this.contract = new ethers.Contract(address, abi, signerOrProvider); + } + + depositAsset(assetSource: string, amount: string, vegaPublicKey: string) { + return this.contract.deposit_asset(assetSource, amount, vegaPublicKey); + } + getAssetSource(vegaAssetId: string) { + return this.contract.get_asset_source(vegaAssetId); + } + getDepositMaximum(assetSource: string): Promise { + return this.contract.get_deposit_maximum(assetSource); + } + getDepositMinimum(assetSource: string): Promise { + return this.contract.get_deposit_minimum(assetSource); + } + getMultisigControlAddres() { + return this.contract.get_multisig_control_address(); + } + getVegaAssetId(address: string) { + return this.contract.get_vega_asset_id(address); + } + isAssetListed(address: string) { + return this.contract.is_asset_listed(address); + } + withdrawAsset( + assetSource: string, + amount: string, + target: string, + nonce: string, + signatures: string + ) { + return this.contract.withdraw_asset( + assetSource, + amount, + target, + nonce, + signatures + ); + } +} diff --git a/libs/smart-contracts/src/contracts/erc20-token.ts b/libs/smart-contracts/src/contracts/erc20-token.ts deleted file mode 100644 index 64f63b6c4..000000000 --- a/libs/smart-contracts/src/contracts/erc20-token.ts +++ /dev/null @@ -1,111 +0,0 @@ -import type { BigNumber as EthersBigNumber } from 'ethers'; -import { ethers } from 'ethers'; -import erc20Abi from '../abis/erc20_abi.json'; -import erc20AbiFaucet from '../abis/erc20_abi_faucet.json'; -import BigNumber from 'bignumber.js'; -import { addDecimal, removeDecimal } from '../utils'; -import { BaseContract } from './base-contract'; - -export class ERC20Token extends BaseContract { - public contract: ethers.Contract; - public dp: Promise; - private faucetable: boolean; - - constructor( - address: string, - provider: ethers.providers.Web3Provider, - signer?: ethers.Signer, - faucetable = false - ) { - super(provider, signer); - - this.faucetable = faucetable; - const contract = new ethers.Contract( - address, - faucetable ? erc20AbiFaucet : erc20Abi, - signer || provider - ); - this.contract = contract; - this.dp = (async () => { - const val = await contract.decimals(); - return Number(val); - })(); - } - - /** Gets Vega token total supply */ - async totalSupply(): Promise { - const res: EthersBigNumber = await this.contract.totalSupply(); - const value = addDecimal(new BigNumber(res.toString()), await this.dp); - return value; - } - - /** Gets number tokens an Ethereum account owns */ - async balanceOf(address: string): Promise { - const res: EthersBigNumber = await this.contract.balanceOf(address); - const value = addDecimal(new BigNumber(res.toString()), await this.dp); - return value; - } - - async transfer( - from: string, - to: string, - amount: BigNumber, - confirmations = 1 - ) { - const value = removeDecimal(amount, await this.dp).toString(); - const tx = await this.contract.transfer(from, to, value); - this.trackTransaction(tx, confirmations); - return tx; - } - - async transferFrom( - sender: string, - recipient: string, - amount: BigNumber, - confirmations = 1 - ) { - const value = removeDecimal(amount, await this.dp).toString(); - const tx = await this.contract.transferFrom(sender, recipient, value); - this.trackTransaction(tx, confirmations); - return tx; - } - - /** Gets Ethereum account's permitted allowance */ - async allowance(address: string, spender: string): Promise { - const res: EthersBigNumber = await this.contract.allowance( - address, - spender - ); - const value = addDecimal(new BigNumber(res.toString()), await this.dp); - return value; - } - - /** Executs contracts approve function */ - async approve( - spender: string, - confirmations = 1 - ): Promise { - const amount = removeDecimal( - new BigNumber(Number.MAX_SAFE_INTEGER), - await this.dp - ).toString(); - const tx = await this.contract.approve(spender, amount); - this.trackTransaction(tx, confirmations); - return tx; - } - - async faucet(confirmations = 1): Promise { - if (!this.faucetable) { - throw new Error('Faucet function can not be called on this contract'); - } - const tx = await this.contract.faucet(); - this.trackTransaction(tx, confirmations); - return tx; - } - - /** Gets number of decimals used by token */ - async decimals(): Promise { - const res: number = await this.contract.decimals(); - return Number(res); - } -} diff --git a/libs/smart-contracts/src/contracts/index.ts b/libs/smart-contracts/src/contracts/index.ts index 86140f0e5..15ec967b1 100644 --- a/libs/smart-contracts/src/contracts/index.ts +++ b/libs/smart-contracts/src/contracts/index.ts @@ -1,8 +1,8 @@ -export * from './stake-helpers'; -export * from './tranche-helpers'; -export * from './vega-claim'; -export * from './vega-erc20-bridge'; -export * from './vega-staking'; -export * from './vega-vesting'; export * from './vega-web3-types'; -export * from './erc20-token'; + +export * from './claim'; +export * from './collateral-bridge'; +export * from './staking-bridge'; +export * from './token-vesting'; +export * from './token'; +export * from './token-faucetable'; diff --git a/libs/smart-contracts/src/contracts/stake-helpers.ts b/libs/smart-contracts/src/contracts/stake-helpers.ts deleted file mode 100644 index 8b924da6f..000000000 --- a/libs/smart-contracts/src/contracts/stake-helpers.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { ethers } from 'ethers'; - -import BigNumber from 'bignumber.js'; -import { addDecimal } from '../utils/decimals'; - -export function combineStakeEventsByVegaKey( - events: ethers.Event[], - decimals: number -): { [vegaKey: string]: BigNumber } { - const res = events.reduce((obj, e) => { - const vegaKey = e.args?.vega_public_key; - const amount = parseEventAmount(e, decimals); - const isDeposit = e.event === 'Stake_Deposited'; - const isRemove = e.event === 'Stake_Removed'; - - if (!isDeposit && !isRemove) return obj; - - if (Object.prototype.hasOwnProperty.call(obj, vegaKey)) { - if (isDeposit) { - obj[vegaKey] = obj[vegaKey].plus(amount); - } else { - obj[vegaKey] = obj[vegaKey].minus(amount); - } - } else { - if (isDeposit) { - obj[vegaKey] = amount; - } else { - obj[vegaKey] = new BigNumber(0); - } - } - return obj; - }, {} as { [vegaKey: string]: BigNumber }); - - return res; -} - -function parseEventAmount(e: ethers.Event, decimals: number) { - const rawAmount = new BigNumber(e.args?.amount.toString() || 0); - return addDecimal(rawAmount, decimals); -} diff --git a/libs/smart-contracts/src/contracts/staking-bridge.ts b/libs/smart-contracts/src/contracts/staking-bridge.ts new file mode 100644 index 000000000..036175596 --- /dev/null +++ b/libs/smart-contracts/src/contracts/staking-bridge.ts @@ -0,0 +1,32 @@ +import { ethers } from 'ethers'; +import abi from '../abis/staking_abi.json'; + +export class StakingBridge { + public contract: ethers.Contract; + + constructor( + address: string, + signerOrProvider: ethers.Signer | ethers.providers.Provider + ) { + this.contract = new ethers.Contract(address, abi, signerOrProvider); + } + + stake(amount: string, vegaPublicKey: string) { + return this.contract.stake(amount, `0x${vegaPublicKey}`); + } + removeStake(amount: string, vegaPublicKey: string) { + return this.contract.remove_stake(amount, `0x${vegaPublicKey}`); + } + transferStake(amount: string, newAddress: string, vegaPublicKey: string) { + return this.contract.transfer_stake(amount, newAddress, vegaPublicKey); + } + stakingToken() { + return this.contract.staking_token(); + } + stakeBalance(target: string, vegaPublicKey: string) { + return this.contract.stake_balance(target, vegaPublicKey); + } + totalStaked() { + return this.contract.total_staked(); + } +} diff --git a/libs/smart-contracts/src/contracts/token-faucetable.ts b/libs/smart-contracts/src/contracts/token-faucetable.ts new file mode 100644 index 000000000..86afcb8f8 --- /dev/null +++ b/libs/smart-contracts/src/contracts/token-faucetable.ts @@ -0,0 +1,37 @@ +import type { BigNumber } from 'ethers'; +import { ethers } from 'ethers'; +import erc20AbiFaucetable from '../abis/erc20_abi_faucet.json'; + +export class TokenFaucetable { + public contract: ethers.Contract; + + constructor( + address: string, + signerOrProvider: ethers.Signer | ethers.providers.Provider + ) { + this.contract = new ethers.Contract( + address, + erc20AbiFaucetable, + signerOrProvider + ); + } + + totalSupply() { + return this.contract.totalSupply(); + } + balanceOf(account: string): Promise { + return this.contract.balanceOf(account); + } + allowance(owner: string, spender: string): Promise { + return this.contract.allowance(owner, spender); + } + approve(spender: string, amount: string) { + return this.contract.approve(spender, amount); + } + decimals(): Promise { + return this.contract.decimals(); + } + faucet() { + return this.contract.faucet(); + } +} diff --git a/libs/smart-contracts/src/contracts/token-vesting.ts b/libs/smart-contracts/src/contracts/token-vesting.ts new file mode 100644 index 000000000..956098d94 --- /dev/null +++ b/libs/smart-contracts/src/contracts/token-vesting.ts @@ -0,0 +1,41 @@ +import { ethers } from 'ethers'; +import abi from '../abis/vesting_abi.json'; + +export class TokenVesting { + public contract: ethers.Contract; + + constructor( + address: string, + signerOrProvider: ethers.Signer | ethers.providers.Provider + ) { + this.contract = new ethers.Contract(address, abi, signerOrProvider); + } + + stakeTokens(amount: string, vegaPublicKey: string) { + return this.contract.stake_tokens(amount, vegaPublicKey); + } + removeStake(amount: string, vegaPublicKey: string) { + return this.contract.remove_stake(amount, vegaPublicKey); + } + stakeBalance(address: string, vegaPublicKey: string) { + return this.contract.stake_balance(address, vegaPublicKey); + } + totalStaked() { + return this.contract.total_staked(); + } + userStats(address: string) { + return this.contract.user_stats(address); + } + getTrancheBalance(address: string, trancheId: number) { + return this.contract.get_tranche_balance(address, trancheId); + } + getVestedForTranche(address: string, trancheId: number) { + return this.contract.get_vested_for_tranche(address, trancheId); + } + userTotalAllTranches(address: string) { + return this.contract.user_total_all_tranches(address); + } + withdrawFromTranche(trancheId: number) { + return this.contract.withdraw_from_tranche(trancheId); + } +} diff --git a/libs/smart-contracts/src/contracts/token.ts b/libs/smart-contracts/src/contracts/token.ts new file mode 100644 index 000000000..ac1ff0a41 --- /dev/null +++ b/libs/smart-contracts/src/contracts/token.ts @@ -0,0 +1,30 @@ +import type { BigNumber } from 'ethers'; +import { ethers } from 'ethers'; +import erc20Abi from '../abis/erc20_abi.json'; + +export class Token { + public contract: ethers.Contract; + + constructor( + address: string, + signerOrProvider: ethers.Signer | ethers.providers.Provider + ) { + this.contract = new ethers.Contract(address, erc20Abi, signerOrProvider); + } + + totalSupply() { + return this.contract.totalSupply(); + } + balanceOf(account: string): Promise { + return this.contract.balanceOf(account); + } + allowance(owner: string, spender: string): Promise { + return this.contract.allowance(owner, spender); + } + approve(spender: string, amount: string) { + return this.contract.approve(spender, amount); + } + decimals(): Promise { + return this.contract.decimals(); + } +} diff --git a/libs/smart-contracts/src/contracts/tranche-helpers.ts b/libs/smart-contracts/src/contracts/tranche-helpers.ts deleted file mode 100644 index 2d0a26d9b..000000000 --- a/libs/smart-contracts/src/contracts/tranche-helpers.ts +++ /dev/null @@ -1,165 +0,0 @@ -import BigNumber from 'bignumber.js'; -import type { ethers } from 'ethers'; -import uniq from 'lodash/uniq'; -import { addDecimal } from '../utils/decimals'; - -import type { Tranche, TrancheUser } from './vega-web3-types'; -import { TrancheEvents } from './vega-web3-types'; - -export function createUserTransactions( - events: ethers.Event[], - decimals: number -) { - return events.map((event) => { - return { - amount: addDecimal( - new BigNumber(event.args?.amount.toString()), - decimals - ), - user: event.args?.user, - tranche_id: event.args?.tranche_id, - tx: event.transactionHash, - }; - }); -} - -export function getUsersInTranche( - balanceAddedEvents: ethers.Event[], - balanceRemovedEvents: ethers.Event[], - addresses: string[], - decimals: number -): TrancheUser[] { - return addresses.map((address) => { - const userDeposits = balanceAddedEvents.filter( - (event) => event.args?.user === address - ); - const userWithdraws = balanceRemovedEvents.filter( - (event) => event.args?.user === address - ); - const deposits = createUserTransactions(userDeposits, decimals); - const withdrawals = createUserTransactions(userWithdraws, decimals); - const total_tokens = deposits.reduce( - (pre, cur) => pre.plus(cur.amount), - new BigNumber(0) - ); - const withdrawn_tokens = withdrawals.reduce( - (pre, cur) => pre.plus(cur.amount), - new BigNumber(0) - ); - const remaining_tokens = total_tokens.minus(withdrawn_tokens); - - return { - address, - deposits, - withdrawals, - total_tokens, - withdrawn_tokens, - remaining_tokens, - }; - }); -} - -export function sumFromEvents(events: ethers.Event[], decimals: number) { - const amounts = events.map((e) => - addDecimal(new BigNumber(e.args?.amount.toString()), decimals) - ); - // Start with a 0 so if there are none there is no NaN - return BigNumber.sum.apply(null, [new BigNumber(0), ...amounts]); -} - -export function getLockedAmount( - totalAdded: BigNumber, - cliffStart: number, - trancheDuration: number -) { - let amount = new BigNumber(0); - const ts = Math.round(new Date().getTime() / 1000); - const tranche_progress = (ts - cliffStart) / trancheDuration; - - if (tranche_progress < 0) { - amount = totalAdded; - } else if (tranche_progress < 1) { - amount = totalAdded.times(1 - tranche_progress); - } - - return amount; -} - -export function createTransactions(events: ethers.Event[], decimals: number) { - return events.map((event) => { - return { - amount: addDecimal( - new BigNumber(event.args?.amount.toString()), - decimals - ), - user: event.args?.user, - tx: event.transactionHash, - }; - }); -} - -export function getTranchesFromHistory( - createEvents: ethers.Event[], - addEvents: ethers.Event[], - removeEvents: ethers.Event[], - decimals: number -): Tranche[] { - return createEvents.map((event) => { - const tranche_id = event.args?.tranche_id; - const balanceAddedEvents = addEvents.filter( - (e) => - e.event === TrancheEvents.BalanceAdded && - e.args?.tranche_id === tranche_id - ); - const balanceRemovedEvents = removeEvents.filter( - (e) => - e.event === TrancheEvents.BalanceRemoved && - e.args?.tranche_id === tranche_id - ); - - //get tranche start and end dates - const tranche_duration = event.args?.duration; - const cliff_start = event.args?.cliff_start; - const tranche_start = new Date(cliff_start.mul(1000).toNumber()); - const tranche_end = new Date( - cliff_start.add(tranche_duration).mul(1000).toNumber() - ); - - // get added and removed values - const total_added = sumFromEvents(balanceAddedEvents, decimals); - const total_removed = sumFromEvents(balanceRemovedEvents, decimals); - // get locked amount - const locked_amount = getLockedAmount( - total_added, - cliff_start, - tranche_duration - ); - - // get all deposits and withdrawals - const deposits = createTransactions(balanceAddedEvents, decimals); - const withdrawals = createTransactions(balanceRemovedEvents, decimals); - - // get all users - const uniqueAddresses = uniq( - balanceAddedEvents.map((event) => event.args?.user) - ); - const users = getUsersInTranche( - balanceAddedEvents, - balanceRemovedEvents, - uniqueAddresses, - decimals - ); - - return { - tranche_id: parseInt(tranche_id), - tranche_start, - tranche_end, - total_added, - total_removed, - locked_amount, - deposits, - withdrawals, - users, - }; - }); -} diff --git a/libs/smart-contracts/src/contracts/vega-erc20-bridge.ts b/libs/smart-contracts/src/contracts/vega-erc20-bridge.ts deleted file mode 100644 index 3926658ae..000000000 --- a/libs/smart-contracts/src/contracts/vega-erc20-bridge.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type { BigNumber as EthersBigNumber } from 'ethers'; -import { ethers } from 'ethers'; -import { EnvironmentConfig } from '../config/ethereum'; -import type { Networks } from '../config/vega'; - -import erc20BridgeAbi from '../abis/erc20_bridge_abi.json'; -import { BaseContract } from './base-contract'; -import BigNumber from 'bignumber.js'; -import { addDecimal } from '../utils'; - -export class VegaErc20Bridge extends BaseContract { - private contract: ethers.Contract; - - constructor( - network: Networks, - provider: ethers.providers.Web3Provider, - signer?: ethers.Signer - ) { - super(provider, signer); - this.contract = new ethers.Contract( - EnvironmentConfig[network].erc20Bridge, - erc20BridgeAbi, - this.signer || this.provider - ); - } - - /** Executes contracts withdraw_asset function */ - async withdraw( - approval: { - assetSource: string; - amount: string; - nonce: string; - signatures: string; - targetAddress: string; - }, - confirmations = 1 - ): Promise { - const tx = await this.contract.withdraw_asset( - approval.assetSource, - approval.amount, // No need to remove decimals as this value is already set and not manipulated by the user - approval.targetAddress, - approval.nonce, - approval.signatures - ); - - this.trackTransaction(tx, confirmations); - - return tx; - } - - async depositAsset( - assetSource: string, - amount: string, - vegaPublicKey: string, - confirmations = 1 - ) { - const tx = await this.contract.deposit_asset( - assetSource, - amount, - vegaPublicKey - ); - - this.trackTransaction(tx, confirmations); - - return tx; - } - - async getAssetSource(vegaAssetId: string): Promise { - const res = await this.contract.get_asset_source(vegaAssetId); - return res; - } - - async getDepositMaximum( - assetSource: string, - decimals: number - ): Promise { - const res: EthersBigNumber = await this.contract.get_deposit_maximum( - assetSource - ); - const value = addDecimal(new BigNumber(res.toString()), decimals); - return value; - } - - async getDepositMinimum( - assetSource: string, - decimals: number - ): Promise { - const res: EthersBigNumber = await this.contract.get_deposit_minimum( - assetSource - ); - const value = addDecimal(new BigNumber(res.toString()), decimals); - return value; - } - - async getMultisigControlAddress(): Promise { - const res = await this.contract.get_multisig_control_address(); - return res; - } - - async getVegaAssetId(): Promise { - const res = await this.contract.get_vega_asset_id(); - return res; - } - - async isAssetListed(assetSource: string): Promise { - const res = await this.contract.is_asset_listed(assetSource); - return res; - } -} diff --git a/libs/smart-contracts/src/contracts/vega-staking.ts b/libs/smart-contracts/src/contracts/vega-staking.ts deleted file mode 100644 index b9c86ef45..000000000 --- a/libs/smart-contracts/src/contracts/vega-staking.ts +++ /dev/null @@ -1,131 +0,0 @@ -import type { BigNumber as EthersBigNumber } from 'ethers'; -import { ethers } from 'ethers'; -import stakingAbi from '../abis/staking_abi.json'; -import tokenAbi from '../abis/vega_token_abi.json'; -import { combineStakeEventsByVegaKey } from './stake-helpers'; -import BigNumber from 'bignumber.js'; -import { BaseContract } from './base-contract'; -import { EnvironmentConfig } from '../config/ethereum'; -import type { Networks } from '../config/vega'; -import { addDecimal, hexadecimalify, removeDecimal } from '../utils'; - -export class VegaStaking extends BaseContract { - public contract: ethers.Contract; - public tokenContract: ethers.Contract; - public dp: Promise; - - constructor( - network: Networks, - provider: ethers.providers.Web3Provider, - signer?: ethers.Signer - ) { - super(provider, signer); - - const tokenContract = new ethers.Contract( - EnvironmentConfig[network].vegaTokenAddress, - tokenAbi, - this.signer || this.provider - ); - this.tokenContract = tokenContract; - - this.contract = new ethers.Contract( - EnvironmentConfig[network].stakingBridge, - stakingAbi, - this.signer || this.provider - ); - - this.dp = (async () => { - const val = await tokenContract.decimals(); - return Number(val); - })(); - } - - /** Executes staking contracts stake function */ - async addStake( - amount: BigNumber, - vegaKey: string, - confirmations = 1 - ): Promise { - const convertedAmount = removeDecimal(amount, await this.dp).toString(); - - const tx = await this.contract.stake( - convertedAmount, - hexadecimalify(vegaKey) - ); - - // store and track the transaction in BaseContract - this.trackTransaction(tx, confirmations); - - return tx; - } - - /** Executes staking contracts remove_stake function */ - async removeStake( - amount: BigNumber, - vegaKey: string, - confirmations = 1 - ): Promise { - const convertedAmount = removeDecimal(amount, await this.dp).toString(); - - const tx = await this.contract.remove_stake( - convertedAmount, - hexadecimalify(vegaKey) - ); - - this.trackTransaction(tx, confirmations); - - return tx; - } - - /** Executes staking contracts transfer_stake function */ - async transferStake( - amount: BigNumber, - newAddress: string, - vegaKey: string, - confirmations = 1 - ): Promise { - const convertedAmount = removeDecimal(amount, await this.dp).toString(); - - const tx = await this.contract.transfer_stake( - convertedAmount, - newAddress, - hexadecimalify(vegaKey) - ); - - this.trackTransaction(tx, confirmations); - - return tx; - } - - /** Returns the amount staked for given Vega public key */ - async stakeBalance(address: string, vegaKey: string): Promise { - const res: EthersBigNumber = await this.contract.stake_balance( - address, - hexadecimalify(vegaKey) - ); - const value = addDecimal(new BigNumber(res.toString()), await this.dp); - return value; - } - - /** Returns the total amount currently staked */ - async totalStaked(): Promise { - const res: EthersBigNumber = await this.contract.total_staked(); - const value = addDecimal(new BigNumber(res.toString()), await this.dp); - return value; - } - - /** Returns amounts staked across all Vega keys for single Ethereum account */ - async userTotalStakedByVegaKey( - ethereumAccount: string - ): Promise<{ [vegaKey: string]: BigNumber }> { - const addFilter = this.contract.filters.Stake_Deposited(ethereumAccount); - const removeFilter = this.contract.filters.Stake_Removed(ethereumAccount); - const addEvents = await this.contract.queryFilter(addFilter); - const removeEvents = await this.contract.queryFilter(removeFilter); - const res = combineStakeEventsByVegaKey( - [...addEvents, ...removeEvents], - await this.dp - ); - return res; - } -} diff --git a/libs/smart-contracts/src/contracts/vega-vesting.ts b/libs/smart-contracts/src/contracts/vega-vesting.ts deleted file mode 100644 index 425672466..000000000 --- a/libs/smart-contracts/src/contracts/vega-vesting.ts +++ /dev/null @@ -1,182 +0,0 @@ -import BigNumber from 'bignumber.js'; -import type { BigNumber as EthersBigNumber } from 'ethers'; -import { ethers } from 'ethers'; -import { EnvironmentConfig } from '../config/ethereum'; -import type { Networks } from '../config/vega'; -import vestingAbi from '../abis/vesting_abi.json'; -import tokenAbi from '../abis/vega_token_abi.json'; -import { BaseContract } from './base-contract'; -import { combineStakeEventsByVegaKey } from './stake-helpers'; -import { getTranchesFromHistory } from './tranche-helpers'; -import type { Tranche } from './vega-web3-types'; -import { addDecimal, hexadecimalify, removeDecimal } from '../utils'; - -export class VegaVesting extends BaseContract { - public contract: ethers.Contract; - public tokenContract: ethers.Contract; - public dp: Promise; - - constructor( - network: Networks, - provider: ethers.providers.Web3Provider, - signer?: ethers.Signer - ) { - super(provider, signer); - - const tokenContract = new ethers.Contract( - EnvironmentConfig[network].vegaTokenAddress, - tokenAbi, - this.signer || this.provider - ); - this.tokenContract = tokenContract; - - this.contract = new ethers.Contract( - EnvironmentConfig[network].vestingAddress, - vestingAbi, - this.signer || this.provider - ); - - this.dp = (async () => { - const val = await tokenContract.decimals(); - return Number(val); - })(); - } - - /** Executes vesting contracts stake_tokens function */ - async addStake( - amount: BigNumber, - vegaKey: string, - confirmations = 1 - ): Promise { - const convertedAmount = removeDecimal(amount, await this.dp).toString(); - - const tx = await this.contract.stake_tokens( - convertedAmount, - hexadecimalify(vegaKey) - ); - - this.trackTransaction(tx, confirmations); - - return tx; - } - - /** Executes vesting contracts remove_stake function */ - async removeStake( - amount: BigNumber, - vegaKey: string, - confirmations = 1 - ): Promise { - const convertedAmount = removeDecimal(amount, await this.dp).toString(); - - const tx = await this.contract.remove_stake( - convertedAmount, - hexadecimalify(vegaKey) - ); - - this.trackTransaction(tx, confirmations); - - return tx; - } - - /** Returns the amount staked for a given Vega public key */ - async stakeBalance(address: string, vegaKey: string): Promise { - const res: EthersBigNumber = await this.contract.stake_balance( - address, - hexadecimalify(vegaKey) - ); - const value = addDecimal(new BigNumber(res.toString()), await this.dp); - return value; - } - - /** Returns the total amount currently staked */ - async totalStaked(): Promise { - const res: EthersBigNumber = await this.contract.total_staked(); - const value = addDecimal(new BigNumber(res.toString()), await this.dp); - return value; - } - - /** Returns the amount of locked tokens in the vesting contract */ - async getLien(address: string): Promise { - const { - lien, - }: { - lien: EthersBigNumber; - total_in_all_tranches: EthersBigNumber; - } = await this.contract.user_stats(address); - const value = addDecimal(new BigNumber(lien.toString()), await this.dp); - return value; - } - - /** Returns the amount a user has in a specific tranche */ - async userTrancheTotalBalance( - address: string, - tranche: number - ): Promise { - const amount: EthersBigNumber = await this.contract.get_tranche_balance( - address, - tranche - ); - const value = addDecimal(new BigNumber(amount.toString()), await this.dp); - return value; - } - - /** Returns vested amount for a given tranche */ - async userTrancheVestedBalance( - address: string, - tranche: number - ): Promise { - const amount: EthersBigNumber = await this.contract.get_vested_for_tranche( - address, - tranche - ); - const value = addDecimal(new BigNumber(amount.toString()), await this.dp); - return value; - } - - /** Returns the users total tokens across all tranches */ - async getUserBalanceAllTranches(account: string): Promise { - const amount: EthersBigNumber = await this.contract.user_total_all_tranches( - account - ); - const value = addDecimal(new BigNumber(amount.toString()), await this.dp); - return value; - } - - /** Gets all tranche data */ - async getAllTranches(): Promise { - const [created, added, removed] = await Promise.all([ - this.contract.queryFilter(this.contract.filters.Tranche_Created()), - this.contract.queryFilter(this.contract.filters.Tranche_Balance_Added()), - this.contract.queryFilter( - this.contract.filters.Tranche_Balance_Removed() - ), - ]); - const dp = await this.dp; - return getTranchesFromHistory(created, added, removed, dp); - } - - /** Executes contracts withdraw_from_tranche function */ - async withdrawFromTranche( - trancheId: number, - confirmations = 1 - ): Promise { - const tx = await this.contract.withdraw_from_tranche(trancheId); - - this.trackTransaction(tx, confirmations); - - return tx; - } - - /** Returns amounts staked across all Vega keys for single Ethereum account */ - async userTotalStakedByVegaKey(address: string) { - const addFilter = this.contract.filters.Stake_Deposited(address); - const removeFilter = this.contract.filters.Stake_Removed(address); - const addEvents = await this.contract.queryFilter(addFilter); - const removeEvents = await this.contract.queryFilter(removeFilter); - const res = combineStakeEventsByVegaKey( - [...addEvents, ...removeEvents], - await this.dp - ); - return res; - } -} diff --git a/libs/smart-contracts/src/contracts/vega-web3-types.ts b/libs/smart-contracts/src/contracts/vega-web3-types.ts index 8b5f5452b..210dc6384 100644 --- a/libs/smart-contracts/src/contracts/vega-web3-types.ts +++ b/libs/smart-contracts/src/contracts/vega-web3-types.ts @@ -1,4 +1,3 @@ -import type { ethers } from 'ethers'; import type BigNumber from 'bignumber.js'; export interface Tranche { @@ -74,10 +73,3 @@ export interface EpochDetails { startSeconds: BigNumber; endSeconds: BigNumber; } - -export interface TxData { - tx: ethers.ContractTransaction; - receipt: ethers.ContractReceipt | null; - pending: boolean; - requiredConfirmations: number; -} diff --git a/libs/smart-contracts/src/index.ts b/libs/smart-contracts/src/index.ts index 4d801929f..d49668b31 100644 --- a/libs/smart-contracts/src/index.ts +++ b/libs/smart-contracts/src/index.ts @@ -1,3 +1,2 @@ -export * from './config'; export * from './contracts'; export * from './utils'; diff --git a/libs/smart-contracts/src/utils/decimals.test.ts b/libs/smart-contracts/src/utils/decimals.test.ts deleted file mode 100644 index 3e4e42a44..000000000 --- a/libs/smart-contracts/src/utils/decimals.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import BigNumber from 'bignumber.js'; -import { addDecimal, removeDecimal } from './decimals'; - -test('Do not pad numbers with 0s when the number length is less than the specified DPs', () => { - expect(addDecimal(new BigNumber(10000), 10).toString()).toEqual('0.000001'); -}); - -test('Handles large numbers correctly', () => { - const claimCode = new BigNumber('20000000000000000000000000'); - const decimals = 18; - - const decimalised = addDecimal(claimCode, decimals); - expect(decimalised.toString()).toEqual('20000000'); - - const undecimalised = removeDecimal(claimCode, decimals); - expect(undecimalised.toString()).toEqual( - '20000000000000000000000000000000000000000000' - ); - - const mangled = removeDecimal(addDecimal(claimCode, decimals), decimals); - expect(mangled.toString()).toEqual('20000000000000000000000000'); -}); diff --git a/libs/smart-contracts/src/utils/decimals.ts b/libs/smart-contracts/src/utils/decimals.ts deleted file mode 100644 index 0654e6bb6..000000000 --- a/libs/smart-contracts/src/utils/decimals.ts +++ /dev/null @@ -1,10 +0,0 @@ -import BigNumber from 'bignumber.js'; - -BigNumber.config({ EXPONENTIAL_AT: 20000 }); - -export function addDecimal(value: BigNumber, decimals: number): BigNumber { - return value.dividedBy(Math.pow(10, decimals)).decimalPlaces(decimals); -} -export function removeDecimal(value: BigNumber, decimals: number): BigNumber { - return value.times(Math.pow(10, decimals)); -} diff --git a/libs/smart-contracts/src/utils/index.ts b/libs/smart-contracts/src/utils/index.ts index 967db1f40..96a2027d6 100644 --- a/libs/smart-contracts/src/utils/index.ts +++ b/libs/smart-contracts/src/utils/index.ts @@ -1,3 +1,2 @@ -export * from './decimals'; export * from './ascii-to-hex'; export * from './hexadecimalify'; diff --git a/libs/ui-toolkit/src/components/loader/loader.tsx b/libs/ui-toolkit/src/components/loader/loader.tsx index 18b0955c7..602519dfb 100644 --- a/libs/ui-toolkit/src/components/loader/loader.tsx +++ b/libs/ui-toolkit/src/components/loader/loader.tsx @@ -1,10 +1,12 @@ +import classNames from 'classnames'; import { useEffect, useState } from 'react'; interface LoaderProps { size?: 'small' | 'large'; + forceTheme?: 'dark' | 'light'; } -export const Loader = ({ size = 'large' }: LoaderProps) => { +export const Loader = ({ size = 'large', forceTheme }: LoaderProps) => { const [, forceRender] = useState(false); useEffect(() => { @@ -15,8 +17,14 @@ export const Loader = ({ size = 'large' }: LoaderProps) => { return () => clearInterval(interval); }, []); + const itemClasses = classNames({ + 'dark:bg-white bg-black': !forceTheme, + 'bg-white': forceTheme === 'dark', + 'bg-black': forceTheme === 'light', + 'w-16 h-16': size === 'large', + 'w-[5px] h-[5px]': size === 'small', + }); const wrapperClasses = size === 'small' ? 'w-[15px] h-[15px]' : 'w-64 h-64'; - const gridItemClasses = size === 'small' ? 'w-[5px] h-[5px]' : 'w-16 h-16'; const items = size === 'small' ? 9 : 16; return ( @@ -25,7 +33,7 @@ export const Loader = ({ size = 'large' }: LoaderProps) => { {new Array(items).fill(null).map((_, i) => { return (
0.75 ? 1 : 0, diff --git a/libs/web3/src/index.ts b/libs/web3/src/index.ts index 8106718bc..cc2f7a4bc 100644 --- a/libs/web3/src/index.ts +++ b/libs/web3/src/index.ts @@ -1,8 +1,11 @@ -export * from './lib/web3-provider'; -export * from './lib/web3-connect-dialog'; export * from './lib/ethereum-error'; +export * from './lib/__generated__/NetworkParamsQuery'; export * from './lib/use-bridge-contract'; export * from './lib/use-token-contract'; +export * from './lib/use-token-decimals'; +export * from './lib/use-ethereum-config'; export * from './lib/use-ethereum-read-contract'; export * from './lib/use-ethereum-transaction'; export * from './lib/transaction-dialog'; +export * from './lib/web3-provider'; +export * from './lib/web3-connect-dialog'; diff --git a/apps/trading/components/web3-container/__generated__/NetworkParamsQuery.ts b/libs/web3/src/lib/__generated__/NetworkParamsQuery.ts similarity index 100% rename from apps/trading/components/web3-container/__generated__/NetworkParamsQuery.ts rename to libs/web3/src/lib/__generated__/NetworkParamsQuery.ts diff --git a/libs/web3/src/lib/use-bridge-contract.ts b/libs/web3/src/lib/use-bridge-contract.ts index 0bc311af1..db7637bf2 100644 --- a/libs/web3/src/lib/use-bridge-contract.ts +++ b/libs/web3/src/lib/use-bridge-contract.ts @@ -1,18 +1,24 @@ -import { VegaErc20Bridge } from '@vegaprotocol/smart-contracts'; +import { CollateralBridge } from '@vegaprotocol/smart-contracts'; import { useWeb3React } from '@web3-react/core'; import { useMemo } from 'react'; -import { useEnvironment } from '@vegaprotocol/react-helpers'; +import { useEthereumConfig } from './use-ethereum-config'; export const useBridgeContract = () => { - const { VEGA_ENV } = useEnvironment(); const { provider } = useWeb3React(); + const { config } = useEthereumConfig(); const contract = useMemo(() => { - if (!provider) { + if (!provider || !config) { return null; } - return new VegaErc20Bridge(VEGA_ENV, provider, provider?.getSigner()); - }, [provider, VEGA_ENV]); + + const signer = provider.getSigner(); + + return new CollateralBridge( + config.collateral_bridge_contract.address, + signer || provider + ); + }, [provider, config]); return contract; }; diff --git a/libs/web3/src/lib/use-ethereum-config.ts b/libs/web3/src/lib/use-ethereum-config.ts new file mode 100644 index 000000000..af3603d0d --- /dev/null +++ b/libs/web3/src/lib/use-ethereum-config.ts @@ -0,0 +1,64 @@ +import { gql, useQuery } from '@apollo/client'; +import { useMemo } from 'react'; +import type { NetworkParamsQuery } from './__generated__/NetworkParamsQuery'; + +export interface EthereumConfig { + network_id: string; + chain_id: string; + confirmations: number; + collateral_bridge_contract: { + address: string; + }; + multisig_control_contract: { + address: string; + deployment_block_height: number; + }; + staking_bridge_contract: { + address: string; + deployment_block_height: number; + }; + token_vesting_contract: { + address: string; + deployment_block_height: number; + }; +} + +export const NETWORK_PARAMS_QUERY = gql` + query NetworkParamsQuery { + networkParameters { + key + value + } + } +`; + +export const useEthereumConfig = () => { + const { data, loading, error } = + useQuery(NETWORK_PARAMS_QUERY); + + const config = useMemo(() => { + if (!data) { + return null; + } + + const param = data.networkParameters?.find( + (np) => np.key === 'blockchains.ethereumConfig' + ); + + if (!param) { + return null; + } + + let parsedConfig: EthereumConfig; + + try { + parsedConfig = JSON.parse(param.value); + } catch { + return null; + } + + return parsedConfig; + }, [data]); + + return { config, loading, error }; +}; diff --git a/libs/web3/src/lib/use-token-contract.ts b/libs/web3/src/lib/use-token-contract.ts index 4a171ccb4..49cbae48f 100644 --- a/libs/web3/src/lib/use-token-contract.ts +++ b/libs/web3/src/lib/use-token-contract.ts @@ -1,22 +1,25 @@ -import { ERC20Token } from '@vegaprotocol/smart-contracts'; +import { Token, TokenFaucetable } from '@vegaprotocol/smart-contracts'; import { useWeb3React } from '@web3-react/core'; import { useMemo } from 'react'; export const useTokenContract = ( contractAddress?: string, faucetable = false -): ERC20Token | null => { +) => { const { provider } = useWeb3React(); + const contract = useMemo(() => { if (!provider || !contractAddress) { return null; } - return new ERC20Token( - contractAddress, - provider, - provider.getSigner(), - faucetable - ); + + const signer = provider.getSigner(); + + if (faucetable) { + return new TokenFaucetable(contractAddress, signer || provider); + } else { + return new Token(contractAddress, signer || provider); + } }, [provider, contractAddress, faucetable]); return contract; diff --git a/libs/web3/src/lib/use-token-decimals.ts b/libs/web3/src/lib/use-token-decimals.ts new file mode 100644 index 000000000..1ac7f232b --- /dev/null +++ b/libs/web3/src/lib/use-token-decimals.ts @@ -0,0 +1,20 @@ +import type { Token } from '@vegaprotocol/smart-contracts'; +import { useCallback } from 'react'; +import { useEthereumReadContract } from './use-ethereum-read-contract'; + +export const useTokenDecimals = (contract: Token | null) => { + const getDecimals = useCallback(async () => { + if (!contract) { + return; + } + + const value = await contract.decimals(); + const decimals = Number(value); + + return decimals; + }, [contract]); + + const { state } = useEthereumReadContract(getDecimals); + + return state.data; +}; diff --git a/libs/withdraws/src/lib/use-complete-withdraw.ts b/libs/withdraws/src/lib/use-complete-withdraw.ts index 70a471db3..5171ee2d8 100644 --- a/libs/withdraws/src/lib/use-complete-withdraw.ts +++ b/libs/withdraws/src/lib/use-complete-withdraw.ts @@ -33,7 +33,13 @@ export const useCompleteWithdraw = () => { if (!contract) { return null; } - return contract.withdraw(args); + return contract.withdrawAsset( + args.assetSource, + args.amount, + args.targetAddress, + args.nonce, + args.signatures + ); }); const submit = useCallback( diff --git a/libs/withdraws/src/lib/use-withdraw.ts b/libs/withdraws/src/lib/use-withdraw.ts index 2fd5da72f..1f242c524 100644 --- a/libs/withdraws/src/lib/use-withdraw.ts +++ b/libs/withdraws/src/lib/use-withdraw.ts @@ -38,7 +38,13 @@ export const useWithdraw = (cancelled: boolean) => { if (!contract) { return null; } - return contract.withdraw(args); + return contract.withdrawAsset( + args.assetSource, + args.amount, + args.targetAddress, + args.nonce, + args.signatures + ); }); const { data, stopPolling } = useQuery(