chore(#315): Convert token to use wallet lib
* delete token version of vega wallet serivce * update use-user-vote to use new wallet service * remove typo * add further types for transaction submissions, add assets to withdraw page query * update api client package to get generated types, adjust render logic of withdrawals page * fix withdrawals list rendering * update determine id function to not use nodejs buffer * update service api client so it accepts new tx types * remove stray logs and formatting * make filtering erc20 assets the responsibility of the withdraw/deposit lib and not the app * remove sha3 dep and use js-sha3 and ethers to determine ids * use hook for fetching withdrawals form lib, add type policy to ensure withdrawal state is updated correctly * fix: markets page feature
This commit is contained in:
parent
b10432fdf3
commit
d8bf887245
@ -147,6 +147,7 @@ const IconLine = ({ inverted }: { inverted: boolean }) => (
|
||||
|
||||
const NavDrawer = ({ inverted }: { inverted: boolean }) => {
|
||||
const { appState, appDispatch } = useAppState();
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
|
@ -25,7 +25,6 @@ export const TransactionComplete = ({
|
||||
<p>
|
||||
<EtherscanLink tx={hash} />
|
||||
</p>
|
||||
LINK
|
||||
{footer && <p data-testid="transaction-complete-footer">{footer}</p>}
|
||||
</Callout>
|
||||
);
|
||||
|
@ -1,143 +0,0 @@
|
||||
import React from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { gql, useApolloClient } from '@apollo/client';
|
||||
import { sigToId } from '../lib/sig-to-id';
|
||||
import { vegaWalletService } from '../lib/vega-wallet/vega-wallet-service';
|
||||
|
||||
import type { WithdrawSubmissionInput } from '../lib/vega-wallet/vega-wallet-service';
|
||||
import type {
|
||||
WithdrawalPoll,
|
||||
WithdrawalPollVariables,
|
||||
} from './__generated__/WithdrawalPoll';
|
||||
|
||||
export enum Status {
|
||||
Idle,
|
||||
Submitted,
|
||||
Pending,
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
|
||||
type Submit = (
|
||||
amount: string,
|
||||
asset: string,
|
||||
receiverAddress: string
|
||||
) => Promise<void>;
|
||||
|
||||
const WITHDRAWAL_QUERY = gql`
|
||||
query WithdrawalPoll($partyId: ID!) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
withdrawals {
|
||||
id
|
||||
amount
|
||||
status
|
||||
asset {
|
||||
id
|
||||
symbol
|
||||
decimals
|
||||
}
|
||||
createdTimestamp
|
||||
withdrawnTimestamp
|
||||
txHash
|
||||
details {
|
||||
... on Erc20WithdrawalDetails {
|
||||
receiverAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function useCreateWithdrawal(pubKey: string): [Status, Submit] {
|
||||
const mountedRef = React.useRef(true);
|
||||
const client = useApolloClient();
|
||||
const [status, setStatus] = React.useState(Status.Idle);
|
||||
const [id, setId] = React.useState('');
|
||||
|
||||
const safeSetStatus = (status: Status) => {
|
||||
if (mountedRef.current) {
|
||||
setStatus(status);
|
||||
}
|
||||
};
|
||||
|
||||
const submit = React.useCallback(
|
||||
async (amount: string, asset: string, receiverAddress: string) => {
|
||||
const command: WithdrawSubmissionInput = {
|
||||
pubKey,
|
||||
withdrawSubmission: {
|
||||
amount,
|
||||
asset,
|
||||
ext: {
|
||||
erc20: {
|
||||
receiverAddress,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
safeSetStatus(Status.Submitted);
|
||||
|
||||
try {
|
||||
const [err, res] = await vegaWalletService.commandSync(command);
|
||||
|
||||
if (err || !res) {
|
||||
safeSetStatus(Status.Failure);
|
||||
} else {
|
||||
const id = sigToId(res.signature.value);
|
||||
setId(id);
|
||||
// Now await subscription
|
||||
}
|
||||
|
||||
safeSetStatus(Status.Pending);
|
||||
} catch (err) {
|
||||
safeSetStatus(Status.Failure);
|
||||
Sentry.captureException(err);
|
||||
}
|
||||
},
|
||||
[pubKey]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
let interval: ReturnType<typeof setInterval>;
|
||||
if (status === Status.Pending) {
|
||||
interval = setInterval(async () => {
|
||||
try {
|
||||
const { data } = await client.query<
|
||||
WithdrawalPoll,
|
||||
WithdrawalPollVariables
|
||||
>({
|
||||
fetchPolicy: 'network-only',
|
||||
query: WITHDRAWAL_QUERY,
|
||||
variables: { partyId: pubKey },
|
||||
});
|
||||
|
||||
// find matching withdrawals
|
||||
const withdrawal = data?.party?.withdrawals?.find((e) => {
|
||||
return e.id === id;
|
||||
});
|
||||
if (withdrawal) {
|
||||
safeSetStatus(Status.Success);
|
||||
clearInterval(interval);
|
||||
}
|
||||
} catch (err) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [client, id, pubKey, status]);
|
||||
|
||||
React.useEffect(() => {
|
||||
mountedRef.current = true;
|
||||
return () => {
|
||||
mountedRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return [status, submit];
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
import React from 'react';
|
||||
import { gql, useApolloClient } from '@apollo/client';
|
||||
|
||||
import type {
|
||||
Erc20ApprovalPoll,
|
||||
Erc20ApprovalPoll_erc20WithdrawalApproval,
|
||||
Erc20ApprovalPollVariables,
|
||||
} from './__generated__/Erc20ApprovalPoll';
|
||||
|
||||
const ERC20_APPROVAL_QUERY = gql`
|
||||
query Erc20ApprovalPoll($withdrawalId: ID!) {
|
||||
erc20WithdrawalApproval(withdrawalId: $withdrawalId) {
|
||||
assetSource
|
||||
amount
|
||||
nonce
|
||||
signatures
|
||||
targetAddress
|
||||
expiry
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const usePollERC20Approval = (withdrawalId: string) => {
|
||||
const mountedRef = React.useRef(true);
|
||||
const client = useApolloClient();
|
||||
const [erc20Approval, setErc20Approval] =
|
||||
React.useState<Erc20ApprovalPoll_erc20WithdrawalApproval | null>(null);
|
||||
|
||||
const safeSetErc20Approval = (
|
||||
approval: Erc20ApprovalPoll_erc20WithdrawalApproval
|
||||
) => {
|
||||
if (mountedRef.current) {
|
||||
setErc20Approval(approval);
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const res = await client.query<
|
||||
Erc20ApprovalPoll,
|
||||
Erc20ApprovalPollVariables
|
||||
>({
|
||||
query: ERC20_APPROVAL_QUERY,
|
||||
variables: { withdrawalId },
|
||||
});
|
||||
|
||||
if (res.data.erc20WithdrawalApproval) {
|
||||
safeSetErc20Approval(res.data.erc20WithdrawalApproval);
|
||||
clearInterval(interval);
|
||||
}
|
||||
} catch (err) {
|
||||
// No op. If the erc20 withdrawal is not created yet it will error
|
||||
// but we will just want to poll until it is. There is no bus event for
|
||||
// erc20 approvals yet..
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
mountedRef.current = false;
|
||||
};
|
||||
}, [withdrawalId, client]);
|
||||
|
||||
return erc20Approval;
|
||||
};
|
@ -76,6 +76,7 @@
|
||||
"Loading": "Loading...",
|
||||
"Something went wrong": "Something went wrong",
|
||||
"Try again": "Try again",
|
||||
"Incomplete": "Incomplete",
|
||||
"Complete": "Complete",
|
||||
"View on Etherscan (opens in a new tab)": "View on Etherscan (opens in a new tab)",
|
||||
"Transaction in progress": "Transaction in progress",
|
||||
@ -437,7 +438,7 @@
|
||||
"withdrawalsText": "These withdrawals need to be completed with an Ethereum transaction.",
|
||||
"withdrawalsNone": "You don't have any pending withdrawals.",
|
||||
"withdrawalsCompleteButton": "Finish withdrawal",
|
||||
"withdrawalsPreparingButton": "Preparing withdrawal",
|
||||
"withdrawalTransaction": "Transaction ({{foreignChain}})",
|
||||
"signature": "Signature",
|
||||
"created": "Created",
|
||||
"toEthereum": "To (Ethereum)",
|
||||
|
@ -136,6 +136,13 @@ export function createClient() {
|
||||
},
|
||||
},
|
||||
},
|
||||
Withdrawal: {
|
||||
fields: {
|
||||
pendingOnForeignChain: {
|
||||
read: (isPending = false) => isPending,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -1,9 +0,0 @@
|
||||
import { sigToId } from './sig-to-id';
|
||||
|
||||
test('creating an id from a signature', () => {
|
||||
const sig =
|
||||
'3594b7f4e1c078aaa65b3efac57e1436da64d60c2961bf432dc62daa36f8b0b2602ed9c990ee46dd8ab5181e19c19b06587f080d22fc4543c1869970e678f20f';
|
||||
const expected =
|
||||
'9ebd2c424a9426bf5f38112878eb2f99bfdf5ec17b5782b944e6be8177084327';
|
||||
expect(sigToId(sig)).toEqual(expected);
|
||||
});
|
@ -1,18 +0,0 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { SHA3 } from 'sha3';
|
||||
|
||||
export const sigToId = (sig: string) => {
|
||||
// Prepend 0x
|
||||
if (sig.slice(0, 2) !== '0x') {
|
||||
sig = '0x' + sig;
|
||||
}
|
||||
|
||||
// Create the ID
|
||||
const hash = new SHA3(256);
|
||||
const bytes = ethers.utils.arrayify(sig);
|
||||
hash.update(Buffer.from(bytes));
|
||||
const id = ethers.utils.hexlify(hash.digest());
|
||||
|
||||
// Remove 0x as core doesn't keep them in the API
|
||||
return id.substring(2);
|
||||
};
|
@ -1,303 +0,0 @@
|
||||
import type { VoteValue } from '../../__generated__/globalTypes';
|
||||
import type { VegaKey } from '../../contexts/app-state/app-state-context';
|
||||
import type { VOTE_VALUE_MAP } from '../../routes/governance/components/vote-details';
|
||||
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import type { GenericErrorResponse } from './vega-wallet-types';
|
||||
|
||||
export const MINIMUM_WALLET_VERSION =
|
||||
process.env['NX_SUPPORTED_WALLET_VERSION'];
|
||||
|
||||
export const DEFAULT_WALLET_URL = 'http://localhost:1789';
|
||||
export const HOSTED_WALLET_URL = 'https://wallet.testnet.vega.xyz';
|
||||
const TOKEN_STORAGE_KEY = 'vega_wallet_token';
|
||||
const WALLET_URL_KEY = 'vega_wallet_url';
|
||||
const KEY_STORAGE_KEY = 'vega_wallet_key';
|
||||
|
||||
const Endpoints = {
|
||||
STATUS: 'status',
|
||||
TOKEN: 'auth/token',
|
||||
KEYS: 'keys',
|
||||
COMMAND: 'command/sync',
|
||||
VERSION: 'version',
|
||||
};
|
||||
|
||||
export const Errors = {
|
||||
NO_TOKEN: 'No token',
|
||||
SERVICE_UNAVAILABLE: 'Wallet service unavailable',
|
||||
SESSION_EXPIRED: 'Session expired',
|
||||
INVALID_CREDENTIALS: 'Invalid credentials',
|
||||
COMMAND_FAILED: 'Command failed',
|
||||
INVALID_URL: 'Invalid wallet URL',
|
||||
};
|
||||
|
||||
export interface DelegateSubmissionInput {
|
||||
pubKey: string;
|
||||
delegateSubmission: {
|
||||
nodeId: string;
|
||||
amount: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface UndelegateSubmissionInput {
|
||||
pubKey: string;
|
||||
undelegateSubmission: {
|
||||
nodeId: string;
|
||||
amount: string;
|
||||
method: 'METHOD_NOW' | 'METHOD_AT_END_OF_EPOCH';
|
||||
};
|
||||
}
|
||||
|
||||
export interface VoteSubmissionInput {
|
||||
pubKey: string;
|
||||
voteSubmission: {
|
||||
value: typeof VOTE_VALUE_MAP[VoteValue];
|
||||
proposalId: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface WithdrawSubmissionInput {
|
||||
pubKey: string;
|
||||
withdrawSubmission: {
|
||||
amount: string;
|
||||
asset: string;
|
||||
ext: {
|
||||
erc20: {
|
||||
receiverAddress: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type CommandSyncInput =
|
||||
| DelegateSubmissionInput
|
||||
| UndelegateSubmissionInput
|
||||
| VoteSubmissionInput
|
||||
| WithdrawSubmissionInput;
|
||||
|
||||
export interface CommandSyncResponse {
|
||||
inputData: string;
|
||||
pubKey: string;
|
||||
signature: {
|
||||
algo: string;
|
||||
value: string;
|
||||
version: number;
|
||||
};
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface IVegaWalletService {
|
||||
url: string;
|
||||
token: string;
|
||||
getToken(params: {
|
||||
wallet: string;
|
||||
passphrase: string;
|
||||
}): Promise<[string | undefined, string | undefined]>;
|
||||
revokeToken(): Promise<[string | undefined, boolean]>;
|
||||
getKeys(): Promise<[string | undefined, VegaKey[] | undefined]>;
|
||||
}
|
||||
|
||||
export class VegaWalletService implements IVegaWalletService {
|
||||
version: number;
|
||||
url: string;
|
||||
token: string;
|
||||
key: string;
|
||||
|
||||
constructor() {
|
||||
this.version = 1;
|
||||
this.url = LocalStorage.getItem(WALLET_URL_KEY) || DEFAULT_WALLET_URL;
|
||||
this.token = LocalStorage.getItem(TOKEN_STORAGE_KEY) || '';
|
||||
this.key = LocalStorage.getItem(KEY_STORAGE_KEY) || '';
|
||||
}
|
||||
|
||||
async getToken(params: {
|
||||
wallet: string;
|
||||
passphrase: string;
|
||||
url: string;
|
||||
}): Promise<[string | undefined, string | undefined]> {
|
||||
const urlValid = this.validateUrl(params.url);
|
||||
|
||||
if (urlValid) {
|
||||
this.setWalletUrl(params.url);
|
||||
} else {
|
||||
return [Errors.INVALID_URL, undefined];
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${this.getUrl()}/${Endpoints.TOKEN}`, {
|
||||
method: 'post',
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
if ('token' in json) {
|
||||
this.setToken(json.token);
|
||||
return [undefined, json.token];
|
||||
} else {
|
||||
return [Errors.INVALID_CREDENTIALS, undefined];
|
||||
}
|
||||
} catch (err) {
|
||||
return this.handleServiceUnavailable();
|
||||
}
|
||||
}
|
||||
|
||||
async revokeToken(): Promise<[string | undefined, boolean]> {
|
||||
if (!this.token) {
|
||||
return [Errors.NO_TOKEN, false];
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${this.getUrl()}/${Endpoints.TOKEN}`, {
|
||||
method: 'delete',
|
||||
headers: { authorization: `Bearer ${this.token}` },
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
if (json.success) {
|
||||
this.clearKey();
|
||||
this.clearToken();
|
||||
this.clearWalletUrl();
|
||||
return [undefined, true];
|
||||
} else {
|
||||
return [undefined, false];
|
||||
}
|
||||
} catch (err) {
|
||||
return this.handleServiceUnavailable(false);
|
||||
}
|
||||
}
|
||||
|
||||
async getKeys(): Promise<[string | undefined, VegaKey[] | undefined]> {
|
||||
if (!this.token) {
|
||||
return [Errors.NO_TOKEN, undefined];
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${this.getUrl()}/${Endpoints.KEYS}`, {
|
||||
headers: { authorization: `Bearer ${this.token}` },
|
||||
});
|
||||
|
||||
const err = this.verifyResponse(res);
|
||||
|
||||
if (err) {
|
||||
return [err, undefined];
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
return [undefined, json.keys];
|
||||
} catch (err) {
|
||||
return this.handleServiceUnavailable();
|
||||
}
|
||||
}
|
||||
|
||||
async commandSync(
|
||||
body: CommandSyncInput
|
||||
): Promise<[string | undefined, CommandSyncResponse | undefined]> {
|
||||
if (!this.token) {
|
||||
return [Errors.NO_TOKEN, undefined];
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${this.getUrl()}/${Endpoints.COMMAND}`, {
|
||||
method: 'post',
|
||||
body: JSON.stringify({
|
||||
...body,
|
||||
propagate: true,
|
||||
}),
|
||||
headers: { authorization: `Bearer ${this.token}` },
|
||||
});
|
||||
|
||||
const err = this.verifyResponse(res);
|
||||
|
||||
if (err) {
|
||||
return [err, undefined];
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
if ('errors' in json) {
|
||||
return [Errors.COMMAND_FAILED, undefined];
|
||||
} else {
|
||||
return [undefined, json];
|
||||
}
|
||||
} catch (err) {
|
||||
return this.handleServiceUnavailable();
|
||||
}
|
||||
}
|
||||
|
||||
setKey(key: string) {
|
||||
this.key = key;
|
||||
LocalStorage.setItem(KEY_STORAGE_KEY, key);
|
||||
}
|
||||
|
||||
private clearKey() {
|
||||
this.key = '';
|
||||
LocalStorage.removeItem(KEY_STORAGE_KEY);
|
||||
}
|
||||
|
||||
private setToken(token: string) {
|
||||
this.token = token;
|
||||
LocalStorage.setItem(TOKEN_STORAGE_KEY, token);
|
||||
}
|
||||
|
||||
private clearToken() {
|
||||
this.token = '';
|
||||
LocalStorage.removeItem(TOKEN_STORAGE_KEY);
|
||||
}
|
||||
|
||||
private setWalletUrl(url: string) {
|
||||
this.url = url;
|
||||
LocalStorage.setItem(WALLET_URL_KEY, url);
|
||||
}
|
||||
|
||||
private clearWalletUrl() {
|
||||
this.url = DEFAULT_WALLET_URL;
|
||||
LocalStorage.removeItem(WALLET_URL_KEY);
|
||||
}
|
||||
|
||||
private getUrl() {
|
||||
return `${this.url}/api/v${this.version}`;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private handleServiceUnavailable(returnVal?: boolean): [string, any] {
|
||||
this.clearWalletUrl();
|
||||
return [Errors.SERVICE_UNAVAILABLE, returnVal];
|
||||
}
|
||||
|
||||
private validateUrl(url: string) {
|
||||
try {
|
||||
new URL(url);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the response object to either return an error string or null if
|
||||
* everything looks good. Clears token 403 response returned
|
||||
*/
|
||||
private verifyResponse(res: Response): string | null {
|
||||
if (res.status === 403) {
|
||||
this.clearToken();
|
||||
return Errors.SESSION_EXPIRED;
|
||||
} else if (!res.ok) {
|
||||
return Errors.COMMAND_FAILED;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function hasErrorProperty(obj: unknown): obj is GenericErrorResponse {
|
||||
if (
|
||||
(obj as GenericErrorResponse).error !== undefined &&
|
||||
typeof (obj as GenericErrorResponse).error === 'string'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export const vegaWalletService = new VegaWalletService();
|
@ -1,3 +0,0 @@
|
||||
export interface GenericErrorResponse {
|
||||
error: string;
|
||||
}
|
@ -2,8 +2,6 @@ import { captureException, captureMessage } from '@sentry/minimal';
|
||||
import * as React from 'react';
|
||||
|
||||
import { VoteValue } from '../../../../__generated__/globalTypes';
|
||||
import type { VoteSubmissionInput } from '../../../../lib/vega-wallet/vega-wallet-service';
|
||||
import { vegaWalletService } from '../../../../lib/vega-wallet/vega-wallet-service';
|
||||
import { VOTE_VALUE_MAP } from './vote-types';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
|
||||
@ -44,7 +42,7 @@ export function useUserVote(
|
||||
yesVotes: Votes | null,
|
||||
noVotes: Votes | null
|
||||
) {
|
||||
const { keypair } = useVegaWallet();
|
||||
const { keypair, sendTx } = useVegaWallet();
|
||||
const yes = React.useMemo(() => yesVotes || [], [yesVotes]);
|
||||
const no = React.useMemo(() => noVotes || [], [noVotes]);
|
||||
|
||||
@ -98,19 +96,15 @@ export function useUserVote(
|
||||
setVoteState(VoteState.Pending);
|
||||
|
||||
try {
|
||||
const variables: VoteSubmissionInput = {
|
||||
const variables = {
|
||||
pubKey: keypair.pub,
|
||||
propagate: true,
|
||||
voteSubmission: {
|
||||
value: VOTE_VALUE_MAP[value],
|
||||
proposalId,
|
||||
},
|
||||
};
|
||||
const [err] = await vegaWalletService.commandSync(variables);
|
||||
|
||||
if (err) {
|
||||
setVoteState(VoteState.Failed);
|
||||
captureException(err);
|
||||
}
|
||||
await sendTx(variables);
|
||||
|
||||
// Now await vote via poll in parent component
|
||||
} catch (err) {
|
||||
|
@ -58,10 +58,12 @@ export const AssociatePage = ({
|
||||
() => totalVestedBalance.plus(totalLockedBalance).isEqualTo(0),
|
||||
[totalLockedBalance, totalVestedBalance]
|
||||
);
|
||||
|
||||
const zeroVega = React.useMemo(
|
||||
() => walletBalance.isEqualTo(0),
|
||||
[walletBalance]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (zeroVega && !zeroVesting) {
|
||||
setSelectedStakingMethod(StakingMethod.Contract);
|
||||
|
@ -5,8 +5,8 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { removeDecimal } from '../../lib/decimals';
|
||||
import { vegaWalletService } from '../../lib/vega-wallet/vega-wallet-service';
|
||||
import type { UndelegateSubmissionInput } from '../../lib/vega-wallet/vega-wallet-service';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { UndelegateSubmissionBody } from '@vegaprotocol/vegawallet-service-api-client';
|
||||
|
||||
interface PendingStakeProps {
|
||||
pendingAmount: BigNumber;
|
||||
@ -27,28 +27,26 @@ export const PendingStake = ({
|
||||
pubkey,
|
||||
}: PendingStakeProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { sendTx } = useVegaWallet();
|
||||
const { appState } = useAppState();
|
||||
const [formState, setFormState] = React.useState(FormState.Default);
|
||||
|
||||
const removeStakeNow = async () => {
|
||||
setFormState(FormState.Pending);
|
||||
const undelegateInput: UndelegateSubmissionInput = {
|
||||
pubKey: pubkey,
|
||||
undelegateSubmission: {
|
||||
nodeId,
|
||||
amount: removeDecimal(new BigNumber(pendingAmount), appState.decimals),
|
||||
method: 'METHOD_NOW',
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const command = undelegateInput;
|
||||
const [err] = await vegaWalletService.commandSync(command);
|
||||
|
||||
if (err) {
|
||||
setFormState(FormState.Failure);
|
||||
Sentry.captureException(err);
|
||||
}
|
||||
const command: UndelegateSubmissionBody = {
|
||||
pubKey: pubkey,
|
||||
propagate: true,
|
||||
undelegateSubmission: {
|
||||
nodeId,
|
||||
amount: removeDecimal(
|
||||
new BigNumber(pendingAmount),
|
||||
appState.decimals
|
||||
),
|
||||
method: 'METHOD_NOW',
|
||||
},
|
||||
};
|
||||
await sendTx(command);
|
||||
} catch (err) {
|
||||
setFormState(FormState.Failure);
|
||||
Sentry.captureException(err);
|
||||
|
@ -12,11 +12,6 @@ import { useNetworkParam } from '../../hooks/use-network-param';
|
||||
import { useSearchParams } from '../../hooks/use-search-params';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { addDecimal, removeDecimal } from '../../lib/decimals';
|
||||
import type {
|
||||
DelegateSubmissionInput,
|
||||
UndelegateSubmissionInput,
|
||||
} from '../../lib/vega-wallet/vega-wallet-service';
|
||||
import { vegaWalletService } from '../../lib/vega-wallet/vega-wallet-service';
|
||||
import type {
|
||||
PartyDelegations,
|
||||
PartyDelegationsVariables,
|
||||
@ -25,6 +20,11 @@ import { StakeFailure } from './stake-failure';
|
||||
import { StakePending } from './stake-pending';
|
||||
import { StakeSuccess } from './stake-success';
|
||||
import { Button, FormGroup } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type {
|
||||
DelegateSubmissionBody,
|
||||
UndelegateSubmissionBody,
|
||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
||||
|
||||
export const PARTY_DELEGATIONS_QUERY = gql`
|
||||
query PartyDelegations($partyId: ID!) {
|
||||
@ -81,6 +81,7 @@ export const StakingForm = ({
|
||||
const navigate = useNavigate();
|
||||
const client = useApolloClient();
|
||||
const { appState } = useAppState();
|
||||
const { sendTx } = useVegaWallet();
|
||||
const [formState, setFormState] = React.useState(FormState.Default);
|
||||
const { t } = useTranslation();
|
||||
const [action, setAction] = React.useState<StakeAction>(params.action);
|
||||
@ -114,15 +115,17 @@ export const StakingForm = ({
|
||||
|
||||
async function onSubmit() {
|
||||
setFormState(FormState.Pending);
|
||||
const delegateInput: DelegateSubmissionInput = {
|
||||
const delegateInput: DelegateSubmissionBody = {
|
||||
pubKey: pubkey,
|
||||
propagate: true,
|
||||
delegateSubmission: {
|
||||
nodeId,
|
||||
amount: removeDecimal(new BigNumber(amount), appState.decimals),
|
||||
},
|
||||
};
|
||||
const undelegateInput: UndelegateSubmissionInput = {
|
||||
const undelegateInput: UndelegateSubmissionBody = {
|
||||
pubKey: pubkey,
|
||||
propagate: true,
|
||||
undelegateSubmission: {
|
||||
nodeId,
|
||||
amount: removeDecimal(new BigNumber(amount), appState.decimals),
|
||||
@ -134,12 +137,7 @@ export const StakingForm = ({
|
||||
};
|
||||
try {
|
||||
const command = action === Actions.Add ? delegateInput : undelegateInput;
|
||||
const [err] = await vegaWalletService.commandSync(command);
|
||||
|
||||
if (err) {
|
||||
setFormState(FormState.Failure);
|
||||
Sentry.captureException(err);
|
||||
}
|
||||
await sendTx(command);
|
||||
|
||||
// await success via poll
|
||||
} catch (err) {
|
||||
|
@ -140,11 +140,53 @@ export interface WithdrawPage_party {
|
||||
withdrawals: WithdrawPage_party_withdrawals[] | null;
|
||||
}
|
||||
|
||||
export interface WithdrawPage_assets_source_BuiltinAsset {
|
||||
__typename: "BuiltinAsset";
|
||||
}
|
||||
|
||||
export interface WithdrawPage_assets_source_ERC20 {
|
||||
__typename: "ERC20";
|
||||
/**
|
||||
* The address of the erc20 contract
|
||||
*/
|
||||
contractAddress: string;
|
||||
}
|
||||
|
||||
export type WithdrawPage_assets_source = WithdrawPage_assets_source_BuiltinAsset | WithdrawPage_assets_source_ERC20;
|
||||
|
||||
export interface WithdrawPage_assets {
|
||||
__typename: "Asset";
|
||||
/**
|
||||
* The id of the asset
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The symbol of the asset (e.g: GBP)
|
||||
*/
|
||||
symbol: string;
|
||||
/**
|
||||
* The full name of the asset (e.g: Great British Pound)
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The precision of the asset
|
||||
*/
|
||||
decimals: number;
|
||||
/**
|
||||
* The origin source of the asset (e.g: an erc20 asset)
|
||||
*/
|
||||
source: WithdrawPage_assets_source;
|
||||
}
|
||||
|
||||
export interface WithdrawPage {
|
||||
/**
|
||||
* An entity that is trading on the VEGA network
|
||||
*/
|
||||
party: WithdrawPage_party | null;
|
||||
/**
|
||||
* The list of all assets in use in the vega network
|
||||
*/
|
||||
assets: WithdrawPage_assets[] | null;
|
||||
}
|
||||
|
||||
export interface WithdrawPageVariables {
|
||||
|
@ -5,7 +5,6 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { AccountType } from '../../__generated__/globalTypes';
|
||||
import { EthWalletContainer } from '../../components/eth-wallet-container';
|
||||
import { Heading } from '../../components/heading';
|
||||
import { SplashLoader } from '../../components/splash-loader';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
@ -15,7 +14,7 @@ import type {
|
||||
WithdrawPage,
|
||||
WithdrawPageVariables,
|
||||
} from './__generated__/WithdrawPage';
|
||||
import { WithdrawForm } from './withdraw-form';
|
||||
import { WithdrawManager } from '@vegaprotocol/withdraws';
|
||||
|
||||
const Withdraw = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -24,9 +23,11 @@ const Withdraw = () => {
|
||||
<>
|
||||
<Heading title={t('withdrawPageHeading')} />
|
||||
<p>{t('withdrawPageText')}</p>
|
||||
<VegaWalletContainer>
|
||||
{(currVegaKey) => <WithdrawContainer currVegaKey={currVegaKey} />}
|
||||
</VegaWalletContainer>
|
||||
<div className="mb-24">
|
||||
<VegaWalletContainer>
|
||||
{(currVegaKey) => <WithdrawContainer currVegaKey={currVegaKey} />}
|
||||
</VegaWalletContainer>
|
||||
</div>
|
||||
<Callout title={t('withdrawPageInfoCalloutTitle')}>
|
||||
<p className="mb-0">{t('withdrawPageInfoCalloutText')}</p>
|
||||
</Callout>
|
||||
@ -74,6 +75,17 @@ const WITHDRAW_PAGE_QUERY = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
assets {
|
||||
id
|
||||
symbol
|
||||
name
|
||||
decimals
|
||||
source {
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -123,27 +135,21 @@ export const WithdrawContainer = ({ currVegaKey }: WithdrawContainerProps) => {
|
||||
return (
|
||||
<>
|
||||
{hasPendingWithdrawals && (
|
||||
<Callout
|
||||
title={t('pendingWithdrawalsCalloutTitle')}
|
||||
intent={Intent.Prompt}
|
||||
>
|
||||
<p>{t('pendingWithdrawalsCalloutText')}</p>
|
||||
<p className="mb-0">
|
||||
<Link to={Routes.WITHDRAWALS}>
|
||||
{t('pendingWithdrawalsCalloutButton')}
|
||||
</Link>
|
||||
</p>
|
||||
</Callout>
|
||||
<div className="mb-24">
|
||||
<Callout
|
||||
title={t('pendingWithdrawalsCalloutTitle')}
|
||||
intent={Intent.Prompt}
|
||||
>
|
||||
<p>{t('pendingWithdrawalsCalloutText')}</p>
|
||||
<p className="mb-0">
|
||||
<Link to={Routes.WITHDRAWALS}>
|
||||
{t('pendingWithdrawalsCalloutButton')}
|
||||
</Link>
|
||||
</p>
|
||||
</Callout>
|
||||
</div>
|
||||
)}
|
||||
<EthWalletContainer>
|
||||
{(connectedAddress) => (
|
||||
<WithdrawForm
|
||||
accounts={accounts}
|
||||
currVegaKey={currVegaKey}
|
||||
connectedAddress={connectedAddress}
|
||||
/>
|
||||
)}
|
||||
</EthWalletContainer>
|
||||
<WithdrawManager assets={data.assets || []} accounts={accounts} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,153 +0,0 @@
|
||||
import { Callout, FormGroup, Intent, Select } from '@vegaprotocol/ui-toolkit';
|
||||
import { ethers } from 'ethers';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
import { Loader } from '../../components/loader';
|
||||
import { StatefulButton } from '../../components/stateful-button';
|
||||
import { AmountInput } from '../../components/token-input';
|
||||
import type { VegaKeyExtended } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
Status as WithdrawStatus,
|
||||
useCreateWithdrawal,
|
||||
} from '../../hooks/use-create-withdrawal';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { removeDecimal } from '../../lib/decimals';
|
||||
import { Routes } from '../router-config';
|
||||
import type { WithdrawPage_party_accounts } from './__generated__/WithdrawPage';
|
||||
import { EthAddressInput } from './eth-address-input';
|
||||
|
||||
interface WithdrawFormProps {
|
||||
accounts: WithdrawPage_party_accounts[];
|
||||
currVegaKey: VegaKeyExtended;
|
||||
connectedAddress: string;
|
||||
}
|
||||
|
||||
export const WithdrawForm = ({
|
||||
accounts,
|
||||
currVegaKey,
|
||||
connectedAddress,
|
||||
}: WithdrawFormProps) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [amountStr, setAmount] = React.useState('');
|
||||
const [account, setAccount] = React.useState(accounts[0]);
|
||||
const [status, submit] = useCreateWithdrawal(currVegaKey.pub);
|
||||
const [destinationAddress, setDestinationAddress] =
|
||||
React.useState(connectedAddress);
|
||||
const amount = React.useMemo(
|
||||
() => new BigNumber(amountStr || 0),
|
||||
[amountStr]
|
||||
);
|
||||
|
||||
const maximum = React.useMemo(() => {
|
||||
if (account) {
|
||||
return new BigNumber(account.balanceFormatted);
|
||||
}
|
||||
|
||||
return new BigNumber(0);
|
||||
}, [account]);
|
||||
|
||||
const valid = React.useMemo(() => {
|
||||
if (
|
||||
!destinationAddress ||
|
||||
amount.isLessThanOrEqualTo(0) ||
|
||||
amount.isGreaterThan(maximum)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, [destinationAddress, amount, maximum]);
|
||||
|
||||
const addressValid = React.useMemo(() => {
|
||||
return ethers.utils.isAddress(destinationAddress);
|
||||
}, [destinationAddress]);
|
||||
|
||||
// Navigate to complete withdrawals page once withdrawal
|
||||
// creation is complete
|
||||
React.useEffect(() => {
|
||||
if (status === WithdrawStatus.Success) {
|
||||
navigate(Routes.WITHDRAWALS);
|
||||
}
|
||||
}, [status, navigate]);
|
||||
|
||||
return (
|
||||
<form
|
||||
className="my-40"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (!valid || !addressValid) return;
|
||||
|
||||
submit(
|
||||
removeDecimal(amount, account.asset.decimals),
|
||||
account.asset.id,
|
||||
destinationAddress
|
||||
);
|
||||
}}
|
||||
>
|
||||
<FormGroup label={t('withdrawFormAssetLabel')} labelFor="asset">
|
||||
{accounts.length ? (
|
||||
<Select
|
||||
name="asset"
|
||||
id="asset"
|
||||
onChange={(e) => {
|
||||
const account = accounts.find(
|
||||
(a) => a.asset.id === e.currentTarget.value
|
||||
);
|
||||
if (!account) throw new Error('No account');
|
||||
setAccount(account);
|
||||
}}
|
||||
>
|
||||
{accounts.map((a) => (
|
||||
<option value={a.asset.id}>
|
||||
{a.asset.symbol} ({a.balanceFormatted})
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
) : (
|
||||
<p className="text-white-60">{t('withdrawFormNoAsset')}</p>
|
||||
)}
|
||||
</FormGroup>
|
||||
<div className="mb-24">
|
||||
<Callout
|
||||
title={t('withdrawPreparedWarningHeading')}
|
||||
intent={Intent.Warning}
|
||||
>
|
||||
<p>{t('withdrawPreparedWarningText1')}</p>
|
||||
<p className="mb-0">{t('withdrawPreparedWarningText2')}</p>
|
||||
</Callout>
|
||||
</div>
|
||||
<EthAddressInput
|
||||
onChange={setDestinationAddress}
|
||||
address={destinationAddress}
|
||||
connectedAddress={connectedAddress}
|
||||
isValid={addressValid}
|
||||
/>
|
||||
<FormGroup label={t('withdrawFormAmountLabel')} labelFor="amount">
|
||||
<AmountInput
|
||||
amount={amountStr}
|
||||
setAmount={setAmount}
|
||||
maximum={maximum}
|
||||
currency={'VEGA'}
|
||||
/>
|
||||
</FormGroup>
|
||||
<StatefulButton
|
||||
type="submit"
|
||||
disabled={!addressValid || !valid || status === WithdrawStatus.Pending}
|
||||
>
|
||||
{status === WithdrawStatus.Pending ? (
|
||||
<>
|
||||
<Loader />
|
||||
<span>{t('withdrawFormSubmitButtonPending')}</span>
|
||||
</>
|
||||
) : (
|
||||
t('withdrawFormSubmitButtonIdle', {
|
||||
amount: amountStr,
|
||||
symbol: account?.asset.symbol,
|
||||
})
|
||||
)}
|
||||
</StatefulButton>
|
||||
</form>
|
||||
);
|
||||
};
|
@ -1,105 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { WithdrawalStatus } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: WithdrawalsPage
|
||||
// ====================================================
|
||||
|
||||
export interface WithdrawalsPage_party_withdrawals_asset {
|
||||
__typename: "Asset";
|
||||
/**
|
||||
* The id of the asset
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The symbol of the asset (e.g: GBP)
|
||||
*/
|
||||
symbol: string;
|
||||
/**
|
||||
* The precision of the asset
|
||||
*/
|
||||
decimals: number;
|
||||
}
|
||||
|
||||
export interface WithdrawalsPage_party_withdrawals_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
*/
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface WithdrawalsPage_party_withdrawals_details {
|
||||
__typename: "Erc20WithdrawalDetails";
|
||||
/**
|
||||
* The ethereum address of the receiver of the asset funds
|
||||
*/
|
||||
receiverAddress: string;
|
||||
}
|
||||
|
||||
export interface WithdrawalsPage_party_withdrawals {
|
||||
__typename: "Withdrawal";
|
||||
/**
|
||||
* The Vega internal id of the withdrawal
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The amount to be withdrawn
|
||||
*/
|
||||
amount: string;
|
||||
/**
|
||||
* The asset to be withdrawn
|
||||
*/
|
||||
asset: WithdrawalsPage_party_withdrawals_asset;
|
||||
/**
|
||||
* The PartyID initiating the withdrawal
|
||||
*/
|
||||
party: WithdrawalsPage_party_withdrawals_party;
|
||||
/**
|
||||
* The current status of the withdrawal
|
||||
*/
|
||||
status: WithdrawalStatus;
|
||||
/**
|
||||
* RFC3339Nano time at which the withdrawal was created
|
||||
*/
|
||||
createdTimestamp: string;
|
||||
/**
|
||||
* RFC3339Nano time at which the withdrawal was finalized
|
||||
*/
|
||||
withdrawnTimestamp: string | null;
|
||||
/**
|
||||
* Hash of the transaction on the foreign chain
|
||||
*/
|
||||
txHash: string | null;
|
||||
/**
|
||||
* Foreign chain specific details about the withdrawal
|
||||
*/
|
||||
details: WithdrawalsPage_party_withdrawals_details | null;
|
||||
}
|
||||
|
||||
export interface WithdrawalsPage_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The list of all withdrawals initiated by the party
|
||||
*/
|
||||
withdrawals: WithdrawalsPage_party_withdrawals[] | null;
|
||||
}
|
||||
|
||||
export interface WithdrawalsPage {
|
||||
/**
|
||||
* An entity that is trading on the VEGA network
|
||||
*/
|
||||
party: WithdrawalsPage_party | null;
|
||||
}
|
||||
|
||||
export interface WithdrawalsPageVariables {
|
||||
partyId: string;
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { format } from 'date-fns';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import React from 'react';
|
||||
@ -10,23 +8,16 @@ import { EtherscanLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../components/heading';
|
||||
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
||||
import { SplashLoader } from '../../components/splash-loader';
|
||||
import { TransactionButton } from '../../components/transaction-button';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import type { VegaKeyExtended } from '@vegaprotocol/wallet';
|
||||
import { useContracts } from '../../contexts/contracts/contracts-context';
|
||||
import { TxState } from '../../hooks/transaction-reducer';
|
||||
import { usePollERC20Approval } from '../../hooks/use-erc-poll20-approval';
|
||||
import { useRefreshBalances } from '../../hooks/use-refresh-balances';
|
||||
import { useTransaction } from '../../hooks/use-transaction';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { DATE_FORMAT_DETAILED } from '../../lib/date-formats';
|
||||
import { addDecimal } from '../../lib/decimals';
|
||||
import { truncateMiddle } from '../../lib/truncate-middle';
|
||||
import type {
|
||||
WithdrawalsPage,
|
||||
WithdrawalsPage_party_withdrawals,
|
||||
WithdrawalsPageVariables,
|
||||
} from './__generated__/WithdrawalsPage';
|
||||
import type { Withdrawals_party_withdrawals } from '@vegaprotocol/withdraws';
|
||||
import { useCompleteWithdraw, useWithdrawals } from '@vegaprotocol/withdraws';
|
||||
import { TransactionDialog } from '@vegaprotocol/web3';
|
||||
import { WithdrawalStatus } from '../../__generated__/globalTypes';
|
||||
|
||||
const Withdrawals = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -34,13 +25,6 @@ const Withdrawals = () => {
|
||||
return (
|
||||
<>
|
||||
<Heading title={t('withdrawalsTitle')} />
|
||||
<p>{t('withdrawalsText')}</p>
|
||||
<Callout
|
||||
title={t('withdrawalsPreparedWarningHeading')}
|
||||
intent={Intent.Warning}
|
||||
>
|
||||
<p>{t('withdrawalsPreparedWarningText')}</p>
|
||||
</Callout>
|
||||
<VegaWalletContainer>
|
||||
{(currVegaKey) => (
|
||||
<WithdrawPendingContainer currVegaKey={currVegaKey} />
|
||||
@ -54,51 +38,12 @@ interface WithdrawPendingContainerProps {
|
||||
currVegaKey: VegaKeyExtended;
|
||||
}
|
||||
|
||||
const WITHDRAWALS_PAGE_QUERY = gql`
|
||||
query WithdrawalsPage($partyId: ID!) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
withdrawals {
|
||||
id
|
||||
amount
|
||||
asset {
|
||||
id
|
||||
symbol
|
||||
decimals
|
||||
}
|
||||
party {
|
||||
id
|
||||
}
|
||||
status
|
||||
createdTimestamp
|
||||
withdrawnTimestamp
|
||||
txHash
|
||||
details {
|
||||
... on Erc20WithdrawalDetails {
|
||||
receiverAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const WithdrawPendingContainer = ({
|
||||
currVegaKey,
|
||||
}: WithdrawPendingContainerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { account } = useWeb3React();
|
||||
const refreshBalances = useRefreshBalances(account || '');
|
||||
const { data, loading, error, refetch } = useQuery<
|
||||
WithdrawalsPage,
|
||||
WithdrawalsPageVariables
|
||||
>(WITHDRAWALS_PAGE_QUERY, {
|
||||
variables: { partyId: currVegaKey.pub },
|
||||
// This must be network-only because you are navigated to this page automatically after the withdrawal is created,
|
||||
// if you have already visited this page the query result is cached with 0 withdrawals, so we need to refetch every
|
||||
// time to ensure the withdrawal is shown immediately
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
const { transaction, submit } = useCompleteWithdraw();
|
||||
const { data, loading, error } = useWithdrawals();
|
||||
|
||||
const withdrawals = React.useMemo(() => {
|
||||
if (!data?.party?.withdrawals?.length) return [];
|
||||
@ -132,61 +77,60 @@ const WithdrawPendingContainer = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<ul role="list">
|
||||
{withdrawals.map((w) => (
|
||||
<li key={w.id} className="mb-28">
|
||||
<Withdrawal
|
||||
withdrawal={w}
|
||||
refetchWithdrawals={refetch}
|
||||
refetchBalances={refreshBalances}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<>
|
||||
<h2>{t('withdrawalsPreparedWarningHeading')}</h2>
|
||||
<p>{t('withdrawalsText')}</p>
|
||||
<p>{t('withdrawalsPreparedWarningText')}</p>
|
||||
<ul role="list">
|
||||
{withdrawals.map((w) => (
|
||||
<li key={w.id}>
|
||||
<Withdrawal withdrawal={w} complete={submit} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<TransactionDialog name="withdraw" {...transaction} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface WithdrawalProps {
|
||||
withdrawal: WithdrawalsPage_party_withdrawals;
|
||||
refetchWithdrawals: () => void;
|
||||
refetchBalances: () => void;
|
||||
withdrawal: Withdrawals_party_withdrawals;
|
||||
complete: (withdrawalId: string) => void;
|
||||
}
|
||||
|
||||
export const Withdrawal = ({
|
||||
withdrawal,
|
||||
refetchWithdrawals,
|
||||
refetchBalances,
|
||||
}: WithdrawalProps) => {
|
||||
export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const erc20Approval = usePollERC20Approval(withdrawal.id);
|
||||
const { erc20Bridge } = useContracts();
|
||||
const { state, perform, reset } = useTransaction(() => {
|
||||
if (!erc20Approval) {
|
||||
throw new Error('Withdraw needs approval object');
|
||||
}
|
||||
if (!withdrawal.details?.receiverAddress) {
|
||||
throw new Error('Missing receiver address');
|
||||
|
||||
const renderStatus = ({
|
||||
id,
|
||||
status,
|
||||
txHash,
|
||||
pendingOnForeignChain,
|
||||
}: Withdrawals_party_withdrawals) => {
|
||||
if (pendingOnForeignChain) {
|
||||
return t('Pending');
|
||||
}
|
||||
|
||||
return erc20Bridge.withdraw({
|
||||
assetSource: erc20Approval.assetSource,
|
||||
amount: erc20Approval.amount,
|
||||
nonce: erc20Approval.nonce,
|
||||
signatures: erc20Approval.signatures,
|
||||
// TODO: switch when targetAddress is populated and deployed to mainnet data.erc20WithdrawalApproval.targetAddress,
|
||||
targetAddress: withdrawal.details.receiverAddress,
|
||||
});
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
// Once complete we need to refetch the withdrawals so that pending withdrawal
|
||||
// is updated to have a txHash indicating it is complete. Updating your account balance
|
||||
// is already handled by the query in the VegaWallet that polls
|
||||
if (state.txState === TxState.Complete) {
|
||||
refetchWithdrawals();
|
||||
refetchBalances();
|
||||
if (status === WithdrawalStatus.Finalized) {
|
||||
if (txHash) {
|
||||
return t('Complete');
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{t('Incomplete')}{' '}
|
||||
<button
|
||||
className="text-white underline"
|
||||
onClick={() => complete(id)}
|
||||
>
|
||||
{t('withdrawalsCompleteButton')}
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [state, refetchWithdrawals, refetchBalances]);
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -201,10 +145,6 @@ export const Withdrawal = ({
|
||||
{withdrawal.asset.symbol}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('from')}
|
||||
<span>{truncateMiddle(withdrawal.party.id)}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('toEthereum')}
|
||||
<span>
|
||||
@ -226,27 +166,23 @@ export const Withdrawal = ({
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('Signature')}
|
||||
<span title={erc20Approval?.signatures}>
|
||||
{!erc20Approval?.signatures
|
||||
? t('Loading')
|
||||
: truncateMiddle(erc20Approval.signatures)}
|
||||
{t('withdrawalTransaction', { foreignChain: 'Ethereum' })}
|
||||
<span>
|
||||
{withdrawal.txHash ? (
|
||||
<EtherscanLink
|
||||
tx={withdrawal.txHash}
|
||||
text={truncateMiddle(withdrawal.txHash)}
|
||||
/>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('status')}
|
||||
{renderStatus(withdrawal)}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
<TransactionButton
|
||||
text={
|
||||
!erc20Approval
|
||||
? t('withdrawalsPreparingButton')
|
||||
: t('withdrawalsCompleteButton')
|
||||
}
|
||||
transactionState={state}
|
||||
forceTxState={withdrawal.txHash ? TxState.Complete : undefined}
|
||||
forceTxHash={withdrawal.txHash}
|
||||
disabled={!erc20Approval}
|
||||
start={perform}
|
||||
reset={reset}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -8,10 +8,12 @@ Feature: Markets page
|
||||
|
||||
Scenario: Select active market
|
||||
Given I am on the markets page
|
||||
And I can view markets
|
||||
When I click on "Active" mocked market
|
||||
Then trading page for "active" market is displayed
|
||||
|
||||
Scenario: Select suspended market
|
||||
Given I am on the markets page
|
||||
And I can view markets
|
||||
When I click on "Suspended" mocked market
|
||||
Then trading page for "suspended" market is displayed
|
||||
|
@ -10,6 +10,9 @@ export default class MarketPage extends BasePage {
|
||||
marketStateColId = 'data';
|
||||
|
||||
validateMarketsAreDisplayed() {
|
||||
// We need this to ensure that ag-grid is fully rendered before asserting
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(1000);
|
||||
cy.get('.ag-root-wrapper').should('be.visible');
|
||||
}
|
||||
|
||||
|
@ -15,21 +15,19 @@ const mockMarkets = () => {
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockMarkets();
|
||||
});
|
||||
|
||||
Then('I navigate to markets page', () => {
|
||||
mockMarkets();
|
||||
marketsPage.navigateToMarkets();
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
Given('I am on the markets page', () => {
|
||||
mockMarkets();
|
||||
cy.visit('/markets');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
Then('I can view markets', () => {
|
||||
cy.wait('@Markets');
|
||||
marketsPage.validateMarketsAreDisplayed();
|
||||
});
|
||||
|
||||
|
@ -1,18 +0,0 @@
|
||||
import type { AssetFields } from './__generated__/AssetFields';
|
||||
|
||||
export interface ERC20Asset extends AssetFields {
|
||||
source: {
|
||||
__typename: 'ERC20';
|
||||
contractAddress: string;
|
||||
};
|
||||
}
|
||||
|
||||
type UnknownAsset = Pick<AssetFields, '__typename' | 'source'>;
|
||||
|
||||
// Type guard to ensure an asset is an ERC20 token
|
||||
export const isERC20Asset = (asset: UnknownAsset): asset is ERC20Asset => {
|
||||
if (asset.source.__typename === 'ERC20') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
@ -6,7 +6,6 @@ import { DepositManager } from '@vegaprotocol/deposits';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { ASSET_FRAGMENT } from '../../../lib/query-fragments';
|
||||
import { isERC20Asset } from '../../../lib/assets';
|
||||
|
||||
const DEPOSIT_PAGE_QUERY = gql`
|
||||
${ASSET_FRAGMENT}
|
||||
@ -45,7 +44,7 @@ export const DepositContainer = ({
|
||||
<DepositManager
|
||||
bridgeAddress={ethereumConfig.collateral_bridge_contract.address}
|
||||
requiredConfirmations={ethereumConfig.confirmations}
|
||||
assets={data.assets.filter(isERC20Asset)}
|
||||
assets={data.assets}
|
||||
initialAssetId={assetId}
|
||||
/>
|
||||
);
|
||||
|
@ -6,7 +6,6 @@ import { WithdrawManager } from '@vegaprotocol/withdraws';
|
||||
import { ASSET_FRAGMENT } from '../../../lib/query-fragments';
|
||||
import Link from 'next/link';
|
||||
import { PageQueryContainer } from '../../../components/page-query-container';
|
||||
import { isERC20Asset } from '../../../lib/assets';
|
||||
import type {
|
||||
WithdrawPageQuery,
|
||||
WithdrawPageQueryVariables,
|
||||
@ -85,7 +84,7 @@ export const WithdrawPageContainer = ({
|
||||
</p>
|
||||
) : null}
|
||||
<WithdrawManager
|
||||
assets={data.assets.filter(isERC20Asset)}
|
||||
assets={data.assets}
|
||||
accounts={data.party?.accounts || []}
|
||||
initialAssetId={assetId}
|
||||
/>
|
||||
|
@ -82,8 +82,8 @@ export const DepositForm = ({
|
||||
});
|
||||
|
||||
const onDeposit = async (fields: FormFields) => {
|
||||
if (!selectedAsset) {
|
||||
throw new Error('Asset not selected');
|
||||
if (!selectedAsset || selectedAsset.source.__typename !== 'ERC20') {
|
||||
throw new Error('Invalid asset');
|
||||
}
|
||||
|
||||
submitDeposit({
|
||||
@ -153,11 +153,13 @@ export const DepositForm = ({
|
||||
<FormGroup label={t('Asset')} labelFor="asset" className="relative">
|
||||
<Select {...register('asset', { validate: { required } })} id="asset">
|
||||
<option value="">{t('Please select')}</option>
|
||||
{assets.map((a) => (
|
||||
<option key={a.id} value={a.id}>
|
||||
{a.name}
|
||||
</option>
|
||||
))}
|
||||
{assets
|
||||
.filter((a) => a.source.__typename === 'ERC20')
|
||||
.map((a) => (
|
||||
<option key={a.id} value={a.id}>
|
||||
{a.name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
{errors.asset?.message && (
|
||||
<InputError intent="danger" className="mt-4" forInput="asset">
|
||||
|
@ -10,16 +10,23 @@ import { useSubmitFaucet } from './use-submit-faucet';
|
||||
import { EthTxStatus, TransactionDialog } from '@vegaprotocol/web3';
|
||||
import { useTokenContract, useBridgeContract } from '@vegaprotocol/web3';
|
||||
|
||||
interface ERC20AssetSource {
|
||||
__typename: 'ERC20';
|
||||
contractAddress: string;
|
||||
}
|
||||
|
||||
interface BuiltinAssetSource {
|
||||
__typename: 'BuiltinAsset';
|
||||
}
|
||||
|
||||
type AssetSource = ERC20AssetSource | BuiltinAssetSource;
|
||||
export interface Asset {
|
||||
__typename: 'Asset';
|
||||
id: string;
|
||||
symbol: string;
|
||||
name: string;
|
||||
decimals: number;
|
||||
source: {
|
||||
__typename: 'ERC20';
|
||||
contractAddress: string;
|
||||
};
|
||||
source: AssetSource;
|
||||
}
|
||||
|
||||
interface DepositManagerProps {
|
||||
@ -44,7 +51,9 @@ export const DepositManager = ({
|
||||
}, [assets, assetId]);
|
||||
|
||||
const tokenContract = useTokenContract(
|
||||
asset?.source.contractAddress,
|
||||
asset?.source.__typename === 'ERC20'
|
||||
? asset.source.contractAddress
|
||||
: undefined,
|
||||
process.env['NX_VEGA_ENV'] !== 'MAINNET'
|
||||
);
|
||||
const bridgeContract = useBridgeContract();
|
||||
|
@ -14,7 +14,7 @@ export const useGetDepositLimits = (
|
||||
asset?: Asset
|
||||
): Limits | null => {
|
||||
const getLimits = useCallback(async () => {
|
||||
if (!contract || !asset) {
|
||||
if (!contract || !asset || asset.source.__typename !== 'ERC20') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,10 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { SHA3 } from 'sha3';
|
||||
import { remove0x } from './remove-0x';
|
||||
import { sha3_256 } from 'js-sha3';
|
||||
|
||||
/**
|
||||
* This function creates an ID in the same way that core does on the backend. This way we
|
||||
* Can match up the newly created order with incoming orders via a subscription
|
||||
*/
|
||||
export const determineId = (sig: string) => {
|
||||
// Prepend 0x
|
||||
if (sig.slice(0, 2) !== '0x') {
|
||||
sig = '0x' + sig;
|
||||
}
|
||||
|
||||
// Create the ID
|
||||
const hash = new SHA3(256);
|
||||
const bytes = ethers.utils.arrayify(sig);
|
||||
hash.update(Buffer.from(bytes));
|
||||
const id = ethers.utils.hexlify(hash.digest());
|
||||
|
||||
// Remove 0x as core doesn't keep them in the API
|
||||
return remove0x(id);
|
||||
return sha3_256(ethers.utils.arrayify('0x' + sig));
|
||||
};
|
||||
|
@ -1,9 +1,8 @@
|
||||
import type {
|
||||
VegaKey,
|
||||
TransactionResponse,
|
||||
OrderSubmissionBody,
|
||||
WithdrawSubmissionBody,
|
||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
||||
import type { TransactionSubmission } from '../types';
|
||||
export { RestConnector } from './rest-connector';
|
||||
|
||||
type ErrorResponse =
|
||||
@ -26,6 +25,6 @@ export interface VegaConnector {
|
||||
|
||||
/** Send a TX to the network. Only support order submission for now */
|
||||
sendTx: (
|
||||
body: OrderSubmissionBody | WithdrawSubmissionBody
|
||||
body: TransactionSubmission
|
||||
) => Promise<TransactionResponse | ErrorResponse>;
|
||||
}
|
||||
|
@ -1,8 +1,4 @@
|
||||
import type {
|
||||
Configuration,
|
||||
OrderSubmissionBody,
|
||||
WithdrawSubmissionBody,
|
||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
||||
import type { Configuration } from '@vegaprotocol/vegawallet-service-api-client';
|
||||
import {
|
||||
createConfiguration,
|
||||
DefaultApi,
|
||||
@ -10,6 +6,7 @@ import {
|
||||
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { WALLET_CONFIG } from '../storage-keys';
|
||||
import type { VegaConnector } from '.';
|
||||
import type { TransactionSubmission } from '../types';
|
||||
|
||||
// Perhaps there should be a default ConnectorConfig that others can extend off. Do all connectors
|
||||
// need to use local storage, I don't think so...
|
||||
@ -88,7 +85,7 @@ export class RestConnector implements VegaConnector {
|
||||
}
|
||||
}
|
||||
|
||||
async sendTx(body: OrderSubmissionBody | WithdrawSubmissionBody) {
|
||||
async sendTx(body: TransactionSubmission) {
|
||||
try {
|
||||
return await this.service.commandSyncPost(body);
|
||||
} catch (err) {
|
||||
|
@ -5,10 +5,7 @@ import type { VegaKeyExtended, VegaWalletContextShape } from '.';
|
||||
import type { VegaConnector } from './connectors';
|
||||
import { VegaWalletContext } from './context';
|
||||
import { WALLET_KEY } from './storage-keys';
|
||||
import type {
|
||||
OrderSubmissionBody,
|
||||
WithdrawSubmissionBody,
|
||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
||||
import type { TransactionSubmission } from './types';
|
||||
|
||||
interface VegaWalletProviderProps {
|
||||
children: ReactNode;
|
||||
@ -72,16 +69,13 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const sendTx = useCallback(
|
||||
(body: OrderSubmissionBody | WithdrawSubmissionBody) => {
|
||||
if (!connector.current) {
|
||||
return null;
|
||||
}
|
||||
const sendTx = useCallback((body: TransactionSubmission) => {
|
||||
if (!connector.current) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return connector.current.sendTx(body);
|
||||
},
|
||||
[]
|
||||
);
|
||||
return connector.current.sendTx(body);
|
||||
}, []);
|
||||
|
||||
// Current selected keypair derived from publicKey state
|
||||
const keypair = useMemo(() => {
|
||||
|
@ -1,5 +1,8 @@
|
||||
import type {
|
||||
DelegateSubmissionBody,
|
||||
OrderSubmissionBody,
|
||||
UndelegateSubmissionBody,
|
||||
VoteSubmissionBody,
|
||||
WithdrawSubmissionBody,
|
||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
||||
|
||||
@ -25,4 +28,7 @@ export enum OrderTimeInForce {
|
||||
// Will make Transaction a union type as other transactions are added
|
||||
export type TransactionSubmission =
|
||||
| OrderSubmissionBody
|
||||
| WithdrawSubmissionBody;
|
||||
| WithdrawSubmissionBody
|
||||
| VoteSubmissionBody
|
||||
| DelegateSubmissionBody
|
||||
| UndelegateSubmissionBody;
|
||||
|
@ -4,3 +4,4 @@ export * from './lib/withdrawals-table';
|
||||
export * from './lib/use-complete-withdraw';
|
||||
export * from './lib/use-withdraw';
|
||||
export * from './lib/use-withdrawals';
|
||||
export * from './lib/__generated__/Withdrawals';
|
||||
|
@ -11,6 +11,7 @@ export const generateAsset = (override?: PartialDeep<Asset>) => {
|
||||
name: 'asset-name',
|
||||
decimals: 5,
|
||||
source: {
|
||||
__typename: 'ERC20',
|
||||
contractAddress: 'contract-address',
|
||||
},
|
||||
};
|
||||
|
@ -1,13 +1,22 @@
|
||||
import type { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
interface ERC20AssetSource {
|
||||
__typename: 'ERC20';
|
||||
contractAddress: string;
|
||||
}
|
||||
|
||||
interface BuiltinAssetSource {
|
||||
__typename: 'BuiltinAsset';
|
||||
}
|
||||
|
||||
type AssetSource = ERC20AssetSource | BuiltinAssetSource;
|
||||
|
||||
export interface Asset {
|
||||
id: string;
|
||||
symbol: string;
|
||||
name: string;
|
||||
decimals: number;
|
||||
source: {
|
||||
contractAddress: string;
|
||||
};
|
||||
source: AssetSource;
|
||||
}
|
||||
|
||||
export interface Account {
|
||||
|
@ -90,11 +90,13 @@ export const WithdrawForm = ({
|
||||
id="asset"
|
||||
>
|
||||
<option value="">{t('Please select')}</option>
|
||||
{assets.map((a) => (
|
||||
<option key={a.id} value={a.id}>
|
||||
{a.name}
|
||||
</option>
|
||||
))}
|
||||
{assets
|
||||
.filter((a) => a.source.__typename === 'ERC20')
|
||||
.map((a) => (
|
||||
<option key={a.id} value={a.id}>
|
||||
{a.name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
|
@ -29,7 +29,7 @@
|
||||
"@sentry/react": "^6.19.2",
|
||||
"@sentry/tracing": "^6.19.2",
|
||||
"@vegaprotocol/smart-contracts-sdk": "^1.6.0",
|
||||
"@vegaprotocol/vegawallet-service-api-client": "^0.4.9",
|
||||
"@vegaprotocol/vegawallet-service-api-client": "^0.4.11",
|
||||
"@walletconnect/ethereum-provider": "^1.7.5",
|
||||
"@web3-react/core": "8.0.20-beta.0",
|
||||
"@web3-react/metamask": "8.0.16-beta.0",
|
||||
@ -52,6 +52,7 @@
|
||||
"i18next": "^20.3.5",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"immer": "^9.0.12",
|
||||
"js-sha3": "^0.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"next": "^12.0.7",
|
||||
"pennant": "0.4.8",
|
||||
@ -69,7 +70,6 @@
|
||||
"react-window-infinite-loader": "^1.0.7",
|
||||
"recharts": "^2.1.2",
|
||||
"regenerator-runtime": "0.13.7",
|
||||
"sha3": "^2.1.4",
|
||||
"tailwindcss": "^3.0.23",
|
||||
"tslib": "^2.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
|
27
yarn.lock
27
yarn.lock
@ -6205,10 +6205,10 @@
|
||||
ethers "^5.5.2"
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@vegaprotocol/vegawallet-service-api-client@^0.4.9":
|
||||
version "0.4.9"
|
||||
resolved "https://registry.yarnpkg.com/@vegaprotocol/vegawallet-service-api-client/-/vegawallet-service-api-client-0.4.9.tgz#9c2566407e9c86248f2c170b1f495afdcb347d87"
|
||||
integrity sha512-zqppuVu3VrsHpdcNBvu6G+qMMt0CBXi5wHBb/0ryiXdXD9dmQci5Qz8zoGx5syOupFMdUDNstmGOaFWbBOuvbg==
|
||||
"@vegaprotocol/vegawallet-service-api-client@^0.4.11":
|
||||
version "0.4.11"
|
||||
resolved "https://registry.yarnpkg.com/@vegaprotocol/vegawallet-service-api-client/-/vegawallet-service-api-client-0.4.11.tgz#41a623afc9957dcf8b5425f74280ba7861e92b74"
|
||||
integrity sha512-yiodc3YFWG+RGG+wjpTjYmNAECP/Nv244mVu8IGVtj8LZo02KC/LpNCgmMhGaK4ZcqVtxHv9t7OUCSEWZhSgOg==
|
||||
dependencies:
|
||||
es6-promise "^4.2.4"
|
||||
url-parse "^1.4.3"
|
||||
@ -8438,14 +8438,6 @@ buffer@5.6.0:
|
||||
base64-js "^1.0.2"
|
||||
ieee754 "^1.1.4"
|
||||
|
||||
buffer@6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
||||
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
buffer@^4.3.0:
|
||||
version "4.9.2"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
|
||||
@ -13304,7 +13296,7 @@ identity-obj-proxy@3.0.0:
|
||||
dependencies:
|
||||
harmony-reflect "^1.4.6"
|
||||
|
||||
ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1:
|
||||
ieee754@^1.1.13, ieee754@^1.1.4:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
@ -14693,7 +14685,7 @@ jest@27.2.3:
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^27.2.3"
|
||||
|
||||
js-sha3@0.8.0:
|
||||
js-sha3@0.8.0, js-sha3@^0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
|
||||
@ -19310,13 +19302,6 @@ sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8, sha.js@~2.4.4:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
sha3@^2.1.4:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/sha3/-/sha3-2.1.4.tgz#000fac0fe7c2feac1f48a25e7a31b52a6492cc8f"
|
||||
integrity sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==
|
||||
dependencies:
|
||||
buffer "6.0.3"
|
||||
|
||||
shallow-clone@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
||||
|
Loading…
Reference in New Issue
Block a user