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
This commit is contained in:
parent
a66be425be
commit
e463bbe238
@ -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) {
|
||||
|
@ -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 (
|
||||
<Callout
|
||||
title={t(
|
||||
|
@ -1,8 +1,8 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { toBigNum } from '@vegaprotocol/react-helpers';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React from 'react';
|
||||
|
||||
import { useEnvironment } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
@ -10,17 +10,19 @@ import {
|
||||
import { useContracts } from '../../contexts/contracts/contracts-context';
|
||||
import { useGetAssociationBreakdown } from '../../hooks/use-get-association-breakdown';
|
||||
import { useGetUserTrancheBalances } from '../../hooks/use-get-user-tranche-balances';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
|
||||
interface BalanceManagerProps {
|
||||
children: React.ReactElement;
|
||||
}
|
||||
|
||||
export const BalanceManager = ({ children }: BalanceManagerProps) => {
|
||||
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.
|
||||
|
@ -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 = () => {
|
||||
<WalletCardHeader>
|
||||
<h1 className="text-h3 uppercase">{t('ethereumKey')}</h1>
|
||||
{account && (
|
||||
<div className="font-mono px-4 text-right">
|
||||
<div>{truncateMiddle(account)}</div>
|
||||
<div className="px-4 text-right">
|
||||
<div className="font-mono">{truncateMiddle(account)}</div>
|
||||
{pendingTxs && (
|
||||
<div>
|
||||
<Button
|
||||
className="flex gap-2 justify-between p-4, bg-black text-white flex-nowrap whitespace-nowrap"
|
||||
<button
|
||||
className="flex items-center gap-4 p-4 border whitespace-nowrap"
|
||||
data-testid="pending-transactions-btn"
|
||||
onClick={() =>
|
||||
appDispatch({
|
||||
@ -203,9 +203,9 @@ export const EthWallet = () => {
|
||||
})
|
||||
}
|
||||
>
|
||||
<Loader size="small" />
|
||||
<Loader size="small" forceTheme="light" />
|
||||
{t('pendingTransactions')}
|
||||
</Button>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -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 }) => (
|
||||
<th className="border-b border-black-25 text-black-60 text-left font-normal">
|
||||
@ -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 = () => {
|
||||
<TransactionModalTd>
|
||||
<Link
|
||||
title={t('View transaction on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/tx/${transaction.tx.hash}`}
|
||||
target="_blank"
|
||||
href={`${ETHERSCAN_URL}/tx/${transaction.tx.hash}`}
|
||||
>
|
||||
{truncateMiddle(transaction.tx.hash)}
|
||||
</Link>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Networks } from '@vegaprotocol/smart-contracts';
|
||||
import { Networks } from '@vegaprotocol/react-helpers';
|
||||
|
||||
interface VegaNode {
|
||||
url: string;
|
||||
|
@ -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<
|
||||
|
@ -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<TxData[]>([]);
|
||||
const { config } = useEthereumConfig();
|
||||
const { VEGA_ENV, ADDRESSES } = useEnvironment();
|
||||
const [contracts, setContracts] = React.useState<Pick<
|
||||
ContractsContextShape,
|
||||
'token' | 'staking' | 'vesting' | 'claim' | 'erc20Bridge'
|
||||
@ -45,49 +44,25 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
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 (
|
||||
<ContractsContext.Provider value={{ ...contracts, transactions: txs }}>
|
||||
<ContractsContext.Provider value={contracts}>
|
||||
{children}
|
||||
</ContractsContext.Provider>
|
||||
);
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
@ -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<void> {
|
||||
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));
|
||||
}
|
||||
|
@ -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]);
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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({
|
||||
|
@ -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,
|
||||
]);
|
||||
};
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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<ethers.ContractTransaction>,
|
||||
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 });
|
||||
|
@ -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;
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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 (
|
||||
<Splash>
|
||||
<SplashLoader />
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading title={'Contracts'} />
|
||||
<hr />
|
||||
{[
|
||||
'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 (
|
||||
<div
|
||||
key={key}
|
||||
style={{ display: 'flex', justifyContent: 'space-between' }}
|
||||
>
|
||||
<div>{key}:</div>
|
||||
<Link
|
||||
title={t('View address on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/address/${contract.address}`}
|
||||
>
|
||||
{config.collateral_bridge_contract.address}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{Object.entries(ADDRESSES).map(([key, value]) => (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<div
|
||||
key={key}
|
||||
style={{ display: 'flex', justifyContent: 'space-between' }}
|
||||
>
|
||||
<div>{key}:</div>
|
||||
<Link
|
||||
title={t('View on Etherscan (opens in a new tab)')}
|
||||
title={t('View address on Etherscan')}
|
||||
href={`${ETHERSCAN_URL}/address/${value}`}
|
||||
target="_blank"
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
|
@ -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 (
|
||||
<Splash>
|
||||
<SplashLoader />
|
||||
@ -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}
|
||||
</Link>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
|
@ -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 (
|
||||
<NetworkParamsContainer>
|
||||
{({ confirmations }) => (
|
||||
<StakingWalletsContainer>
|
||||
{({ address, currVegaKey }) =>
|
||||
currVegaKey ? (
|
||||
<AssociatePage
|
||||
address={address}
|
||||
vegaKey={currVegaKey}
|
||||
requiredConfirmations={confirmations}
|
||||
/>
|
||||
) : (
|
||||
<AssociatePageNoVega />
|
||||
)
|
||||
}
|
||||
</StakingWalletsContainer>
|
||||
)}
|
||||
</NetworkParamsContainer>
|
||||
<StakingWalletsContainer>
|
||||
{({ address, currVegaKey }) =>
|
||||
currVegaKey ? (
|
||||
<AssociatePage
|
||||
address={address}
|
||||
vegaKey={currVegaKey}
|
||||
ethereumConfig={config}
|
||||
/>
|
||||
) : (
|
||||
<AssociatePageNoVega />
|
||||
)
|
||||
}
|
||||
</StakingWalletsContainer>
|
||||
);
|
||||
};
|
||||
|
@ -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}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
|
@ -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);
|
||||
|
@ -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<React.SetStateAction<string>>;
|
||||
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;
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
<TrancheLabel {...props} chainId={WRONG_CHAIN} />
|
||||
);
|
||||
@ -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(
|
||||
<TrancheLabel {...props} contract={WRONG_ADDRESS} />
|
||||
);
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('Renders null for right network, right contract address, tranche without a name', () => {
|
||||
const UNNAMED_TRANCHE = 0;
|
||||
|
@ -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<number, string[]> = {
|
||||
'5': ['Coinlist Option 1', 'Community Whitelist'],
|
||||
'6': ['Coinlist Option 2'],
|
||||
@ -11,8 +7,7 @@ const TRANCHE_LABELS: Record<number, string[]> = {
|
||||
};
|
||||
|
||||
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 (
|
||||
|
@ -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<ITranche[]>();
|
||||
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={
|
||||
<TrancheLabel
|
||||
contract={ADDRESSES.vestingAddress}
|
||||
chainId={`0x${chainId}` as EthereumChainId}
|
||||
id={tranche.tranche_id}
|
||||
/>
|
||||
<TrancheLabel chainId={chainId} id={tranche.tranche_id} />
|
||||
}
|
||||
/>
|
||||
<div
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import type { Tranche, EthereumChainId } from '@vegaprotocol/smart-contracts';
|
||||
import type { Tranche } from '@vegaprotocol/smart-contracts';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -8,7 +8,7 @@ import { TrancheItem } from '../redemption/tranche-item';
|
||||
import { TrancheLabel } from './tranche-label';
|
||||
import { VestingChart } from './vesting-chart';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEnvironment } from '@vegaprotocol/react-helpers';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
|
||||
const trancheMinimum = 10;
|
||||
|
||||
@ -16,13 +16,17 @@ const shouldShowTranche = (t: Tranche) =>
|
||||
!t.total_added.isLessThanOrEqualTo(trancheMinimum);
|
||||
|
||||
export const Tranches = () => {
|
||||
const { ADDRESSES } = useEnvironment();
|
||||
const tranches = useOutletContext<Tranche[]>();
|
||||
const [showAll, setShowAll] = React.useState<boolean>(false);
|
||||
const { t } = useTranslation();
|
||||
const { chainId } = useWeb3React();
|
||||
const { config } = useEthereumConfig();
|
||||
const filteredTranches = tranches?.filter(shouldShowTranche) || [];
|
||||
|
||||
if (!config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<h2 className="text-h4">{t('chartTitle')}</h2>
|
||||
@ -41,11 +45,7 @@ export const Tranches = () => {
|
||||
unlocked={tranche.total_added.minus(tranche.locked_amount)}
|
||||
total={tranche.total_added}
|
||||
secondaryHeader={
|
||||
<TrancheLabel
|
||||
contract={ADDRESSES.vestingAddress}
|
||||
chainId={`0x${chainId}` as EthereumChainId}
|
||||
id={tranche.tranche_id}
|
||||
/>
|
||||
<TrancheLabel chainId={chainId} id={tranche.tranche_id} />
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
|
42
apps/token/src/stores/transactions.ts
Normal file
42
apps/token/src/stores/transactions.ts
Normal file
@ -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<TxData>;
|
||||
add: (tx: TxData) => void;
|
||||
update: (tx: TxData) => void;
|
||||
remove: (tx: TxData) => void;
|
||||
}
|
||||
|
||||
export const useTransactionStore = create(
|
||||
(set: SetState<TransactionStore>, get: GetState<TransactionStore>) => ({
|
||||
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),
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -19,3 +19,7 @@ Given('I am on the homepage', () => {
|
||||
basePage.closeDialog();
|
||||
marketPage.validateMarketsAreDisplayed();
|
||||
});
|
||||
|
||||
Given('I can connect to Ethereum', () => {
|
||||
cy.mockWeb3Provider();
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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(
|
||||
<MockedProvider mocks={[mock]}>
|
||||
<Web3Container
|
||||
render={({ ethereumConfig }) => (
|
||||
<div>
|
||||
<div>Child</div>
|
||||
<div>{ethereumConfig.collateral_bridge_contract.address}</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Web3Container>
|
||||
<div>
|
||||
<div>Child</div>
|
||||
<div>{mockEthereumConfig.collateral_bridge_contract.address}</div>
|
||||
</div>
|
||||
</Web3Container>
|
||||
</MockedProvider>
|
||||
);
|
||||
}
|
||||
@ -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<NetworkParamsQuery> = {
|
||||
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();
|
||||
});
|
||||
|
@ -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 (
|
||||
<PageQueryContainer<NetworkParamsQuery>
|
||||
query={NETWORK_PARAMS_QUERY}
|
||||
render={(data) => {
|
||||
const ethereumConfigParam = data.networkParameters?.find(
|
||||
(np) => np.key === 'blockchains.ethereumConfig'
|
||||
);
|
||||
|
||||
if (!ethereumConfigParam) {
|
||||
return (
|
||||
<Splash>
|
||||
<p>{t('No ethereum config found')}</p>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
let ethereumConfig: EthereumConfig;
|
||||
|
||||
try {
|
||||
ethereumConfig = JSON.parse(ethereumConfigParam.value);
|
||||
} catch {
|
||||
return (
|
||||
<Splash>
|
||||
<p>{t('Could not parse config')}</p>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Web3Provider connectors={Connectors}>
|
||||
<Web3Content
|
||||
appChainId={Number(ethereumConfig.chain_id)}
|
||||
setDialogOpen={setDialogOpen}
|
||||
>
|
||||
{render({ ethereumConfig })}
|
||||
</Web3Content>
|
||||
<Web3ConnectDialog
|
||||
connectors={Connectors}
|
||||
dialogOpen={dialogOpen}
|
||||
setDialogOpen={setDialogOpen}
|
||||
desiredChainId={Number(ethereumConfig.chain_id)}
|
||||
/>
|
||||
</Web3Provider>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<AsyncRenderer data={config} loading={loading} error={error}>
|
||||
{config ? (
|
||||
<Web3Provider connectors={Connectors}>
|
||||
<Web3Content
|
||||
appChainId={Number(config.chain_id)}
|
||||
setDialogOpen={setDialogOpen}
|
||||
>
|
||||
{children}
|
||||
</Web3Content>
|
||||
<Web3ConnectDialog
|
||||
connectors={Connectors}
|
||||
dialogOpen={dialogOpen}
|
||||
setDialogOpen={setDialogOpen}
|
||||
desiredChainId={Number(config.chain_id)}
|
||||
/>
|
||||
</Web3Provider>
|
||||
) : null}
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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 (
|
||||
<DepositManager
|
||||
bridgeAddress={ethereumConfig.collateral_bridge_contract.address}
|
||||
requiredConfirmations={ethereumConfig.confirmations}
|
||||
assets={data.assets}
|
||||
initialAssetId={assetId}
|
||||
isFaucetable={VEGA_ENV !== 'MAINNET'}
|
||||
|
@ -20,14 +20,12 @@ const Deposit = () => {
|
||||
}, [query]);
|
||||
|
||||
return (
|
||||
<Web3Container
|
||||
render={({ ethereumConfig }) => (
|
||||
<div className="max-w-[420px] p-24 mx-auto">
|
||||
<h1 className="text-h3 mb-12">Deposit</h1>
|
||||
<DepositContainer ethereumConfig={ethereumConfig} assetId={assetId} />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Web3Container>
|
||||
<div className="max-w-[420px] p-24 mx-auto">
|
||||
<h1 className="text-h3 mb-12">Deposit</h1>
|
||||
<DepositContainer assetId={assetId} />
|
||||
</div>
|
||||
</Web3Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -12,68 +12,66 @@ const Portfolio = () => {
|
||||
const tabClassName = 'p-[16px] pl-[316px]';
|
||||
|
||||
return (
|
||||
<Web3Container
|
||||
render={() => (
|
||||
<div className="h-full text-ui">
|
||||
<main className="relative h-[calc(100%-200px)]">
|
||||
<aside className="absolute px-[8px] py-[16px] w-[300px] mt-[28px] h-[calc(100%-28px)] w-[300px] overflow-auto">
|
||||
<h2 className="text-h4 text-black dark:text-white">
|
||||
{t('Filters')}
|
||||
</h2>
|
||||
</aside>
|
||||
<section>
|
||||
<GridTabs group="portfolio">
|
||||
<GridTab id="positions" name={t('Positions')}>
|
||||
<div className={tabClassName}>
|
||||
<h4 className="text-h4 text-black dark:text-white">
|
||||
{t('Positions')}
|
||||
</h4>
|
||||
<PositionsContainer />
|
||||
</div>
|
||||
</GridTab>
|
||||
<GridTab id="orders" name={t('Orders')}>
|
||||
<div className={tabClassName}>
|
||||
<h4 className="text-h4 text-black dark:text-white">
|
||||
{t('Orders')}
|
||||
</h4>
|
||||
<OrderListContainer />
|
||||
</div>
|
||||
</GridTab>
|
||||
<GridTab id="fills" name={t('Fills')}>
|
||||
<div className={tabClassName}>
|
||||
<h4 className="text-h4 text-black dark:text-white">
|
||||
{t('Fills')}
|
||||
</h4>
|
||||
</div>
|
||||
</GridTab>
|
||||
<GridTab id="history" name={t('History')}>
|
||||
<div className={tabClassName}>
|
||||
<h4 className="text-h4 text-black dark:text-white">
|
||||
{t('History')}
|
||||
</h4>
|
||||
</div>
|
||||
</GridTab>
|
||||
</GridTabs>
|
||||
</section>
|
||||
</main>
|
||||
<section className="fixed bottom-0 left-0 w-full h-[200px]">
|
||||
<GridTabs group="collaterals">
|
||||
<GridTab id="collateral" name={t('Collateral')}>
|
||||
<AccountsContainer />
|
||||
<Web3Container>
|
||||
<div className="h-full text-ui">
|
||||
<main className="relative h-[calc(100%-200px)]">
|
||||
<aside className="absolute px-[8px] py-[16px] w-[300px] mt-[28px] h-[calc(100%-28px)] w-[300px] overflow-auto">
|
||||
<h2 className="text-h4 text-black dark:text-white">
|
||||
{t('Filters')}
|
||||
</h2>
|
||||
</aside>
|
||||
<section>
|
||||
<GridTabs group="portfolio">
|
||||
<GridTab id="positions" name={t('Positions')}>
|
||||
<div className={tabClassName}>
|
||||
<h4 className="text-h4 text-black dark:text-white">
|
||||
{t('Positions')}
|
||||
</h4>
|
||||
<PositionsContainer />
|
||||
</div>
|
||||
</GridTab>
|
||||
<GridTab id="deposits" name={t('Deposits')}>
|
||||
<AnchorButton data-testid="deposit" href="/portfolio/deposit">
|
||||
{t('Deposit')}
|
||||
</AnchorButton>
|
||||
<GridTab id="orders" name={t('Orders')}>
|
||||
<div className={tabClassName}>
|
||||
<h4 className="text-h4 text-black dark:text-white">
|
||||
{t('Orders')}
|
||||
</h4>
|
||||
<OrderListContainer />
|
||||
</div>
|
||||
</GridTab>
|
||||
<GridTab id="withdrawals" name={t('Withdrawals')}>
|
||||
<WithdrawalsContainer />
|
||||
<GridTab id="fills" name={t('Fills')}>
|
||||
<div className={tabClassName}>
|
||||
<h4 className="text-h4 text-black dark:text-white">
|
||||
{t('Fills')}
|
||||
</h4>
|
||||
</div>
|
||||
</GridTab>
|
||||
<GridTab id="history" name={t('History')}>
|
||||
<div className={tabClassName}>
|
||||
<h4 className="text-h4 text-black dark:text-white">
|
||||
{t('History')}
|
||||
</h4>
|
||||
</div>
|
||||
</GridTab>
|
||||
</GridTabs>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</main>
|
||||
<section className="fixed bottom-0 left-0 w-full h-[200px]">
|
||||
<GridTabs group="collaterals">
|
||||
<GridTab id="collateral" name={t('Collateral')}>
|
||||
<AccountsContainer />
|
||||
</GridTab>
|
||||
<GridTab id="deposits" name={t('Deposits')}>
|
||||
<AnchorButton data-testid="deposit" href="/portfolio/deposit">
|
||||
{t('Deposit')}
|
||||
</AnchorButton>
|
||||
</GridTab>
|
||||
<GridTab id="withdrawals" name={t('Withdrawals')}>
|
||||
<WithdrawalsContainer />
|
||||
</GridTab>
|
||||
</GridTabs>
|
||||
</section>
|
||||
</div>
|
||||
</Web3Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -23,14 +23,12 @@ const Withdraw = () => {
|
||||
|
||||
return (
|
||||
<VegaWalletContainer>
|
||||
<Web3Container
|
||||
render={() => (
|
||||
<div className="max-w-[420px] p-24 mx-auto">
|
||||
<h1 className="text-h3 mb-12">{t('Withdraw')}</h1>
|
||||
<WithdrawPageContainer assetId={assetId} />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Web3Container>
|
||||
<div className="max-w-[420px] p-24 mx-auto">
|
||||
<h1 className="text-h3 mb-12">{t('Withdraw')}</h1>
|
||||
<WithdrawPageContainer assetId={assetId} />
|
||||
</div>
|
||||
</Web3Container>
|
||||
</VegaWalletContainer>
|
||||
);
|
||||
};
|
||||
|
@ -7,19 +7,17 @@ import { WithdrawalsContainer } from './withdrawals-container';
|
||||
const Withdrawals = () => {
|
||||
return (
|
||||
<VegaWalletContainer>
|
||||
<Web3Container
|
||||
render={() => (
|
||||
<div className="h-full grid grid grid-rows-[min-content,1fr]">
|
||||
<header className="flex justify-between p-24">
|
||||
<h1 className="text-h3">{t('Withdrawals')}</h1>
|
||||
<AnchorButton href="/portfolio/withdraw">
|
||||
{t('Start withdrawal')}
|
||||
</AnchorButton>
|
||||
</header>
|
||||
<WithdrawalsContainer />
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Web3Container>
|
||||
<div className="h-full grid grid grid-rows-[min-content,1fr]">
|
||||
<header className="flex justify-between p-24">
|
||||
<h1 className="text-h3">{t('Withdrawals')}</h1>
|
||||
<AnchorButton href="/portfolio/withdraw">
|
||||
{t('Start withdrawal')}
|
||||
</AnchorButton>
|
||||
</header>
|
||||
<WithdrawalsContainer />
|
||||
</div>
|
||||
</Web3Container>
|
||||
</VegaWalletContainer>
|
||||
);
|
||||
};
|
||||
|
@ -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 (
|
||||
<>
|
||||
<DepositForm
|
||||
available={balanceOf}
|
||||
available={balance}
|
||||
selectedAsset={asset}
|
||||
onSelectAsset={(id) => 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}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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 };
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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<DepositEvent_busEvents_event_Deposit | null>(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<DepositEvent, DepositEventVariables>(DEPOSIT_EVENT_SUB, {
|
||||
variables: { partyId: partyId ? remove0x(partyId) : '' },
|
||||
|
@ -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();
|
||||
|
@ -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<RawEnvironment>;
|
||||
@ -106,7 +144,7 @@ export const EnvironmentProvider = ({
|
||||
<EnvironmentContext.Provider
|
||||
value={{
|
||||
...environment,
|
||||
ADDRESSES: EnvironmentConfig[environment['VEGA_ENV']],
|
||||
ADDRESSES: ContractAddresses[environment['VEGA_ENV']],
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -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';
|
||||
|
@ -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 {
|
||||
|
@ -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<EthereumChainId, EthereumChainName> = {
|
||||
'0x1': 'Mainnet',
|
||||
'0x3': 'Ropsten',
|
||||
'0x4': 'Rinkeby',
|
||||
'0x5': 'Goerli',
|
||||
'0x2a': 'Kovan',
|
||||
};
|
||||
|
||||
export const EthereumChainIds: Record<EthereumChainName, EthereumChainId> = {
|
||||
Mainnet: '0x1',
|
||||
Ropsten: '0x3',
|
||||
Rinkeby: '0x4',
|
||||
Goerli: '0x5',
|
||||
Kovan: '0x2a',
|
||||
};
|
||||
|
||||
export const ChainIdMap: Record<EthereumChainId, number> = {
|
||||
'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 },
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
export * from './ethereum';
|
||||
export * from './vega';
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<number>;
|
||||
|
||||
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<ethers.ContractTransaction> {
|
||||
const tx = await this.contract.commit_untargeted(s);
|
||||
|
||||
this.trackTransaction(tx, confirmations);
|
||||
|
||||
return tx;
|
||||
public commit(s: string): Promise<ethers.ContractTransaction> {
|
||||
return this.contract.commit_untargeted(s);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,36 +40,32 @@ export class VegaClaim extends BaseContract {
|
||||
* was performed and mined beforehand
|
||||
* @return {Promise<boolean>}
|
||||
*/
|
||||
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<ethers.ContractTransaction> {
|
||||
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<ethers.ContractTransaction> {
|
||||
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<boolean>}
|
||||
*/
|
||||
async isCommitted({ s }: { s: string }): Promise<string> {
|
||||
return await this.contract.commitments(s);
|
||||
isCommitted({ s }: { s: string }): Promise<string> {
|
||||
return this.contract.commitments(s);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,7 +89,7 @@ export class VegaClaim extends BaseContract {
|
||||
* @returns Promise<boolean>
|
||||
*/
|
||||
async isExpired(expiry: number): Promise<boolean> {
|
||||
return expiry < (await this.provider.getBlock('latest')).timestamp;
|
||||
return expiry < (await this.contract.provider.getBlock('latest')).timestamp;
|
||||
}
|
||||
|
||||
/**
|
51
libs/smart-contracts/src/contracts/collateral-bridge.ts
Normal file
51
libs/smart-contracts/src/contracts/collateral-bridge.ts
Normal file
@ -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<BigNumber> {
|
||||
return this.contract.get_deposit_maximum(assetSource);
|
||||
}
|
||||
getDepositMinimum(assetSource: string): Promise<BigNumber> {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
@ -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<number>;
|
||||
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<BigNumber> {
|
||||
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<BigNumber> {
|
||||
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<BigNumber> {
|
||||
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<ethers.ContractTransaction> {
|
||||
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<ethers.ContractTransaction> {
|
||||
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<number> {
|
||||
const res: number = await this.contract.decimals();
|
||||
return Number(res);
|
||||
}
|
||||
}
|
@ -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';
|
||||
|
@ -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);
|
||||
}
|
32
libs/smart-contracts/src/contracts/staking-bridge.ts
Normal file
32
libs/smart-contracts/src/contracts/staking-bridge.ts
Normal file
@ -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();
|
||||
}
|
||||
}
|
37
libs/smart-contracts/src/contracts/token-faucetable.ts
Normal file
37
libs/smart-contracts/src/contracts/token-faucetable.ts
Normal file
@ -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<BigNumber> {
|
||||
return this.contract.balanceOf(account);
|
||||
}
|
||||
allowance(owner: string, spender: string): Promise<BigNumber> {
|
||||
return this.contract.allowance(owner, spender);
|
||||
}
|
||||
approve(spender: string, amount: string) {
|
||||
return this.contract.approve(spender, amount);
|
||||
}
|
||||
decimals(): Promise<number> {
|
||||
return this.contract.decimals();
|
||||
}
|
||||
faucet() {
|
||||
return this.contract.faucet();
|
||||
}
|
||||
}
|
41
libs/smart-contracts/src/contracts/token-vesting.ts
Normal file
41
libs/smart-contracts/src/contracts/token-vesting.ts
Normal file
@ -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);
|
||||
}
|
||||
}
|
30
libs/smart-contracts/src/contracts/token.ts
Normal file
30
libs/smart-contracts/src/contracts/token.ts
Normal file
@ -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<BigNumber> {
|
||||
return this.contract.balanceOf(account);
|
||||
}
|
||||
allowance(owner: string, spender: string): Promise<BigNumber> {
|
||||
return this.contract.allowance(owner, spender);
|
||||
}
|
||||
approve(spender: string, amount: string) {
|
||||
return this.contract.approve(spender, amount);
|
||||
}
|
||||
decimals(): Promise<number> {
|
||||
return this.contract.decimals();
|
||||
}
|
||||
}
|
@ -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,
|
||||
};
|
||||
});
|
||||
}
|
@ -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<ethers.ContractTransaction> {
|
||||
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<string> {
|
||||
const res = await this.contract.get_asset_source(vegaAssetId);
|
||||
return res;
|
||||
}
|
||||
|
||||
async getDepositMaximum(
|
||||
assetSource: string,
|
||||
decimals: number
|
||||
): Promise<BigNumber> {
|
||||
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<BigNumber> {
|
||||
const res: EthersBigNumber = await this.contract.get_deposit_minimum(
|
||||
assetSource
|
||||
);
|
||||
const value = addDecimal(new BigNumber(res.toString()), decimals);
|
||||
return value;
|
||||
}
|
||||
|
||||
async getMultisigControlAddress(): Promise<string> {
|
||||
const res = await this.contract.get_multisig_control_address();
|
||||
return res;
|
||||
}
|
||||
|
||||
async getVegaAssetId(): Promise<string> {
|
||||
const res = await this.contract.get_vega_asset_id();
|
||||
return res;
|
||||
}
|
||||
|
||||
async isAssetListed(assetSource: string): Promise<boolean> {
|
||||
const res = await this.contract.is_asset_listed(assetSource);
|
||||
return res;
|
||||
}
|
||||
}
|
@ -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<number>;
|
||||
|
||||
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<ethers.ContractTransaction> {
|
||||
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<ethers.ContractTransaction> {
|
||||
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<ethers.ContractTransaction> {
|
||||
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<BigNumber> {
|
||||
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<BigNumber> {
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<number>;
|
||||
|
||||
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<ethers.ContractTransaction> {
|
||||
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<ethers.ContractTransaction> {
|
||||
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<BigNumber> {
|
||||
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<BigNumber> {
|
||||
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<BigNumber> {
|
||||
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<BigNumber> {
|
||||
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<BigNumber> {
|
||||
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<BigNumber> {
|
||||
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<Tranche[]> {
|
||||
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<ethers.ContractTransaction> {
|
||||
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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -1,3 +1,2 @@
|
||||
export * from './config';
|
||||
export * from './contracts';
|
||||
export * from './utils';
|
||||
|
@ -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');
|
||||
});
|
@ -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));
|
||||
}
|
@ -1,3 +1,2 @@
|
||||
export * from './decimals';
|
||||
export * from './ascii-to-hex';
|
||||
export * from './hexadecimalify';
|
||||
|
@ -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 (
|
||||
<div
|
||||
className={`${gridItemClasses} dark:bg-white bg-black`}
|
||||
className={itemClasses}
|
||||
key={i}
|
||||
style={{
|
||||
opacity: Math.random() > 0.75 ? 1 : 0,
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
};
|
||||
|
64
libs/web3/src/lib/use-ethereum-config.ts
Normal file
64
libs/web3/src/lib/use-ethereum-config.ts
Normal file
@ -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<NetworkParamsQuery>(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 };
|
||||
};
|
@ -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;
|
||||
|
20
libs/web3/src/lib/use-token-decimals.ts
Normal file
20
libs/web3/src/lib/use-token-decimals.ts
Normal file
@ -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;
|
||||
};
|
@ -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(
|
||||
|
@ -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<Erc20Approval, Erc20ApprovalVariables>(
|
||||
|
Loading…
Reference in New Issue
Block a user