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 NavDrawer = ({ inverted }: { inverted: boolean }) => {
|
||||||
const { appState, appDispatch } = useAppState();
|
const { appState, appDispatch } = useAppState();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
@ -25,7 +25,6 @@ export const TransactionComplete = ({
|
|||||||
<p>
|
<p>
|
||||||
<EtherscanLink tx={hash} />
|
<EtherscanLink tx={hash} />
|
||||||
</p>
|
</p>
|
||||||
LINK
|
|
||||||
{footer && <p data-testid="transaction-complete-footer">{footer}</p>}
|
{footer && <p data-testid="transaction-complete-footer">{footer}</p>}
|
||||||
</Callout>
|
</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...",
|
"Loading": "Loading...",
|
||||||
"Something went wrong": "Something went wrong",
|
"Something went wrong": "Something went wrong",
|
||||||
"Try again": "Try again",
|
"Try again": "Try again",
|
||||||
|
"Incomplete": "Incomplete",
|
||||||
"Complete": "Complete",
|
"Complete": "Complete",
|
||||||
"View on Etherscan (opens in a new tab)": "View on Etherscan (opens in a new tab)",
|
"View on Etherscan (opens in a new tab)": "View on Etherscan (opens in a new tab)",
|
||||||
"Transaction in progress": "Transaction in progress",
|
"Transaction in progress": "Transaction in progress",
|
||||||
@ -437,7 +438,7 @@
|
|||||||
"withdrawalsText": "These withdrawals need to be completed with an Ethereum transaction.",
|
"withdrawalsText": "These withdrawals need to be completed with an Ethereum transaction.",
|
||||||
"withdrawalsNone": "You don't have any pending withdrawals.",
|
"withdrawalsNone": "You don't have any pending withdrawals.",
|
||||||
"withdrawalsCompleteButton": "Finish withdrawal",
|
"withdrawalsCompleteButton": "Finish withdrawal",
|
||||||
"withdrawalsPreparingButton": "Preparing withdrawal",
|
"withdrawalTransaction": "Transaction ({{foreignChain}})",
|
||||||
"signature": "Signature",
|
"signature": "Signature",
|
||||||
"created": "Created",
|
"created": "Created",
|
||||||
"toEthereum": "To (Ethereum)",
|
"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 * as React from 'react';
|
||||||
|
|
||||||
import { VoteValue } from '../../../../__generated__/globalTypes';
|
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 { VOTE_VALUE_MAP } from './vote-types';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
@ -44,7 +42,7 @@ export function useUserVote(
|
|||||||
yesVotes: Votes | null,
|
yesVotes: Votes | null,
|
||||||
noVotes: Votes | null
|
noVotes: Votes | null
|
||||||
) {
|
) {
|
||||||
const { keypair } = useVegaWallet();
|
const { keypair, sendTx } = useVegaWallet();
|
||||||
const yes = React.useMemo(() => yesVotes || [], [yesVotes]);
|
const yes = React.useMemo(() => yesVotes || [], [yesVotes]);
|
||||||
const no = React.useMemo(() => noVotes || [], [noVotes]);
|
const no = React.useMemo(() => noVotes || [], [noVotes]);
|
||||||
|
|
||||||
@ -98,19 +96,15 @@ export function useUserVote(
|
|||||||
setVoteState(VoteState.Pending);
|
setVoteState(VoteState.Pending);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const variables: VoteSubmissionInput = {
|
const variables = {
|
||||||
pubKey: keypair.pub,
|
pubKey: keypair.pub,
|
||||||
|
propagate: true,
|
||||||
voteSubmission: {
|
voteSubmission: {
|
||||||
value: VOTE_VALUE_MAP[value],
|
value: VOTE_VALUE_MAP[value],
|
||||||
proposalId,
|
proposalId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const [err] = await vegaWalletService.commandSync(variables);
|
await sendTx(variables);
|
||||||
|
|
||||||
if (err) {
|
|
||||||
setVoteState(VoteState.Failed);
|
|
||||||
captureException(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now await vote via poll in parent component
|
// Now await vote via poll in parent component
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -58,10 +58,12 @@ export const AssociatePage = ({
|
|||||||
() => totalVestedBalance.plus(totalLockedBalance).isEqualTo(0),
|
() => totalVestedBalance.plus(totalLockedBalance).isEqualTo(0),
|
||||||
[totalLockedBalance, totalVestedBalance]
|
[totalLockedBalance, totalVestedBalance]
|
||||||
);
|
);
|
||||||
|
|
||||||
const zeroVega = React.useMemo(
|
const zeroVega = React.useMemo(
|
||||||
() => walletBalance.isEqualTo(0),
|
() => walletBalance.isEqualTo(0),
|
||||||
[walletBalance]
|
[walletBalance]
|
||||||
);
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (zeroVega && !zeroVesting) {
|
if (zeroVega && !zeroVesting) {
|
||||||
setSelectedStakingMethod(StakingMethod.Contract);
|
setSelectedStakingMethod(StakingMethod.Contract);
|
||||||
|
@ -5,8 +5,8 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
import { BigNumber } from '../../lib/bignumber';
|
||||||
import { removeDecimal } from '../../lib/decimals';
|
import { removeDecimal } from '../../lib/decimals';
|
||||||
import { vegaWalletService } from '../../lib/vega-wallet/vega-wallet-service';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import type { UndelegateSubmissionInput } from '../../lib/vega-wallet/vega-wallet-service';
|
import type { UndelegateSubmissionBody } from '@vegaprotocol/vegawallet-service-api-client';
|
||||||
|
|
||||||
interface PendingStakeProps {
|
interface PendingStakeProps {
|
||||||
pendingAmount: BigNumber;
|
pendingAmount: BigNumber;
|
||||||
@ -27,28 +27,26 @@ export const PendingStake = ({
|
|||||||
pubkey,
|
pubkey,
|
||||||
}: PendingStakeProps) => {
|
}: PendingStakeProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { sendTx } = useVegaWallet();
|
||||||
const { appState } = useAppState();
|
const { appState } = useAppState();
|
||||||
const [formState, setFormState] = React.useState(FormState.Default);
|
const [formState, setFormState] = React.useState(FormState.Default);
|
||||||
|
|
||||||
const removeStakeNow = async () => {
|
const removeStakeNow = async () => {
|
||||||
setFormState(FormState.Pending);
|
setFormState(FormState.Pending);
|
||||||
const undelegateInput: UndelegateSubmissionInput = {
|
|
||||||
pubKey: pubkey,
|
|
||||||
undelegateSubmission: {
|
|
||||||
nodeId,
|
|
||||||
amount: removeDecimal(new BigNumber(pendingAmount), appState.decimals),
|
|
||||||
method: 'METHOD_NOW',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const command = undelegateInput;
|
const command: UndelegateSubmissionBody = {
|
||||||
const [err] = await vegaWalletService.commandSync(command);
|
pubKey: pubkey,
|
||||||
|
propagate: true,
|
||||||
if (err) {
|
undelegateSubmission: {
|
||||||
setFormState(FormState.Failure);
|
nodeId,
|
||||||
Sentry.captureException(err);
|
amount: removeDecimal(
|
||||||
}
|
new BigNumber(pendingAmount),
|
||||||
|
appState.decimals
|
||||||
|
),
|
||||||
|
method: 'METHOD_NOW',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await sendTx(command);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setFormState(FormState.Failure);
|
setFormState(FormState.Failure);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
|
@ -12,11 +12,6 @@ import { useNetworkParam } from '../../hooks/use-network-param';
|
|||||||
import { useSearchParams } from '../../hooks/use-search-params';
|
import { useSearchParams } from '../../hooks/use-search-params';
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
import { BigNumber } from '../../lib/bignumber';
|
||||||
import { addDecimal, removeDecimal } from '../../lib/decimals';
|
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 {
|
import type {
|
||||||
PartyDelegations,
|
PartyDelegations,
|
||||||
PartyDelegationsVariables,
|
PartyDelegationsVariables,
|
||||||
@ -25,6 +20,11 @@ import { StakeFailure } from './stake-failure';
|
|||||||
import { StakePending } from './stake-pending';
|
import { StakePending } from './stake-pending';
|
||||||
import { StakeSuccess } from './stake-success';
|
import { StakeSuccess } from './stake-success';
|
||||||
import { Button, FormGroup } from '@vegaprotocol/ui-toolkit';
|
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`
|
export const PARTY_DELEGATIONS_QUERY = gql`
|
||||||
query PartyDelegations($partyId: ID!) {
|
query PartyDelegations($partyId: ID!) {
|
||||||
@ -81,6 +81,7 @@ export const StakingForm = ({
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const { appState } = useAppState();
|
const { appState } = useAppState();
|
||||||
|
const { sendTx } = useVegaWallet();
|
||||||
const [formState, setFormState] = React.useState(FormState.Default);
|
const [formState, setFormState] = React.useState(FormState.Default);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [action, setAction] = React.useState<StakeAction>(params.action);
|
const [action, setAction] = React.useState<StakeAction>(params.action);
|
||||||
@ -114,15 +115,17 @@ export const StakingForm = ({
|
|||||||
|
|
||||||
async function onSubmit() {
|
async function onSubmit() {
|
||||||
setFormState(FormState.Pending);
|
setFormState(FormState.Pending);
|
||||||
const delegateInput: DelegateSubmissionInput = {
|
const delegateInput: DelegateSubmissionBody = {
|
||||||
pubKey: pubkey,
|
pubKey: pubkey,
|
||||||
|
propagate: true,
|
||||||
delegateSubmission: {
|
delegateSubmission: {
|
||||||
nodeId,
|
nodeId,
|
||||||
amount: removeDecimal(new BigNumber(amount), appState.decimals),
|
amount: removeDecimal(new BigNumber(amount), appState.decimals),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const undelegateInput: UndelegateSubmissionInput = {
|
const undelegateInput: UndelegateSubmissionBody = {
|
||||||
pubKey: pubkey,
|
pubKey: pubkey,
|
||||||
|
propagate: true,
|
||||||
undelegateSubmission: {
|
undelegateSubmission: {
|
||||||
nodeId,
|
nodeId,
|
||||||
amount: removeDecimal(new BigNumber(amount), appState.decimals),
|
amount: removeDecimal(new BigNumber(amount), appState.decimals),
|
||||||
@ -134,12 +137,7 @@ export const StakingForm = ({
|
|||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const command = action === Actions.Add ? delegateInput : undelegateInput;
|
const command = action === Actions.Add ? delegateInput : undelegateInput;
|
||||||
const [err] = await vegaWalletService.commandSync(command);
|
await sendTx(command);
|
||||||
|
|
||||||
if (err) {
|
|
||||||
setFormState(FormState.Failure);
|
|
||||||
Sentry.captureException(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// await success via poll
|
// await success via poll
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -140,11 +140,53 @@ export interface WithdrawPage_party {
|
|||||||
withdrawals: WithdrawPage_party_withdrawals[] | null;
|
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 {
|
export interface WithdrawPage {
|
||||||
/**
|
/**
|
||||||
* An entity that is trading on the VEGA network
|
* An entity that is trading on the VEGA network
|
||||||
*/
|
*/
|
||||||
party: WithdrawPage_party | null;
|
party: WithdrawPage_party | null;
|
||||||
|
/**
|
||||||
|
* The list of all assets in use in the vega network
|
||||||
|
*/
|
||||||
|
assets: WithdrawPage_assets[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WithdrawPageVariables {
|
export interface WithdrawPageVariables {
|
||||||
|
@ -5,7 +5,6 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { AccountType } from '../../__generated__/globalTypes';
|
import { AccountType } from '../../__generated__/globalTypes';
|
||||||
import { EthWalletContainer } from '../../components/eth-wallet-container';
|
|
||||||
import { Heading } from '../../components/heading';
|
import { Heading } from '../../components/heading';
|
||||||
import { SplashLoader } from '../../components/splash-loader';
|
import { SplashLoader } from '../../components/splash-loader';
|
||||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||||
@ -15,7 +14,7 @@ import type {
|
|||||||
WithdrawPage,
|
WithdrawPage,
|
||||||
WithdrawPageVariables,
|
WithdrawPageVariables,
|
||||||
} from './__generated__/WithdrawPage';
|
} from './__generated__/WithdrawPage';
|
||||||
import { WithdrawForm } from './withdraw-form';
|
import { WithdrawManager } from '@vegaprotocol/withdraws';
|
||||||
|
|
||||||
const Withdraw = () => {
|
const Withdraw = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -24,9 +23,11 @@ const Withdraw = () => {
|
|||||||
<>
|
<>
|
||||||
<Heading title={t('withdrawPageHeading')} />
|
<Heading title={t('withdrawPageHeading')} />
|
||||||
<p>{t('withdrawPageText')}</p>
|
<p>{t('withdrawPageText')}</p>
|
||||||
<VegaWalletContainer>
|
<div className="mb-24">
|
||||||
{(currVegaKey) => <WithdrawContainer currVegaKey={currVegaKey} />}
|
<VegaWalletContainer>
|
||||||
</VegaWalletContainer>
|
{(currVegaKey) => <WithdrawContainer currVegaKey={currVegaKey} />}
|
||||||
|
</VegaWalletContainer>
|
||||||
|
</div>
|
||||||
<Callout title={t('withdrawPageInfoCalloutTitle')}>
|
<Callout title={t('withdrawPageInfoCalloutTitle')}>
|
||||||
<p className="mb-0">{t('withdrawPageInfoCalloutText')}</p>
|
<p className="mb-0">{t('withdrawPageInfoCalloutText')}</p>
|
||||||
</Callout>
|
</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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{hasPendingWithdrawals && (
|
{hasPendingWithdrawals && (
|
||||||
<Callout
|
<div className="mb-24">
|
||||||
title={t('pendingWithdrawalsCalloutTitle')}
|
<Callout
|
||||||
intent={Intent.Prompt}
|
title={t('pendingWithdrawalsCalloutTitle')}
|
||||||
>
|
intent={Intent.Prompt}
|
||||||
<p>{t('pendingWithdrawalsCalloutText')}</p>
|
>
|
||||||
<p className="mb-0">
|
<p>{t('pendingWithdrawalsCalloutText')}</p>
|
||||||
<Link to={Routes.WITHDRAWALS}>
|
<p className="mb-0">
|
||||||
{t('pendingWithdrawalsCalloutButton')}
|
<Link to={Routes.WITHDRAWALS}>
|
||||||
</Link>
|
{t('pendingWithdrawalsCalloutButton')}
|
||||||
</p>
|
</Link>
|
||||||
</Callout>
|
</p>
|
||||||
|
</Callout>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<EthWalletContainer>
|
<WithdrawManager assets={data.assets || []} accounts={accounts} />
|
||||||
{(connectedAddress) => (
|
|
||||||
<WithdrawForm
|
|
||||||
accounts={accounts}
|
|
||||||
currVegaKey={currVegaKey}
|
|
||||||
connectedAddress={connectedAddress}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</EthWalletContainer>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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 { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { useWeb3React } from '@web3-react/core';
|
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import orderBy from 'lodash/orderBy';
|
import orderBy from 'lodash/orderBy';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -10,23 +8,16 @@ import { EtherscanLink } from '@vegaprotocol/ui-toolkit';
|
|||||||
import { Heading } from '../../components/heading';
|
import { Heading } from '../../components/heading';
|
||||||
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SplashLoader } from '../../components/splash-loader';
|
import { SplashLoader } from '../../components/splash-loader';
|
||||||
import { TransactionButton } from '../../components/transaction-button';
|
|
||||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||||
import type { VegaKeyExtended } from '@vegaprotocol/wallet';
|
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 { BigNumber } from '../../lib/bignumber';
|
||||||
import { DATE_FORMAT_DETAILED } from '../../lib/date-formats';
|
import { DATE_FORMAT_DETAILED } from '../../lib/date-formats';
|
||||||
import { addDecimal } from '../../lib/decimals';
|
import { addDecimal } from '../../lib/decimals';
|
||||||
import { truncateMiddle } from '../../lib/truncate-middle';
|
import { truncateMiddle } from '../../lib/truncate-middle';
|
||||||
import type {
|
import type { Withdrawals_party_withdrawals } from '@vegaprotocol/withdraws';
|
||||||
WithdrawalsPage,
|
import { useCompleteWithdraw, useWithdrawals } from '@vegaprotocol/withdraws';
|
||||||
WithdrawalsPage_party_withdrawals,
|
import { TransactionDialog } from '@vegaprotocol/web3';
|
||||||
WithdrawalsPageVariables,
|
import { WithdrawalStatus } from '../../__generated__/globalTypes';
|
||||||
} from './__generated__/WithdrawalsPage';
|
|
||||||
|
|
||||||
const Withdrawals = () => {
|
const Withdrawals = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -34,13 +25,6 @@ const Withdrawals = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Heading title={t('withdrawalsTitle')} />
|
<Heading title={t('withdrawalsTitle')} />
|
||||||
<p>{t('withdrawalsText')}</p>
|
|
||||||
<Callout
|
|
||||||
title={t('withdrawalsPreparedWarningHeading')}
|
|
||||||
intent={Intent.Warning}
|
|
||||||
>
|
|
||||||
<p>{t('withdrawalsPreparedWarningText')}</p>
|
|
||||||
</Callout>
|
|
||||||
<VegaWalletContainer>
|
<VegaWalletContainer>
|
||||||
{(currVegaKey) => (
|
{(currVegaKey) => (
|
||||||
<WithdrawPendingContainer currVegaKey={currVegaKey} />
|
<WithdrawPendingContainer currVegaKey={currVegaKey} />
|
||||||
@ -54,51 +38,12 @@ interface WithdrawPendingContainerProps {
|
|||||||
currVegaKey: VegaKeyExtended;
|
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 = ({
|
const WithdrawPendingContainer = ({
|
||||||
currVegaKey,
|
currVegaKey,
|
||||||
}: WithdrawPendingContainerProps) => {
|
}: WithdrawPendingContainerProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { account } = useWeb3React();
|
const { transaction, submit } = useCompleteWithdraw();
|
||||||
const refreshBalances = useRefreshBalances(account || '');
|
const { data, loading, error } = useWithdrawals();
|
||||||
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 withdrawals = React.useMemo(() => {
|
const withdrawals = React.useMemo(() => {
|
||||||
if (!data?.party?.withdrawals?.length) return [];
|
if (!data?.party?.withdrawals?.length) return [];
|
||||||
@ -132,61 +77,60 @@ const WithdrawPendingContainer = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul role="list">
|
<>
|
||||||
{withdrawals.map((w) => (
|
<h2>{t('withdrawalsPreparedWarningHeading')}</h2>
|
||||||
<li key={w.id} className="mb-28">
|
<p>{t('withdrawalsText')}</p>
|
||||||
<Withdrawal
|
<p>{t('withdrawalsPreparedWarningText')}</p>
|
||||||
withdrawal={w}
|
<ul role="list">
|
||||||
refetchWithdrawals={refetch}
|
{withdrawals.map((w) => (
|
||||||
refetchBalances={refreshBalances}
|
<li key={w.id}>
|
||||||
/>
|
<Withdrawal withdrawal={w} complete={submit} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
<TransactionDialog name="withdraw" {...transaction} />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface WithdrawalProps {
|
interface WithdrawalProps {
|
||||||
withdrawal: WithdrawalsPage_party_withdrawals;
|
withdrawal: Withdrawals_party_withdrawals;
|
||||||
refetchWithdrawals: () => void;
|
complete: (withdrawalId: string) => void;
|
||||||
refetchBalances: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Withdrawal = ({
|
export const Withdrawal = ({ withdrawal, complete }: WithdrawalProps) => {
|
||||||
withdrawal,
|
|
||||||
refetchWithdrawals,
|
|
||||||
refetchBalances,
|
|
||||||
}: WithdrawalProps) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const erc20Approval = usePollERC20Approval(withdrawal.id);
|
|
||||||
const { erc20Bridge } = useContracts();
|
const renderStatus = ({
|
||||||
const { state, perform, reset } = useTransaction(() => {
|
id,
|
||||||
if (!erc20Approval) {
|
status,
|
||||||
throw new Error('Withdraw needs approval object');
|
txHash,
|
||||||
}
|
pendingOnForeignChain,
|
||||||
if (!withdrawal.details?.receiverAddress) {
|
}: Withdrawals_party_withdrawals) => {
|
||||||
throw new Error('Missing receiver address');
|
if (pendingOnForeignChain) {
|
||||||
|
return t('Pending');
|
||||||
}
|
}
|
||||||
|
|
||||||
return erc20Bridge.withdraw({
|
if (status === WithdrawalStatus.Finalized) {
|
||||||
assetSource: erc20Approval.assetSource,
|
if (txHash) {
|
||||||
amount: erc20Approval.amount,
|
return t('Complete');
|
||||||
nonce: erc20Approval.nonce,
|
} else {
|
||||||
signatures: erc20Approval.signatures,
|
return (
|
||||||
// TODO: switch when targetAddress is populated and deployed to mainnet data.erc20WithdrawalApproval.targetAddress,
|
<>
|
||||||
targetAddress: withdrawal.details.receiverAddress,
|
{t('Incomplete')}{' '}
|
||||||
});
|
<button
|
||||||
});
|
className="text-white underline"
|
||||||
|
onClick={() => complete(id)}
|
||||||
React.useEffect(() => {
|
>
|
||||||
// Once complete we need to refetch the withdrawals so that pending withdrawal
|
{t('withdrawalsCompleteButton')}
|
||||||
// is updated to have a txHash indicating it is complete. Updating your account balance
|
</button>
|
||||||
// is already handled by the query in the VegaWallet that polls
|
</>
|
||||||
if (state.txState === TxState.Complete) {
|
);
|
||||||
refetchWithdrawals();
|
}
|
||||||
refetchBalances();
|
|
||||||
}
|
}
|
||||||
}, [state, refetchWithdrawals, refetchBalances]);
|
|
||||||
|
return status;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -201,10 +145,6 @@ export const Withdrawal = ({
|
|||||||
{withdrawal.asset.symbol}
|
{withdrawal.asset.symbol}
|
||||||
</span>
|
</span>
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
<KeyValueTableRow>
|
|
||||||
{t('from')}
|
|
||||||
<span>{truncateMiddle(withdrawal.party.id)}</span>
|
|
||||||
</KeyValueTableRow>
|
|
||||||
<KeyValueTableRow>
|
<KeyValueTableRow>
|
||||||
{t('toEthereum')}
|
{t('toEthereum')}
|
||||||
<span>
|
<span>
|
||||||
@ -226,27 +166,23 @@ export const Withdrawal = ({
|
|||||||
</span>
|
</span>
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
<KeyValueTableRow>
|
<KeyValueTableRow>
|
||||||
{t('Signature')}
|
{t('withdrawalTransaction', { foreignChain: 'Ethereum' })}
|
||||||
<span title={erc20Approval?.signatures}>
|
<span>
|
||||||
{!erc20Approval?.signatures
|
{withdrawal.txHash ? (
|
||||||
? t('Loading')
|
<EtherscanLink
|
||||||
: truncateMiddle(erc20Approval.signatures)}
|
tx={withdrawal.txHash}
|
||||||
|
text={truncateMiddle(withdrawal.txHash)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
|
<KeyValueTableRow>
|
||||||
|
{t('status')}
|
||||||
|
{renderStatus(withdrawal)}
|
||||||
|
</KeyValueTableRow>
|
||||||
</KeyValueTable>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,10 +8,12 @@ Feature: Markets page
|
|||||||
|
|
||||||
Scenario: Select active market
|
Scenario: Select active market
|
||||||
Given I am on the markets page
|
Given I am on the markets page
|
||||||
|
And I can view markets
|
||||||
When I click on "Active" mocked market
|
When I click on "Active" mocked market
|
||||||
Then trading page for "active" market is displayed
|
Then trading page for "active" market is displayed
|
||||||
|
|
||||||
Scenario: Select suspended market
|
Scenario: Select suspended market
|
||||||
Given I am on the markets page
|
Given I am on the markets page
|
||||||
|
And I can view markets
|
||||||
When I click on "Suspended" mocked market
|
When I click on "Suspended" mocked market
|
||||||
Then trading page for "suspended" market is displayed
|
Then trading page for "suspended" market is displayed
|
||||||
|
@ -10,6 +10,9 @@ export default class MarketPage extends BasePage {
|
|||||||
marketStateColId = 'data';
|
marketStateColId = 'data';
|
||||||
|
|
||||||
validateMarketsAreDisplayed() {
|
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');
|
cy.get('.ag-root-wrapper').should('be.visible');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,21 +15,19 @@ const mockMarkets = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mockMarkets();
|
|
||||||
});
|
|
||||||
|
|
||||||
Then('I navigate to markets page', () => {
|
Then('I navigate to markets page', () => {
|
||||||
|
mockMarkets();
|
||||||
marketsPage.navigateToMarkets();
|
marketsPage.navigateToMarkets();
|
||||||
|
cy.wait('@Markets');
|
||||||
});
|
});
|
||||||
|
|
||||||
Given('I am on the markets page', () => {
|
Given('I am on the markets page', () => {
|
||||||
|
mockMarkets();
|
||||||
cy.visit('/markets');
|
cy.visit('/markets');
|
||||||
cy.wait('@Markets');
|
cy.wait('@Markets');
|
||||||
});
|
});
|
||||||
|
|
||||||
Then('I can view markets', () => {
|
Then('I can view markets', () => {
|
||||||
cy.wait('@Markets');
|
|
||||||
marketsPage.validateMarketsAreDisplayed();
|
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 { t } from '@vegaprotocol/react-helpers';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { ASSET_FRAGMENT } from '../../../lib/query-fragments';
|
import { ASSET_FRAGMENT } from '../../../lib/query-fragments';
|
||||||
import { isERC20Asset } from '../../../lib/assets';
|
|
||||||
|
|
||||||
const DEPOSIT_PAGE_QUERY = gql`
|
const DEPOSIT_PAGE_QUERY = gql`
|
||||||
${ASSET_FRAGMENT}
|
${ASSET_FRAGMENT}
|
||||||
@ -45,7 +44,7 @@ export const DepositContainer = ({
|
|||||||
<DepositManager
|
<DepositManager
|
||||||
bridgeAddress={ethereumConfig.collateral_bridge_contract.address}
|
bridgeAddress={ethereumConfig.collateral_bridge_contract.address}
|
||||||
requiredConfirmations={ethereumConfig.confirmations}
|
requiredConfirmations={ethereumConfig.confirmations}
|
||||||
assets={data.assets.filter(isERC20Asset)}
|
assets={data.assets}
|
||||||
initialAssetId={assetId}
|
initialAssetId={assetId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,6 @@ import { WithdrawManager } from '@vegaprotocol/withdraws';
|
|||||||
import { ASSET_FRAGMENT } from '../../../lib/query-fragments';
|
import { ASSET_FRAGMENT } from '../../../lib/query-fragments';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { PageQueryContainer } from '../../../components/page-query-container';
|
import { PageQueryContainer } from '../../../components/page-query-container';
|
||||||
import { isERC20Asset } from '../../../lib/assets';
|
|
||||||
import type {
|
import type {
|
||||||
WithdrawPageQuery,
|
WithdrawPageQuery,
|
||||||
WithdrawPageQueryVariables,
|
WithdrawPageQueryVariables,
|
||||||
@ -85,7 +84,7 @@ export const WithdrawPageContainer = ({
|
|||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
<WithdrawManager
|
<WithdrawManager
|
||||||
assets={data.assets.filter(isERC20Asset)}
|
assets={data.assets}
|
||||||
accounts={data.party?.accounts || []}
|
accounts={data.party?.accounts || []}
|
||||||
initialAssetId={assetId}
|
initialAssetId={assetId}
|
||||||
/>
|
/>
|
||||||
|
@ -82,8 +82,8 @@ export const DepositForm = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onDeposit = async (fields: FormFields) => {
|
const onDeposit = async (fields: FormFields) => {
|
||||||
if (!selectedAsset) {
|
if (!selectedAsset || selectedAsset.source.__typename !== 'ERC20') {
|
||||||
throw new Error('Asset not selected');
|
throw new Error('Invalid asset');
|
||||||
}
|
}
|
||||||
|
|
||||||
submitDeposit({
|
submitDeposit({
|
||||||
@ -153,11 +153,13 @@ export const DepositForm = ({
|
|||||||
<FormGroup label={t('Asset')} labelFor="asset" className="relative">
|
<FormGroup label={t('Asset')} labelFor="asset" className="relative">
|
||||||
<Select {...register('asset', { validate: { required } })} id="asset">
|
<Select {...register('asset', { validate: { required } })} id="asset">
|
||||||
<option value="">{t('Please select')}</option>
|
<option value="">{t('Please select')}</option>
|
||||||
{assets.map((a) => (
|
{assets
|
||||||
<option key={a.id} value={a.id}>
|
.filter((a) => a.source.__typename === 'ERC20')
|
||||||
{a.name}
|
.map((a) => (
|
||||||
</option>
|
<option key={a.id} value={a.id}>
|
||||||
))}
|
{a.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
{errors.asset?.message && (
|
{errors.asset?.message && (
|
||||||
<InputError intent="danger" className="mt-4" forInput="asset">
|
<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 { EthTxStatus, TransactionDialog } from '@vegaprotocol/web3';
|
||||||
import { useTokenContract, useBridgeContract } 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 {
|
export interface Asset {
|
||||||
__typename: 'Asset';
|
__typename: 'Asset';
|
||||||
id: string;
|
id: string;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
name: string;
|
name: string;
|
||||||
decimals: number;
|
decimals: number;
|
||||||
source: {
|
source: AssetSource;
|
||||||
__typename: 'ERC20';
|
|
||||||
contractAddress: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DepositManagerProps {
|
interface DepositManagerProps {
|
||||||
@ -44,7 +51,9 @@ export const DepositManager = ({
|
|||||||
}, [assets, assetId]);
|
}, [assets, assetId]);
|
||||||
|
|
||||||
const tokenContract = useTokenContract(
|
const tokenContract = useTokenContract(
|
||||||
asset?.source.contractAddress,
|
asset?.source.__typename === 'ERC20'
|
||||||
|
? asset.source.contractAddress
|
||||||
|
: undefined,
|
||||||
process.env['NX_VEGA_ENV'] !== 'MAINNET'
|
process.env['NX_VEGA_ENV'] !== 'MAINNET'
|
||||||
);
|
);
|
||||||
const bridgeContract = useBridgeContract();
|
const bridgeContract = useBridgeContract();
|
||||||
|
@ -14,7 +14,7 @@ export const useGetDepositLimits = (
|
|||||||
asset?: Asset
|
asset?: Asset
|
||||||
): Limits | null => {
|
): Limits | null => {
|
||||||
const getLimits = useCallback(async () => {
|
const getLimits = useCallback(async () => {
|
||||||
if (!contract || !asset) {
|
if (!contract || !asset || asset.source.__typename !== 'ERC20') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,23 +1,10 @@
|
|||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import { SHA3 } from 'sha3';
|
import { sha3_256 } from 'js-sha3';
|
||||||
import { remove0x } from './remove-0x';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function creates an ID in the same way that core does on the backend. This way we
|
* 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
|
* Can match up the newly created order with incoming orders via a subscription
|
||||||
*/
|
*/
|
||||||
export const determineId = (sig: string) => {
|
export const determineId = (sig: string) => {
|
||||||
// Prepend 0x
|
return sha3_256(ethers.utils.arrayify('0x' + sig));
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import type {
|
import type {
|
||||||
VegaKey,
|
VegaKey,
|
||||||
TransactionResponse,
|
TransactionResponse,
|
||||||
OrderSubmissionBody,
|
|
||||||
WithdrawSubmissionBody,
|
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
} from '@vegaprotocol/vegawallet-service-api-client';
|
||||||
|
import type { TransactionSubmission } from '../types';
|
||||||
export { RestConnector } from './rest-connector';
|
export { RestConnector } from './rest-connector';
|
||||||
|
|
||||||
type ErrorResponse =
|
type ErrorResponse =
|
||||||
@ -26,6 +25,6 @@ export interface VegaConnector {
|
|||||||
|
|
||||||
/** Send a TX to the network. Only support order submission for now */
|
/** Send a TX to the network. Only support order submission for now */
|
||||||
sendTx: (
|
sendTx: (
|
||||||
body: OrderSubmissionBody | WithdrawSubmissionBody
|
body: TransactionSubmission
|
||||||
) => Promise<TransactionResponse | ErrorResponse>;
|
) => Promise<TransactionResponse | ErrorResponse>;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
import type {
|
import type { Configuration } from '@vegaprotocol/vegawallet-service-api-client';
|
||||||
Configuration,
|
|
||||||
OrderSubmissionBody,
|
|
||||||
WithdrawSubmissionBody,
|
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
|
||||||
import {
|
import {
|
||||||
createConfiguration,
|
createConfiguration,
|
||||||
DefaultApi,
|
DefaultApi,
|
||||||
@ -10,6 +6,7 @@ import {
|
|||||||
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
import { WALLET_CONFIG } from '../storage-keys';
|
import { WALLET_CONFIG } from '../storage-keys';
|
||||||
import type { VegaConnector } from '.';
|
import type { VegaConnector } from '.';
|
||||||
|
import type { TransactionSubmission } from '../types';
|
||||||
|
|
||||||
// Perhaps there should be a default ConnectorConfig that others can extend off. Do all connectors
|
// 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...
|
// 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 {
|
try {
|
||||||
return await this.service.commandSyncPost(body);
|
return await this.service.commandSyncPost(body);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -5,10 +5,7 @@ import type { VegaKeyExtended, VegaWalletContextShape } from '.';
|
|||||||
import type { VegaConnector } from './connectors';
|
import type { VegaConnector } from './connectors';
|
||||||
import { VegaWalletContext } from './context';
|
import { VegaWalletContext } from './context';
|
||||||
import { WALLET_KEY } from './storage-keys';
|
import { WALLET_KEY } from './storage-keys';
|
||||||
import type {
|
import type { TransactionSubmission } from './types';
|
||||||
OrderSubmissionBody,
|
|
||||||
WithdrawSubmissionBody,
|
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
|
||||||
|
|
||||||
interface VegaWalletProviderProps {
|
interface VegaWalletProviderProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -72,16 +69,13 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const sendTx = useCallback(
|
const sendTx = useCallback((body: TransactionSubmission) => {
|
||||||
(body: OrderSubmissionBody | WithdrawSubmissionBody) => {
|
if (!connector.current) {
|
||||||
if (!connector.current) {
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return connector.current.sendTx(body);
|
return connector.current.sendTx(body);
|
||||||
},
|
}, []);
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Current selected keypair derived from publicKey state
|
// Current selected keypair derived from publicKey state
|
||||||
const keypair = useMemo(() => {
|
const keypair = useMemo(() => {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import type {
|
import type {
|
||||||
|
DelegateSubmissionBody,
|
||||||
OrderSubmissionBody,
|
OrderSubmissionBody,
|
||||||
|
UndelegateSubmissionBody,
|
||||||
|
VoteSubmissionBody,
|
||||||
WithdrawSubmissionBody,
|
WithdrawSubmissionBody,
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
} from '@vegaprotocol/vegawallet-service-api-client';
|
||||||
|
|
||||||
@ -25,4 +28,7 @@ export enum OrderTimeInForce {
|
|||||||
// Will make Transaction a union type as other transactions are added
|
// Will make Transaction a union type as other transactions are added
|
||||||
export type TransactionSubmission =
|
export type TransactionSubmission =
|
||||||
| OrderSubmissionBody
|
| 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-complete-withdraw';
|
||||||
export * from './lib/use-withdraw';
|
export * from './lib/use-withdraw';
|
||||||
export * from './lib/use-withdrawals';
|
export * from './lib/use-withdrawals';
|
||||||
|
export * from './lib/__generated__/Withdrawals';
|
||||||
|
@ -11,6 +11,7 @@ export const generateAsset = (override?: PartialDeep<Asset>) => {
|
|||||||
name: 'asset-name',
|
name: 'asset-name',
|
||||||
decimals: 5,
|
decimals: 5,
|
||||||
source: {
|
source: {
|
||||||
|
__typename: 'ERC20',
|
||||||
contractAddress: 'contract-address',
|
contractAddress: 'contract-address',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
import type { AccountType } from '@vegaprotocol/types';
|
import type { AccountType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
interface ERC20AssetSource {
|
||||||
|
__typename: 'ERC20';
|
||||||
|
contractAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BuiltinAssetSource {
|
||||||
|
__typename: 'BuiltinAsset';
|
||||||
|
}
|
||||||
|
|
||||||
|
type AssetSource = ERC20AssetSource | BuiltinAssetSource;
|
||||||
|
|
||||||
export interface Asset {
|
export interface Asset {
|
||||||
id: string;
|
id: string;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
name: string;
|
name: string;
|
||||||
decimals: number;
|
decimals: number;
|
||||||
source: {
|
source: AssetSource;
|
||||||
contractAddress: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Account {
|
export interface Account {
|
||||||
|
@ -90,11 +90,13 @@ export const WithdrawForm = ({
|
|||||||
id="asset"
|
id="asset"
|
||||||
>
|
>
|
||||||
<option value="">{t('Please select')}</option>
|
<option value="">{t('Please select')}</option>
|
||||||
{assets.map((a) => (
|
{assets
|
||||||
<option key={a.id} value={a.id}>
|
.filter((a) => a.source.__typename === 'ERC20')
|
||||||
{a.name}
|
.map((a) => (
|
||||||
</option>
|
<option key={a.id} value={a.id}>
|
||||||
))}
|
{a.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
"@sentry/react": "^6.19.2",
|
"@sentry/react": "^6.19.2",
|
||||||
"@sentry/tracing": "^6.19.2",
|
"@sentry/tracing": "^6.19.2",
|
||||||
"@vegaprotocol/smart-contracts-sdk": "^1.6.0",
|
"@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",
|
"@walletconnect/ethereum-provider": "^1.7.5",
|
||||||
"@web3-react/core": "8.0.20-beta.0",
|
"@web3-react/core": "8.0.20-beta.0",
|
||||||
"@web3-react/metamask": "8.0.16-beta.0",
|
"@web3-react/metamask": "8.0.16-beta.0",
|
||||||
@ -52,6 +52,7 @@
|
|||||||
"i18next": "^20.3.5",
|
"i18next": "^20.3.5",
|
||||||
"i18next-browser-languagedetector": "^6.1.2",
|
"i18next-browser-languagedetector": "^6.1.2",
|
||||||
"immer": "^9.0.12",
|
"immer": "^9.0.12",
|
||||||
|
"js-sha3": "^0.8.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"next": "^12.0.7",
|
"next": "^12.0.7",
|
||||||
"pennant": "0.4.8",
|
"pennant": "0.4.8",
|
||||||
@ -69,7 +70,6 @@
|
|||||||
"react-window-infinite-loader": "^1.0.7",
|
"react-window-infinite-loader": "^1.0.7",
|
||||||
"recharts": "^2.1.2",
|
"recharts": "^2.1.2",
|
||||||
"regenerator-runtime": "0.13.7",
|
"regenerator-runtime": "0.13.7",
|
||||||
"sha3": "^2.1.4",
|
|
||||||
"tailwindcss": "^3.0.23",
|
"tailwindcss": "^3.0.23",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
|
27
yarn.lock
27
yarn.lock
@ -6205,10 +6205,10 @@
|
|||||||
ethers "^5.5.2"
|
ethers "^5.5.2"
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
"@vegaprotocol/vegawallet-service-api-client@^0.4.9":
|
"@vegaprotocol/vegawallet-service-api-client@^0.4.11":
|
||||||
version "0.4.9"
|
version "0.4.11"
|
||||||
resolved "https://registry.yarnpkg.com/@vegaprotocol/vegawallet-service-api-client/-/vegawallet-service-api-client-0.4.9.tgz#9c2566407e9c86248f2c170b1f495afdcb347d87"
|
resolved "https://registry.yarnpkg.com/@vegaprotocol/vegawallet-service-api-client/-/vegawallet-service-api-client-0.4.11.tgz#41a623afc9957dcf8b5425f74280ba7861e92b74"
|
||||||
integrity sha512-zqppuVu3VrsHpdcNBvu6G+qMMt0CBXi5wHBb/0ryiXdXD9dmQci5Qz8zoGx5syOupFMdUDNstmGOaFWbBOuvbg==
|
integrity sha512-yiodc3YFWG+RGG+wjpTjYmNAECP/Nv244mVu8IGVtj8LZo02KC/LpNCgmMhGaK4ZcqVtxHv9t7OUCSEWZhSgOg==
|
||||||
dependencies:
|
dependencies:
|
||||||
es6-promise "^4.2.4"
|
es6-promise "^4.2.4"
|
||||||
url-parse "^1.4.3"
|
url-parse "^1.4.3"
|
||||||
@ -8438,14 +8438,6 @@ buffer@5.6.0:
|
|||||||
base64-js "^1.0.2"
|
base64-js "^1.0.2"
|
||||||
ieee754 "^1.1.4"
|
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:
|
buffer@^4.3.0:
|
||||||
version "4.9.2"
|
version "4.9.2"
|
||||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
|
||||||
@ -13304,7 +13296,7 @@ identity-obj-proxy@3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
harmony-reflect "^1.4.6"
|
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"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||||
@ -14693,7 +14685,7 @@ jest@27.2.3:
|
|||||||
import-local "^3.0.2"
|
import-local "^3.0.2"
|
||||||
jest-cli "^27.2.3"
|
jest-cli "^27.2.3"
|
||||||
|
|
||||||
js-sha3@0.8.0:
|
js-sha3@0.8.0, js-sha3@^0.8.0:
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||||
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
|
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"
|
inherits "^2.0.1"
|
||||||
safe-buffer "^5.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:
|
shallow-clone@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
||||||
|
Loading…
Reference in New Issue
Block a user