Feat/smart contracts update (#536)

* feat: add new bridge contract logic

* chore: remove unused contract from provider

* chore: remove minimum as no longer exists

* feat: use new withdrawals contract, but allow for old contract for token

* feat: power contracts selection by a flag

* style: lint

* Update libs/smart-contracts/src/contracts/collateral-bridge-new.ts

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>

* chore: rename env var as per feedback

* chore: consistent varaible names as per PR comments

* chore: add back in checks for minimum as per PR comments

* style: formatting

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
Dexter Edwards 2022-06-10 11:36:38 +01:00 committed by GitHub
parent ecda46caa5
commit 9266ff4bd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1254 additions and 150 deletions

View File

@ -25,6 +25,7 @@ NX_ETHEREUM_CHAIN_ID = 3
NX_ETHEREUM_PROVIDER_URL = "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8" NX_ETHEREUM_PROVIDER_URL = "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8"
NX_ETHERSCAN_URL = "https://ropsten.etherscan.io" NX_ETHERSCAN_URL = "https://ropsten.etherscan.io"
NX_FAIRGROUND = false NX_FAIRGROUND = false
NX_IS_NEW_BRIDGE_CONTRACT = true
NX_VEGA_NETWORKS='{\"DEVNET\":\"https://dev.token.vega.xyz\",\"STAGNET\":\"https://dev.token.vega.xyz\",\"STAGNET2\":\"staging2.token.vega.xyz\",\"TESTNET\":\"token.fairground.wtf\",\"MAINNET\":\"token.vega.xyz\"}' NX_VEGA_NETWORKS='{\"DEVNET\":\"https://dev.token.vega.xyz\",\"STAGNET\":\"https://dev.token.vega.xyz\",\"STAGNET2\":\"staging2.token.vega.xyz\",\"TESTNET\":\"token.fairground.wtf\",\"MAINNET\":\"token.vega.xyz\"}'
#Test configuration variables #Test configuration variables

View File

@ -8,4 +8,7 @@ export const Flags = {
MOCK: TRUTHY.includes(process.env['NX_MOCKED'] as string), MOCK: TRUTHY.includes(process.env['NX_MOCKED'] as string),
FAIRGROUND: TRUTHY.includes(process.env['NX_FAIRGROUND'] as string), FAIRGROUND: TRUTHY.includes(process.env['NX_FAIRGROUND'] as string),
NETWORK_LIMITS: TRUTHY.includes(process.env['NX_NETWORK_LIMITS'] as string), NETWORK_LIMITS: TRUTHY.includes(process.env['NX_NETWORK_LIMITS'] as string),
USE_NEW_BRIDGE_CONTRACT: TRUTHY.includes(
process.env['NX_IS_NEW_BRIDGE_CONTRACT'] as string
),
}; };

View File

@ -1,6 +1,5 @@
import type { import type {
Claim, Claim,
CollateralBridge,
Token, Token,
TokenVesting, TokenVesting,
StakingBridge, StakingBridge,
@ -12,7 +11,6 @@ export interface ContractsContextShape {
staking: StakingBridge; staking: StakingBridge;
vesting: TokenVesting; vesting: TokenVesting;
claim: Claim; claim: Claim;
erc20Bridge: CollateralBridge;
} }
export const ContractsContext = React.createContext< export const ContractsContext = React.createContext<

View File

@ -2,7 +2,6 @@ import {
Token, Token,
TokenVesting, TokenVesting,
Claim, Claim,
CollateralBridge,
StakingBridge, StakingBridge,
} from '@vegaprotocol/smart-contracts'; } from '@vegaprotocol/smart-contracts';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { Splash } from '@vegaprotocol/ui-toolkit';
@ -23,10 +22,8 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
const { provider: activeProvider, account } = useWeb3React(); const { provider: activeProvider, account } = useWeb3React();
const { config } = useEthereumConfig(); const { config } = useEthereumConfig();
const { VEGA_ENV, ADDRESSES } = useEnvironment(); const { VEGA_ENV, ADDRESSES } = useEnvironment();
const [contracts, setContracts] = React.useState<Pick< const [contracts, setContracts] =
ContractsContextShape, React.useState<ContractsContextShape | null>(null);
'token' | 'staking' | 'vesting' | 'claim' | 'erc20Bridge'
> | null>(null);
// Create instances of contract classes. If we have an account use a signer for the // Create instances of contract classes. If we have an account use a signer for the
// contracts so that we can sign transactions, otherwise use the provider for just // contracts so that we can sign transactions, otherwise use the provider for just
@ -56,10 +53,6 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
signer || provider signer || provider
), ),
claim: new Claim(ADDRESSES.claimAddress, signer || provider), claim: new Claim(ADDRESSES.claimAddress, signer || provider),
erc20Bridge: new CollateralBridge(
config.collateral_bridge_contract.address,
signer || provider
),
}); });
} }
}, [activeProvider, account, config, ADDRESSES, VEGA_ENV]); }, [activeProvider, account, config, ADDRESSES, VEGA_ENV]);

View File

@ -15,6 +15,7 @@ import type {
WithdrawPageVariables, WithdrawPageVariables,
} from './__generated__/WithdrawPage'; } from './__generated__/WithdrawPage';
import { WithdrawManager } from '@vegaprotocol/withdraws'; import { WithdrawManager } from '@vegaprotocol/withdraws';
import { Flags } from '../../config';
const Withdraw = () => { const Withdraw = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -149,7 +150,11 @@ export const WithdrawContainer = ({ currVegaKey }: WithdrawContainerProps) => {
</Callout> </Callout>
</div> </div>
)} )}
<WithdrawManager assets={data.assets || []} accounts={accounts} /> <WithdrawManager
assets={data.assets || []}
accounts={accounts}
isNewContract={Flags.USE_NEW_BRIDGE_CONTRACT}
/>
</> </>
); );
}; };

View File

@ -18,6 +18,7 @@ import type { Withdrawals_party_withdrawals } from '@vegaprotocol/withdraws';
import { useCompleteWithdraw, useWithdrawals } from '@vegaprotocol/withdraws'; import { useCompleteWithdraw, useWithdrawals } from '@vegaprotocol/withdraws';
import { TransactionDialog } from '@vegaprotocol/web3'; import { TransactionDialog } from '@vegaprotocol/web3';
import { WithdrawalStatus } from '../../__generated__/globalTypes'; import { WithdrawalStatus } from '../../__generated__/globalTypes';
import { Flags } from '../../config';
const Withdrawals = () => { const Withdrawals = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -34,7 +35,9 @@ const Withdrawals = () => {
const WithdrawPendingContainer = () => { const WithdrawPendingContainer = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { transaction, submit } = useCompleteWithdraw(); const { transaction, submit } = useCompleteWithdraw(
Flags.USE_NEW_BRIDGE_CONTRACT
);
const { data, loading, error } = useWithdrawals(); const { data, loading, error } = useWithdrawals();
const withdrawals = React.useMemo(() => { const withdrawals = React.useMemo(() => {

View File

@ -83,6 +83,7 @@ export const WithdrawPageContainer = ({
assets={data.assets} assets={data.assets}
accounts={data.party?.accounts || []} accounts={data.party?.accounts || []}
initialAssetId={assetId} initialAssetId={assetId}
isNewContract={true}
/> />
</> </>
); );

View File

@ -0,0 +1,112 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { MarketState, MarketTradingMode } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: DealTicketQuery
// ====================================================
export interface DealTicketQuery_market_tradableInstrument_instrument_product {
__typename: "Future";
/**
* String representing the quote (e.g. BTCUSD -> USD is quote)
*/
quoteName: string;
}
export interface DealTicketQuery_market_tradableInstrument_instrument {
__typename: "Instrument";
/**
* A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
*/
product: DealTicketQuery_market_tradableInstrument_instrument_product;
}
export interface DealTicketQuery_market_tradableInstrument {
__typename: "TradableInstrument";
/**
* An instance of or reference to a fully specified instrument.
*/
instrument: DealTicketQuery_market_tradableInstrument_instrument;
}
export interface DealTicketQuery_market_depth_lastTrade {
__typename: "Trade";
/**
* The price of the trade (probably initially the passive order price, other determination algorithms are possible though) (uint64)
*/
price: string;
}
export interface DealTicketQuery_market_depth {
__typename: "MarketDepth";
/**
* Last trade for the given market (if available)
*/
lastTrade: DealTicketQuery_market_depth_lastTrade | null;
}
export interface DealTicketQuery_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
/**
* Market full name
*/
name: string;
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the Market. (uint64)
*
* Examples:
* Currency Balance decimalPlaces Real Balance
* GBP 100 0 GBP 100
* GBP 100 2 GBP 1.00
* GBP 100 4 GBP 0.01
* GBP 1 4 GBP 0.0001 ( 0.01p )
*
* GBX (pence) 100 0 GBP 1.00 (100p )
* GBX (pence) 100 2 GBP 0.01 ( 1p )
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/
decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/**
* Current state of the market
*/
state: MarketState;
/**
* Current mode of execution of the market
*/
tradingMode: MarketTradingMode;
/**
* An instance of or reference to a tradable instrument.
*/
tradableInstrument: DealTicketQuery_market_tradableInstrument;
/**
* Current depth on the order book for this market
*/
depth: DealTicketQuery_market_depth;
}
export interface DealTicketQuery {
/**
* An instrument that is trading on the VEGA network
*/
market: DealTicketQuery_market | null;
}
export interface DealTicketQueryVariables {
marketId: string;
}

View File

@ -0,0 +1,108 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { BusEventType, OrderType, OrderStatus, OrderRejectionReason } from "@vegaprotocol/types";
// ====================================================
// GraphQL subscription operation: OrderEvent
// ====================================================
export interface OrderEvent_busEvents_event_TimeUpdate {
__typename: "TimeUpdate" | "MarketEvent" | "TransferResponses" | "PositionResolution" | "Trade" | "Account" | "Party" | "MarginLevels" | "Proposal" | "Vote" | "MarketData" | "NodeSignature" | "LossSocialization" | "SettlePosition" | "Market" | "Asset" | "MarketTick" | "SettleDistressed" | "AuctionEvent" | "RiskFactor" | "Deposit" | "Withdrawal" | "OracleSpec" | "LiquidityProvision";
}
export interface OrderEvent_busEvents_event_Order_market {
__typename: "Market";
/**
* Market full name
*/
name: string;
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the Market. (uint64)
*
* Examples:
* Currency Balance decimalPlaces Real Balance
* GBP 100 0 GBP 100
* GBP 100 2 GBP 1.00
* GBP 100 4 GBP 0.01
* GBP 1 4 GBP 0.0001 ( 0.01p )
*
* GBX (pence) 100 0 GBP 1.00 (100p )
* GBX (pence) 100 2 GBP 0.01 ( 1p )
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/
decimalPlaces: number;
}
export interface OrderEvent_busEvents_event_Order {
__typename: "Order";
/**
* Type the order type (defaults to PARTY)
*/
type: OrderType | null;
/**
* Hash of the order data
*/
id: string;
/**
* The status of an order, for example 'Active'
*/
status: OrderStatus;
/**
* Reason for the order to be rejected
*/
rejectionReason: OrderRejectionReason | null;
/**
* RFC3339Nano formatted date and time for when the order was created (timestamp)
*/
createdAt: string;
/**
* Total number of contracts that may be bought or sold (immutable) (uint64)
*/
size: string;
/**
* The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
*/
price: string;
/**
* The market the order is trading on (probably stored internally as a hash of the market details)
*/
market: OrderEvent_busEvents_event_Order_market | null;
}
export type OrderEvent_busEvents_event = OrderEvent_busEvents_event_TimeUpdate | OrderEvent_busEvents_event_Order;
export interface OrderEvent_busEvents {
__typename: "BusEvent";
/**
* the id for this event
*/
eventId: string;
/**
* the block hash
*/
block: string;
/**
* the type of event we're dealing with
*/
type: BusEventType;
/**
* the payload - the wrapped event
*/
event: OrderEvent_busEvents_event;
}
export interface OrderEvent {
/**
* Subscribe to event data from the event bus
*/
busEvents: OrderEvent_busEvents[] | null;
}
export interface OrderEventVariables {
partyId: string;
}

View File

@ -105,12 +105,7 @@ it('Form validation', async () => {
await screen.findByText('Amount is above permitted maximum') await screen.findByText('Amount is above permitted maximum')
).toBeInTheDocument(); ).toBeInTheDocument();
rerender( rerender(<DepositForm {...props} limits={{ max: new BigNumber(100) }} />);
<DepositForm
{...props}
limits={{ min: new BigNumber(0), max: new BigNumber(100) }}
/>
);
const amountMoreThanAllowance = '31'; const amountMoreThanAllowance = '31';
fireEvent.change(screen.getByLabelText('Amount'), { fireEvent.change(screen.getByLabelText('Amount'), {
@ -130,16 +125,11 @@ it('Form validation', async () => {
expect(await screen.findByText('Value is below minimum')).toBeInTheDocument(); expect(await screen.findByText('Value is below minimum')).toBeInTheDocument();
rerender( const amountLessThanZero = '-0.00001';
<DepositForm
{...props}
limits={{ max: new BigNumber(20), min: new BigNumber(10) }}
/>
);
const amountLessThanLimit = '5';
fireEvent.change(screen.getByLabelText('Amount'), { fireEvent.change(screen.getByLabelText('Amount'), {
target: { value: amountLessThanLimit }, target: { value: amountLessThanZero },
}); });
expect(await screen.findByText('Value is below minimum')).toBeInTheDocument(); expect(await screen.findByText('Value is below minimum')).toBeInTheDocument();
}); });
@ -190,10 +180,7 @@ it('Deposit', async () => {
/> />
); );
// Check deposit limits are displayed // Check deposit limit is displayed
expect(
screen.getByText('Minimum', { selector: 'th' }).nextElementSibling
).toHaveTextContent(limits.min.toString());
expect( expect(
screen.getByText('Maximum', { selector: 'th' }).nextElementSibling screen.getByText('Maximum', { selector: 'th' }).nextElementSibling
).toHaveTextContent(limits.max.toString()); ).toHaveTextContent(limits.max.toString());

View File

@ -1,5 +1,4 @@
import { import {
addDecimal,
removeDecimal, removeDecimal,
t, t,
ethereumAddress, ethereumAddress,
@ -7,6 +6,7 @@ import {
vegaPublicKey, vegaPublicKey,
minSafe, minSafe,
maxSafe, maxSafe,
addDecimal,
} from '@vegaprotocol/react-helpers'; } from '@vegaprotocol/react-helpers';
import { import {
Button, Button,
@ -46,7 +46,6 @@ export interface DepositFormProps {
}) => Promise<void>; }) => Promise<void>;
requestFaucet: () => Promise<void>; requestFaucet: () => Promise<void>;
limits: { limits: {
min: BigNumber;
max: BigNumber; max: BigNumber;
} | null; } | null;
allowance: BigNumber | undefined; allowance: BigNumber | undefined;
@ -97,19 +96,6 @@ export const DepositForm = ({
const assetId = useWatch({ name: 'asset', control }); const assetId = useWatch({ name: 'asset', control });
const amount = useWatch({ name: 'amount', control }); const amount = useWatch({ name: 'amount', control });
const min = useMemo(() => {
// Min viable amount given asset decimals EG for WEI 0.000000000000000001
const minViableAmount = selectedAsset
? new BigNumber(addDecimal('1', selectedAsset.decimals))
: new BigNumber(0);
const min = limits
? BigNumber.maximum(minViableAmount, limits.min)
: minViableAmount;
return min;
}, [limits, selectedAsset]);
const max = useMemo(() => { const max = useMemo(() => {
const maxApproved = allowance ? allowance : new BigNumber(Infinity); const maxApproved = allowance ? allowance : new BigNumber(Infinity);
const maxAvailable = available ? available : new BigNumber(Infinity); const maxAvailable = available ? available : new BigNumber(Infinity);
@ -127,6 +113,15 @@ export const DepositForm = ({
}; };
}, [limits, allowance, available]); }, [limits, allowance, available]);
const min = useMemo(() => {
// Min viable amount given asset decimals EG for WEI 0.000000000000000001
const minViableAmount = selectedAsset
? new BigNumber(addDecimal('1', selectedAsset.decimals))
: new BigNumber(0);
return minViableAmount;
}, [selectedAsset]);
useEffect(() => { useEffect(() => {
onSelectAsset(assetId); onSelectAsset(assetId);
}, [assetId, onSelectAsset]); }, [assetId, onSelectAsset]);
@ -207,7 +202,7 @@ export const DepositForm = ({
{...register('amount', { {...register('amount', {
validate: { validate: {
required, required,
minSafe: (value) => minSafe(min)(value), minSafe: (value) => minSafe(new BigNumber(min))(value),
maxSafe: (v) => { maxSafe: (v) => {
const value = new BigNumber(v); const value = new BigNumber(v);
if (value.isGreaterThan(max.approved)) { if (value.isGreaterThan(max.approved)) {

View File

@ -3,13 +3,11 @@ import type BigNumber from 'bignumber.js';
interface DepositLimitsProps { interface DepositLimitsProps {
limits: { limits: {
min: BigNumber;
max: BigNumber; max: BigNumber;
}; };
} }
export const DepositLimits = ({ limits }: DepositLimitsProps) => { export const DepositLimits = ({ limits }: DepositLimitsProps) => {
const minLimit = limits.min.toString();
const maxLimit = limits.max.isEqualTo(Infinity) const maxLimit = limits.max.isEqualTo(Infinity)
? t('No limit') ? t('No limit')
: limits.max.toString(); : limits.max.toString();
@ -18,10 +16,6 @@ export const DepositLimits = ({ limits }: DepositLimitsProps) => {
<p className="text-ui font-bold">{t('Temporary deposit limits')}</p> <p className="text-ui font-bold">{t('Temporary deposit limits')}</p>
<table className="w-full text-ui"> <table className="w-full text-ui">
<tbody> <tbody>
<tr>
<th className="text-left font-normal">{t('Minimum')}</th>
<td className="text-right">{minLimit}</td>
</tr>
<tr> <tr>
<th className="text-left font-normal">{t('Maximum')}</th> <th className="text-left font-normal">{t('Maximum')}</th>
<td className="text-right">{maxLimit}</td> <td className="text-right">{maxLimit}</td>

View File

@ -5,16 +5,13 @@ import BigNumber from 'bignumber.js';
import { addDecimal } from '@vegaprotocol/react-helpers'; import { addDecimal } from '@vegaprotocol/react-helpers';
export const useGetDepositLimits = (asset?: Asset, decimals?: number) => { export const useGetDepositLimits = (asset?: Asset, decimals?: number) => {
const contract = useBridgeContract(); const contract = useBridgeContract(true);
const getLimits = useCallback(async () => { const getLimits = useCallback(async () => {
if (!contract || !asset || asset.source.__typename !== 'ERC20') { if (!contract || !asset || asset.source.__typename !== 'ERC20') {
return; return;
} }
return Promise.all([ return contract.getDepositMaximum(asset.source.contractAddress);
contract.getDepositMinimum(asset.source.contractAddress),
contract.getDepositMaximum(asset.source.contractAddress),
]);
}, [asset, contract]); }, [asset, contract]);
const { const {
@ -23,11 +20,9 @@ export const useGetDepositLimits = (asset?: Asset, decimals?: number) => {
if (!data || !decimals) return null; if (!data || !decimals) return null;
const min = new BigNumber(addDecimal(data[0].toString(), decimals)); const max = new BigNumber(addDecimal(data.toString(), decimals));
const max = new BigNumber(addDecimal(data[1].toString(), decimals));
return { return {
min,
max: max.isEqualTo(0) ? new BigNumber(Infinity) : max, max: max.isEqualTo(0) ? new BigNumber(Infinity) : max,
}; };
}; };

View File

@ -29,7 +29,7 @@ const DEPOSIT_EVENT_SUB = gql`
export const useSubmitDeposit = () => { export const useSubmitDeposit = () => {
const { config } = useEthereumConfig(); const { config } = useEthereumConfig();
const contract = useBridgeContract(); const contract = useBridgeContract(true);
const [confirmationEvent, setConfirmationEvent] = const [confirmationEvent, setConfirmationEvent] =
useState<DepositEvent_busEvents_event_Deposit | null>(null); useState<DepositEvent_busEvents_event_Deposit | null>(null);
// Store public key from contract arguments for use in the subscription, // Store public key from contract arguments for use in the subscription,

View File

@ -86,7 +86,7 @@ export interface MarketDepthSubscription_marketDepthUpdate {
*/ */
buy: MarketDepthSubscription_marketDepthUpdate_buy[] | null; buy: MarketDepthSubscription_marketDepthUpdate_buy[] | null;
/** /**
* Sequence number for the current snapshot of the market depth * Sequence number for the current snapshot of the market depth. It is always increasing but not monotonic.
*/ */
sequenceNumber: string; sequenceNumber: string;
} }

View File

@ -173,8 +173,16 @@
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" }, {
{ "internalType": "uint256", "name": "amount", "type": "uint256" }, "internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{ {
"internalType": "bytes32", "internalType": "bytes32",
"name": "vega_public_key", "name": "vega_public_key",
@ -188,62 +196,134 @@
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "bytes32", "name": "vega_asset_id", "type": "bytes32" } {
"internalType": "bytes32",
"name": "vega_asset_id",
"type": "bytes32"
}
], ],
"name": "get_asset_source", "name": "get_asset_source",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }], "outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" } {
"internalType": "address",
"name": "asset_source",
"type": "address"
}
], ],
"name": "get_deposit_maximum", "name": "get_deposit_maximum",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" } {
"internalType": "address",
"name": "asset_source",
"type": "address"
}
], ],
"name": "get_deposit_minimum", "name": "get_deposit_minimum",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{ {
"inputs": [], "inputs": [],
"name": "get_multisig_control_address", "name": "get_multisig_control_address",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }], "outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" } {
"internalType": "address",
"name": "asset_source",
"type": "address"
}
], ],
"name": "get_vega_asset_id", "name": "get_vega_asset_id",
"outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], "outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" } {
"internalType": "address",
"name": "asset_source",
"type": "address"
}
], ],
"name": "is_asset_listed", "name": "is_asset_listed",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" }, {
{ "internalType": "bytes32", "name": "vega_asset_id", "type": "bytes32" }, "internalType": "address",
{ "internalType": "uint256", "name": "nonce", "type": "uint256" }, "name": "asset_source",
{ "internalType": "bytes", "name": "signatures", "type": "bytes" } "type": "address"
},
{
"internalType": "bytes32",
"name": "vega_asset_id",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
], ],
"name": "list_asset", "name": "list_asset",
"outputs": [], "outputs": [],
@ -252,9 +332,21 @@
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" }, {
{ "internalType": "uint256", "name": "nonce", "type": "uint256" }, "internalType": "address",
{ "internalType": "bytes", "name": "signatures", "type": "bytes" } "name": "asset_source",
"type": "address"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
], ],
"name": "remove_asset", "name": "remove_asset",
"outputs": [], "outputs": [],
@ -263,14 +355,26 @@
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" }, {
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "maximum_amount", "name": "maximum_amount",
"type": "uint256" "type": "uint256"
}, },
{ "internalType": "uint256", "name": "nonce", "type": "uint256" }, {
{ "internalType": "bytes", "name": "signatures", "type": "bytes" } "internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
], ],
"name": "set_deposit_maximum", "name": "set_deposit_maximum",
"outputs": [], "outputs": [],
@ -279,14 +383,26 @@
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" }, {
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "minimum_amount", "name": "minimum_amount",
"type": "uint256" "type": "uint256"
}, },
{ "internalType": "uint256", "name": "nonce", "type": "uint256" }, {
{ "internalType": "bytes", "name": "signatures", "type": "bytes" } "internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
], ],
"name": "set_deposit_minimum", "name": "set_deposit_minimum",
"outputs": [], "outputs": [],
@ -295,11 +411,31 @@
}, },
{ {
"inputs": [ "inputs": [
{ "internalType": "address", "name": "asset_source", "type": "address" }, {
{ "internalType": "uint256", "name": "amount", "type": "uint256" }, "internalType": "address",
{ "internalType": "address", "name": "target", "type": "address" }, "name": "asset_source",
{ "internalType": "uint256", "name": "nonce", "type": "uint256" }, "type": "address"
{ "internalType": "bytes", "name": "signatures", "type": "bytes" } },
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
], ],
"name": "withdraw_asset", "name": "withdraw_asset",
"outputs": [], "outputs": [],

View File

@ -0,0 +1,589 @@
[
{
"inputs": [
{
"internalType": "address payable",
"name": "erc20_asset_pool",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "user_address",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "vega_public_key",
"type": "bytes32"
}
],
"name": "Asset_Deposited",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "lifetime_limit",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "withdraw_threshold",
"type": "uint256"
}
],
"name": "Asset_Limits_Updated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "vega_asset_id",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
}
],
"name": "Asset_Listed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
}
],
"name": "Asset_Removed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "user_address",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
}
],
"name": "Asset_Withdrawn",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "Bridge_Resumed",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "Bridge_Stopped",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "withdraw_delay",
"type": "uint256"
}
],
"name": "Bridge_Withdraw_Delay_Set",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "depositor",
"type": "address"
}
],
"name": "Depositor_Exempted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "depositor",
"type": "address"
}
],
"name": "Depositor_Exemption_Revoked",
"type": "event"
},
{
"inputs": [],
"name": "default_withdraw_delay",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "vega_public_key",
"type": "bytes32"
}
],
"name": "deposit_asset",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "erc20_asset_pool_address",
"outputs": [
{
"internalType": "address payable",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "exempt_depositor",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset_source",
"type": "address"
}
],
"name": "get_asset_deposit_lifetime_limit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "vega_asset_id",
"type": "bytes32"
}
],
"name": "get_asset_source",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "get_multisig_control_address",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset_source",
"type": "address"
}
],
"name": "get_vega_asset_id",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset_source",
"type": "address"
}
],
"name": "get_withdraw_threshold",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
],
"name": "global_resume",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
],
"name": "global_stop",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset_source",
"type": "address"
}
],
"name": "is_asset_listed",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "depositor",
"type": "address"
}
],
"name": "is_exempt_depositor",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "is_stopped",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"internalType": "bytes32",
"name": "vega_asset_id",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "lifetime_limit",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "withdraw_threshold",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
],
"name": "list_asset",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
],
"name": "remove_asset",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "revoke_exempt_depositor",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"internalType": "uint256",
"name": "lifetime_limit",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "threshold",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
],
"name": "set_asset_limits",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "delay",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
],
"name": "set_withdraw_delay",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "asset_source",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "uint256",
"name": "creation",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "signatures",
"type": "bytes"
}
],
"name": "withdraw_asset",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

View File

@ -0,0 +1,51 @@
import type { BigNumber } from 'ethers';
import { ethers } from 'ethers';
import abi from '../abis/erc20_bridge_new_abi.json';
export class CollateralBridgeNew {
public contract: ethers.Contract;
public isNewContract = true;
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_asset_deposit_lifetime_limit(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,
creation: string,
nonce: string,
signatures: string
) {
return this.contract.withdraw_asset(
assetSource,
amount,
target,
creation,
nonce,
signatures
);
}
}

View File

@ -4,6 +4,7 @@ import abi from '../abis/erc20_bridge_abi.json';
export class CollateralBridge { export class CollateralBridge {
public contract: ethers.Contract; public contract: ethers.Contract;
public isNewContract = false;
constructor( constructor(
address: string, address: string,

View File

@ -2,6 +2,7 @@ export * from './vega-web3-types';
export * from './claim'; export * from './claim';
export * from './collateral-bridge'; export * from './collateral-bridge';
export * from './collateral-bridge-new';
export * from './staking-bridge'; export * from './staking-bridge';
export * from './token-vesting'; export * from './token-vesting';
export * from './token'; export * from './token';

1
libs/types/.env Normal file
View File

@ -0,0 +1 @@
NX_VEGA_URL = "https://n04.d.vega.xyz/query"

View File

@ -1,9 +1,12 @@
import { CollateralBridge } from '@vegaprotocol/smart-contracts'; import {
CollateralBridge,
CollateralBridgeNew,
} from '@vegaprotocol/smart-contracts';
import { useWeb3React } from '@web3-react/core'; import { useWeb3React } from '@web3-react/core';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useEthereumConfig } from './use-ethereum-config'; import { useEthereumConfig } from './use-ethereum-config';
export const useBridgeContract = () => { export const useBridgeContract = (isNewContract: boolean) => {
const { provider } = useWeb3React(); const { provider } = useWeb3React();
const { config } = useEthereumConfig(); const { config } = useEthereumConfig();
@ -14,11 +17,16 @@ export const useBridgeContract = () => {
const signer = provider.getSigner(); const signer = provider.getSigner();
return new CollateralBridge( return isNewContract
config.collateral_bridge_contract.address, ? new CollateralBridgeNew(
signer || provider config.collateral_bridge_contract.address,
); signer || provider
}, [provider, config]); )
: new CollateralBridge(
config.collateral_bridge_contract.address,
signer || provider
);
}, [provider, config, isNewContract]);
return contract; return contract;
}; };

View File

@ -0,0 +1,52 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: Erc20ApprovalNew
// ====================================================
export interface Erc20ApprovalNew_erc20WithdrawalApproval {
__typename: "Erc20WithdrawalApproval";
/**
* The source asset in the ethereum network
*/
assetSource: string;
/**
* The amount to be withdrawn
*/
amount: string;
/**
* The nonce to be used in the request
*/
nonce: string;
/**
* Signature aggregate from the nodes, in the following format:
* 0x + sig1 + sig2 + ... + sigN
*/
signatures: string;
/**
* The target address which will receive the funds
*/
targetAddress: string;
/**
* Timestamp in seconds for expiry of the approval
*/
expiry: string;
/**
* Timestamp at which the withdrawal was created
*/
creation: string;
}
export interface Erc20ApprovalNew {
/**
* find an erc20 withdrawal approval using its withdrawal id
*/
erc20WithdrawalApproval: Erc20ApprovalNew_erc20WithdrawalApproval | null;
}
export interface Erc20ApprovalNewVariables {
withdrawalId: string;
}

View File

@ -12,3 +12,17 @@ export const ERC20_APPROVAL_QUERY = gql`
} }
} }
`; `;
export const ERC20_APPROVAL_QUERY_NEW = gql`
query Erc20ApprovalNew($withdrawalId: ID!) {
erc20WithdrawalApproval(withdrawalId: $withdrawalId) {
assetSource
amount
nonce
signatures
targetAddress
expiry
creation
}
}
`;

View File

@ -4,13 +4,11 @@ import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useCompleteWithdraw } from './use-complete-withdraw'; import { useCompleteWithdraw } from './use-complete-withdraw';
import type { import type { Erc20Approval } from './__generated__/Erc20Approval';
Erc20Approval, import { ERC20_APPROVAL_QUERY_NEW } from './queries';
Erc20Approval_erc20WithdrawalApproval,
} from './__generated__/Erc20Approval';
import { ERC20_APPROVAL_QUERY } from './queries';
import * as web3 from '@vegaprotocol/web3'; import * as web3 from '@vegaprotocol/web3';
import * as sentry from '@sentry/nextjs'; import * as sentry from '@sentry/nextjs';
import type { Erc20ApprovalNew_erc20WithdrawalApproval } from './__generated__/Erc20ApprovalNew';
jest.mock('@vegaprotocol/web3', () => ({ jest.mock('@vegaprotocol/web3', () => ({
useBridgeContract: jest.fn(), useBridgeContract: jest.fn(),
@ -21,23 +19,24 @@ function setup(mocks?: MockedResponse[]) {
const wrapper = ({ children }: { children: ReactNode }) => ( const wrapper = ({ children }: { children: ReactNode }) => (
<MockedProvider mocks={mocks}>{children}</MockedProvider> <MockedProvider mocks={mocks}>{children}</MockedProvider>
); );
return renderHook(() => useCompleteWithdraw(), { wrapper }); return renderHook(() => useCompleteWithdraw(true), { wrapper });
} }
it('Should perform the Ethereum transaction with the fetched approval', async () => { it('Should perform the Ethereum transaction with the fetched approval', async () => {
const withdrawalId = 'withdrawal-id'; const withdrawalId = 'withdrawal-id';
const erc20WithdrawalApproval: Erc20Approval_erc20WithdrawalApproval = { const erc20WithdrawalApproval: Erc20ApprovalNew_erc20WithdrawalApproval = {
__typename: 'Erc20WithdrawalApproval', __typename: 'Erc20WithdrawalApproval',
assetSource: 'asset-source', assetSource: 'asset-source',
amount: '100', amount: '100',
nonce: '1', nonce: '1',
creation: '1',
signatures: 'signatures', signatures: 'signatures',
targetAddress: 'target-address', targetAddress: 'target-address',
expiry: 'expiry', expiry: 'expiry',
}; };
const mockERC20Approval: MockedResponse<Erc20Approval> = { const mockERC20Approval: MockedResponse<Erc20Approval> = {
request: { request: {
query: ERC20_APPROVAL_QUERY, query: ERC20_APPROVAL_QUERY_NEW,
variables: { withdrawalId }, variables: { withdrawalId },
}, },
result: { result: {
@ -66,7 +65,7 @@ it('Captures an error if the erc20Withdrawal is not found', async () => {
const withdrawalId = 'withdrawal-id'; const withdrawalId = 'withdrawal-id';
const mockERC20Approval: MockedResponse<Erc20Approval> = { const mockERC20Approval: MockedResponse<Erc20Approval> = {
request: { request: {
query: ERC20_APPROVAL_QUERY, query: ERC20_APPROVAL_QUERY_NEW,
variables: { withdrawalId }, variables: { withdrawalId },
}, },
result: { result: {
@ -97,7 +96,7 @@ it('Captures an error if erc20 approval query fails', async () => {
const withdrawalId = 'withdrawal-id'; const withdrawalId = 'withdrawal-id';
const mockERC20Approval: MockedResponse<Erc20Approval> = { const mockERC20Approval: MockedResponse<Erc20Approval> = {
request: { request: {
query: ERC20_APPROVAL_QUERY, query: ERC20_APPROVAL_QUERY_NEW,
variables: { withdrawalId }, variables: { withdrawalId },
}, },
error: new Error('query failed'), error: new Error('query failed'),

View File

@ -1,12 +1,17 @@
import { gql, useApolloClient } from '@apollo/client'; import { gql, useApolloClient } from '@apollo/client';
import { captureException } from '@sentry/nextjs'; import { captureException } from '@sentry/nextjs';
import type {
CollateralBridge,
CollateralBridgeNew,
} from '@vegaprotocol/smart-contracts';
import { useBridgeContract, useEthereumTransaction } from '@vegaprotocol/web3'; import { useBridgeContract, useEthereumTransaction } from '@vegaprotocol/web3';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { ERC20_APPROVAL_QUERY } from './queries'; import { ERC20_APPROVAL_QUERY, ERC20_APPROVAL_QUERY_NEW } from './queries';
import type { import type {
Erc20Approval, Erc20Approval,
Erc20ApprovalVariables, Erc20ApprovalVariables,
} from './__generated__/Erc20Approval'; } from './__generated__/Erc20Approval';
import type { Erc20ApprovalNew } from './__generated__/Erc20ApprovalNew';
import type { PendingWithdrawal } from './__generated__/PendingWithdrawal'; import type { PendingWithdrawal } from './__generated__/PendingWithdrawal';
export const PENDING_WITHDRAWAL_FRAGMMENT = gql` export const PENDING_WITHDRAWAL_FRAGMMENT = gql`
@ -16,6 +21,15 @@ export const PENDING_WITHDRAWAL_FRAGMMENT = gql`
} }
`; `;
export interface NewWithdrawTransactionArgs {
assetSource: string;
amount: string;
nonce: string;
signatures: string;
targetAddress: string;
creation: string;
}
export interface WithdrawTransactionArgs { export interface WithdrawTransactionArgs {
assetSource: string; assetSource: string;
amount: string; amount: string;
@ -24,30 +38,48 @@ export interface WithdrawTransactionArgs {
targetAddress: string; targetAddress: string;
} }
export const useCompleteWithdraw = () => { export const useCompleteWithdraw = (isNewContract: boolean) => {
const { query, cache } = useApolloClient(); const { query, cache } = useApolloClient();
const contract = useBridgeContract(); const contract = useBridgeContract(isNewContract);
const [id, setId] = useState(''); const [id, setId] = useState('');
const { transaction, perform } = const { transaction, perform } = useEthereumTransaction<
useEthereumTransaction<WithdrawTransactionArgs>((args) => { WithdrawTransactionArgs | NewWithdrawTransactionArgs
if (!contract) { >((args) => {
return null; if (!contract) {
} return null;
return contract.withdrawAsset( }
if (contract.isNewContract) {
const withdrawalData = args as NewWithdrawTransactionArgs;
return (contract as CollateralBridgeNew).withdrawAsset(
withdrawalData.assetSource,
withdrawalData.amount,
withdrawalData.targetAddress,
withdrawalData.creation,
withdrawalData.nonce,
withdrawalData.signatures
);
} else {
return (contract as CollateralBridge).withdrawAsset(
args.assetSource, args.assetSource,
args.amount, args.amount,
args.targetAddress, args.targetAddress,
args.nonce, args.nonce,
args.signatures args.signatures
); );
}); }
});
const submit = useCallback( const submit = useCallback(
async (withdrawalId: string) => { async (withdrawalId: string) => {
setId(withdrawalId); setId(withdrawalId);
try { try {
const res = await query<Erc20Approval, Erc20ApprovalVariables>({ const res = await query<
query: ERC20_APPROVAL_QUERY, Erc20Approval | Erc20ApprovalNew,
Erc20ApprovalVariables
>({
query: isNewContract
? ERC20_APPROVAL_QUERY_NEW
: ERC20_APPROVAL_QUERY,
variables: { withdrawalId }, variables: { withdrawalId },
}); });
@ -60,7 +92,7 @@ export const useCompleteWithdraw = () => {
captureException(err); captureException(err);
} }
}, },
[query, perform] [query, isNewContract, perform]
); );
useEffect(() => { useEffect(() => {

View File

@ -2,12 +2,12 @@ import { act, renderHook } from '@testing-library/react-hooks';
import type { MockedResponse } from '@apollo/client/testing'; import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import type { Erc20Approval } from './__generated__/Erc20Approval'; import { ERC20_APPROVAL_QUERY_NEW } from './queries';
import { ERC20_APPROVAL_QUERY } from './queries';
import * as web3 from '@vegaprotocol/web3'; import * as web3 from '@vegaprotocol/web3';
import * as wallet from '@vegaprotocol/wallet'; import * as wallet from '@vegaprotocol/wallet';
import type { WithdrawalFields } from './use-withdraw'; import type { WithdrawalFields } from './use-withdraw';
import { useWithdraw } from './use-withdraw'; import { useWithdraw } from './use-withdraw';
import type { Erc20ApprovalNew } from './__generated__/Erc20ApprovalNew';
jest.mock('@vegaprotocol/web3', () => ({ jest.mock('@vegaprotocol/web3', () => ({
useBridgeContract: jest.fn(), useBridgeContract: jest.fn(),
@ -27,7 +27,7 @@ function setup(mocks?: MockedResponse[], cancelled = false) {
const wrapper = ({ children }: { children: ReactNode }) => ( const wrapper = ({ children }: { children: ReactNode }) => (
<MockedProvider mocks={mocks}>{children}</MockedProvider> <MockedProvider mocks={mocks}>{children}</MockedProvider>
); );
return renderHook(() => useWithdraw(cancelled), { wrapper }); return renderHook(() => useWithdraw(cancelled, true), { wrapper });
} }
const signature = const signature =
@ -49,7 +49,7 @@ let mockPerform: jest.Mock;
let mockEthReset: jest.Mock; let mockEthReset: jest.Mock;
let mockVegaReset: jest.Mock; let mockVegaReset: jest.Mock;
let withdrawalInput: WithdrawalFields; let withdrawalInput: WithdrawalFields;
let mockERC20Approval: MockedResponse<Erc20Approval>; let mockERC20Approval: MockedResponse<Erc20ApprovalNew>;
beforeEach(() => { beforeEach(() => {
pubkey = 'pubkey'; pubkey = 'pubkey';
@ -82,7 +82,7 @@ beforeEach(() => {
}; };
mockERC20Approval = { mockERC20Approval = {
request: { request: {
query: ERC20_APPROVAL_QUERY, query: ERC20_APPROVAL_QUERY_NEW,
variables: { withdrawalId: derivedWithdrawalId }, variables: { withdrawalId: derivedWithdrawalId },
}, },
result: { result: {
@ -95,6 +95,7 @@ beforeEach(() => {
signatures: 'signatures', signatures: 'signatures',
targetAddress: 'targetAddress', targetAddress: 'targetAddress',
expiry: 'expiry', expiry: 'expiry',
creation: '1',
}, },
}, },
}, },

View File

@ -3,13 +3,21 @@ import { determineId } from '@vegaprotocol/react-helpers';
import { useBridgeContract, useEthereumTransaction } from '@vegaprotocol/web3'; import { useBridgeContract, useEthereumTransaction } from '@vegaprotocol/web3';
import { useVegaTransaction, useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaTransaction, useVegaWallet } from '@vegaprotocol/wallet';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { ERC20_APPROVAL_QUERY } from './queries'; import { ERC20_APPROVAL_QUERY, ERC20_APPROVAL_QUERY_NEW } from './queries';
import type { WithdrawTransactionArgs } from './use-complete-withdraw'; import type {
NewWithdrawTransactionArgs,
WithdrawTransactionArgs,
} from './use-complete-withdraw';
import type { import type {
Erc20Approval, Erc20Approval,
Erc20ApprovalVariables, Erc20ApprovalVariables,
Erc20Approval_erc20WithdrawalApproval, Erc20Approval_erc20WithdrawalApproval,
} from './__generated__/Erc20Approval'; } from './__generated__/Erc20Approval';
import type {
CollateralBridge,
CollateralBridgeNew,
} from '@vegaprotocol/smart-contracts';
import type { Erc20ApprovalNew } from './__generated__/Erc20ApprovalNew';
export interface WithdrawalFields { export interface WithdrawalFields {
amount: string; amount: string;
@ -17,12 +25,12 @@ export interface WithdrawalFields {
receiverAddress: string; receiverAddress: string;
} }
export const useWithdraw = (cancelled: boolean) => { export const useWithdraw = (cancelled: boolean, isNewContract: boolean) => {
const [withdrawalId, setWithdrawalId] = useState<string | null>(null); const [withdrawalId, setWithdrawalId] = useState<string | null>(null);
const [approval, setApproval] = const [approval, setApproval] =
useState<Erc20Approval_erc20WithdrawalApproval | null>(null); useState<Erc20Approval_erc20WithdrawalApproval | null>(null);
const contract = useBridgeContract(); const contract = useBridgeContract(isNewContract);
const { keypair } = useVegaWallet(); const { keypair } = useVegaWallet();
const { const {
transaction: vegaTx, transaction: vegaTx,
@ -38,23 +46,35 @@ export const useWithdraw = (cancelled: boolean) => {
if (!contract) { if (!contract) {
return null; return null;
} }
return contract.withdrawAsset( if (contract.isNewContract) {
args.assetSource, const withdrawalArguments = args as NewWithdrawTransactionArgs;
args.amount, return (contract as CollateralBridgeNew).withdrawAsset(
args.targetAddress, withdrawalArguments.assetSource,
args.nonce, withdrawalArguments.amount,
args.signatures withdrawalArguments.targetAddress,
); withdrawalArguments.creation,
withdrawalArguments.nonce,
withdrawalArguments.signatures
);
} else {
return (contract as CollateralBridge).withdrawAsset(
args.assetSource,
args.amount,
args.targetAddress,
args.nonce,
args.signatures
);
}
}); });
const { data, stopPolling } = useQuery<Erc20Approval, Erc20ApprovalVariables>( const { data, stopPolling } = useQuery<
ERC20_APPROVAL_QUERY, Erc20Approval | Erc20ApprovalNew,
{ Erc20ApprovalVariables
variables: { withdrawalId: withdrawalId || '' }, >(isNewContract ? ERC20_APPROVAL_QUERY_NEW : ERC20_APPROVAL_QUERY, {
skip: !withdrawalId, variables: { withdrawalId: withdrawalId || '' },
pollInterval: 1000, skip: !withdrawalId,
} pollInterval: 1000,
); });
const submit = useCallback( const submit = useCallback(
async (withdrawal: WithdrawalFields) => { async (withdrawal: WithdrawalFields) => {

View File

@ -27,6 +27,7 @@ beforeEach(() => {
assets: [generateAsset()], assets: [generateAsset()],
accounts: [generateAccount()], accounts: [generateAccount()],
initialAssetId: undefined, initialAssetId: undefined,
isNewContract: true,
}; };
mockSubmit = jest.fn(); mockSubmit = jest.fn();
mockReset = jest.fn(); mockReset = jest.fn();

View File

@ -15,12 +15,14 @@ export interface WithdrawManagerProps {
assets: Asset[]; assets: Asset[];
accounts: Account[]; accounts: Account[];
initialAssetId?: string; initialAssetId?: string;
isNewContract: boolean;
} }
export const WithdrawManager = ({ export const WithdrawManager = ({
assets, assets,
accounts, accounts,
initialAssetId, initialAssetId,
isNewContract,
}: WithdrawManagerProps) => { }: WithdrawManagerProps) => {
const dialogDismissed = useRef(false); const dialogDismissed = useRef(false);
const [dialogOpen, setDialogOpen] = useState(false); const [dialogOpen, setDialogOpen] = useState(false);
@ -28,7 +30,8 @@ export const WithdrawManager = ({
const { account: ethereumAccount } = useWeb3React(); const { account: ethereumAccount } = useWeb3React();
const { ethTx, vegaTx, approval, submit, reset } = useWithdraw( const { ethTx, vegaTx, approval, submit, reset } = useWithdraw(
dialogDismissed.current dialogDismissed.current,
isNewContract
); );
// Find the asset object from the select box // Find the asset object from the select box

View File

@ -22,7 +22,7 @@ export interface WithdrawalsTableProps {
export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => { export const WithdrawalsTable = ({ withdrawals }: WithdrawalsTableProps) => {
const { ETHERSCAN_URL } = useEnvironment(); const { ETHERSCAN_URL } = useEnvironment();
const { transaction, submit } = useCompleteWithdraw(); const { transaction, submit } = useCompleteWithdraw(true);
return ( return (
<> <>