Task/Remove vegawallet service api client (#916)
* chore: remove generated vegawallet client code and implement in rest connector * feat: add zod validation * feat: handle specific auth/token delete case * feat: make withdraw dialog match vega tx dialog * fix: response stub to be right shape, add content type to requests * chore: revert unrelated classname change
This commit is contained in:
parent
76efc3f68b
commit
4269060c9c
@ -1,2 +1 @@
|
|||||||
export { VoteDetails } from './vote-details';
|
export { VoteDetails } from './vote-details';
|
||||||
export { VOTE_VALUE_MAP } from './vote-types';
|
|
||||||
|
@ -2,8 +2,7 @@ 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 { VOTE_VALUE_MAP } from './vote-types';
|
import { useVegaWallet, VegaWalletVoteValue } from '@vegaprotocol/wallet';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
|
||||||
|
|
||||||
export type Vote = {
|
export type Vote = {
|
||||||
value: VoteValue;
|
value: VoteValue;
|
||||||
@ -101,7 +100,7 @@ export function useUserVote(
|
|||||||
pubKey: keypair.pub,
|
pubKey: keypair.pub,
|
||||||
propagate: true,
|
propagate: true,
|
||||||
voteSubmission: {
|
voteSubmission: {
|
||||||
value: VOTE_VALUE_MAP[value],
|
value: VegaWalletVoteValue[value],
|
||||||
proposalId,
|
proposalId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
import { VoteValue } from '../../../../__generated__/globalTypes';
|
|
||||||
|
|
||||||
export const VOTE_VALUE_MAP = {
|
|
||||||
[VoteValue.Yes]: 'VALUE_YES',
|
|
||||||
[VoteValue.No]: 'VALUE_NO',
|
|
||||||
} as const;
|
|
@ -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 type { UndelegateSubmissionBody } from '@vegaprotocol/wallet';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import type { UndelegateSubmissionBody } from '@vegaprotocol/vegawallet-service-api-client';
|
|
||||||
|
|
||||||
interface PendingStakeProps {
|
interface PendingStakeProps {
|
||||||
pendingAmount: BigNumber;
|
pendingAmount: BigNumber;
|
||||||
|
@ -26,11 +26,11 @@ import {
|
|||||||
Radio,
|
Radio,
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
|
||||||
import type {
|
import type {
|
||||||
DelegateSubmissionBody,
|
DelegateSubmissionBody,
|
||||||
UndelegateSubmissionBody,
|
UndelegateSubmissionBody,
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
} from '@vegaprotocol/wallet';
|
||||||
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
export const PARTY_DELEGATIONS_QUERY = gql`
|
export const PARTY_DELEGATIONS_QUERY = gql`
|
||||||
query PartyDelegations($partyId: ID!) {
|
query PartyDelegations($partyId: ID!) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
import type { TransactionResponse } from '@vegaprotocol/vegawallet-service-api-client';
|
import type { TransactionResponse } from '@vegaprotocol/wallet';
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@ -17,7 +17,10 @@ export function addMockVegaWalletCommands() {
|
|||||||
'mockVegaCommandSync',
|
'mockVegaCommandSync',
|
||||||
(override?: PartialDeep<TransactionResponse>) => {
|
(override?: PartialDeep<TransactionResponse>) => {
|
||||||
const defaultTransactionResponse = {
|
const defaultTransactionResponse = {
|
||||||
|
txId: 'tx-id',
|
||||||
txHash: 'tx-hash',
|
txHash: 'tx-hash',
|
||||||
|
sentAt: new Date().toISOString(),
|
||||||
|
receivedAt: new Date().toISOString(),
|
||||||
tx: {
|
tx: {
|
||||||
input_data:
|
input_data:
|
||||||
'CPe6vpiqsPqxDBDC1w7KPkoKQGE4Y2M0NjUwMjhiMGY4OTM4YTYzZTEzNDViYzM2ODc3ZWRmODg4MjNmOWU0ZmI4ZDRlN2VkMmFlMzAwNzA3ZTMYASABKAM4Ag==',
|
'CPe6vpiqsPqxDBDC1w7KPkoKQGE4Y2M0NjUwMjhiMGY4OTM4YTYzZTEzNDViYzM2ODc3ZWRmODg4MjNmOWU0ZmI4ZDRlN2VkMmFlMzAwNzA3ZTMYASABKAM4Ag==',
|
||||||
@ -28,7 +31,7 @@ export function addMockVegaWalletCommands() {
|
|||||||
version: 1,
|
version: 1,
|
||||||
},
|
},
|
||||||
From: {
|
From: {
|
||||||
PubKey: Cypress.env('vegaPublicKey'),
|
PubKey: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||||
},
|
},
|
||||||
version: 2,
|
version: 2,
|
||||||
pow: {
|
pow: {
|
||||||
|
@ -51,14 +51,11 @@ export const useOrderEdit = (order: OrderFields | null) => {
|
|||||||
orderAmendment: {
|
orderAmendment: {
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
marketId: order.market.id,
|
marketId: order.market.id,
|
||||||
// @ts-ignore fix me please!
|
|
||||||
price: {
|
price: {
|
||||||
value: removeDecimal(args.price, order.market.decimalPlaces),
|
value: removeDecimal(args.price, order.market.decimalPlaces),
|
||||||
},
|
},
|
||||||
timeInForce: VegaWalletOrderTimeInForce[order.timeInForce],
|
timeInForce: VegaWalletOrderTimeInForce[order.timeInForce],
|
||||||
// @ts-ignore fix me please!
|
|
||||||
sizeDelta: 0,
|
sizeDelta: 0,
|
||||||
// @ts-ignore fix me please!
|
|
||||||
expiresAt: order.expiresAt
|
expiresAt: order.expiresAt
|
||||||
? {
|
? {
|
||||||
value: toNanoSeconds(new Date(order.expiresAt)), // Wallet expects timestamp in nanoseconds
|
value: toNanoSeconds(new Date(order.expiresAt)), // Wallet expects timestamp in nanoseconds
|
||||||
|
@ -1,43 +1,80 @@
|
|||||||
import type { Configuration } from '@vegaprotocol/vegawallet-service-api-client';
|
|
||||||
import {
|
|
||||||
createConfiguration,
|
|
||||||
ServerConfiguration,
|
|
||||||
DefaultApi,
|
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
|
||||||
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 './vega-connector';
|
import type { VegaConnector } from './vega-connector';
|
||||||
import type { TransactionSubmission } from '../wallet-types';
|
import type { TransactionSubmission } from '../wallet-types';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
// 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...
|
||||||
interface RestConnectorConfig {
|
interface RestConnectorConfig {
|
||||||
token: string | null;
|
token: string | null;
|
||||||
connector: 'rest';
|
connector: 'rest';
|
||||||
|
url: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Endpoint = 'auth/token' | 'command/sync' | 'keys';
|
||||||
|
|
||||||
|
export const AuthTokenSchema = z.object({
|
||||||
|
token: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const TransactionResponseSchema = z.object({
|
||||||
|
txId: z.string(),
|
||||||
|
txHash: z.string(),
|
||||||
|
tx: z.object({
|
||||||
|
From: z.object({
|
||||||
|
PubKey: z.string(),
|
||||||
|
}),
|
||||||
|
input_data: z.string(),
|
||||||
|
pow: z.object({
|
||||||
|
tid: z.string(),
|
||||||
|
nonce: z.number(),
|
||||||
|
}),
|
||||||
|
signature: z.object({
|
||||||
|
algo: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
version: z.number(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
sentAt: z.string(),
|
||||||
|
receivedAt: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GetKeysSchema = z.object({
|
||||||
|
keys: z.array(
|
||||||
|
z.object({
|
||||||
|
algorithm: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
version: z.number(),
|
||||||
|
}),
|
||||||
|
index: z.number(),
|
||||||
|
meta: z.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
pub: z.string(),
|
||||||
|
tainted: z.boolean(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connector for using the Vega Wallet Service rest api, requires authentication to get a session token
|
* Connector for using the Vega Wallet Service rest api, requires authentication to get a session token
|
||||||
*/
|
*/
|
||||||
export class RestConnector implements VegaConnector {
|
export class RestConnector implements VegaConnector {
|
||||||
configKey = WALLET_CONFIG;
|
configKey = WALLET_CONFIG;
|
||||||
apiConfig: Configuration;
|
|
||||||
service: DefaultApi;
|
|
||||||
description = 'Connects using REST to a running Vega wallet service';
|
description = 'Connects using REST to a running Vega wallet service';
|
||||||
|
url: string | null = null;
|
||||||
|
token: string | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const cfg = this.getConfig();
|
const cfg = this.getConfig();
|
||||||
|
if (cfg) {
|
||||||
// If theres a stored auth token create api config with bearer authMethod
|
this.token = cfg.token;
|
||||||
this.apiConfig = cfg?.token
|
this.url = cfg.url;
|
||||||
? createConfiguration({
|
}
|
||||||
authMethods: {
|
|
||||||
bearer: `Bearer ${cfg.token}`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
: createConfiguration();
|
|
||||||
|
|
||||||
this.service = new DefaultApi(this.apiConfig);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticate(
|
async authenticate(
|
||||||
@ -48,26 +85,22 @@ export class RestConnector implements VegaConnector {
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const service = new DefaultApi(
|
this.url = url;
|
||||||
createConfiguration({
|
|
||||||
baseServer: new ServerConfiguration<Record<string, never>>(url, {}),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const res = await service.authTokenPost(params);
|
const res = await this.request('auth/token', {
|
||||||
|
method: 'post',
|
||||||
|
body: JSON.stringify(params),
|
||||||
|
});
|
||||||
|
|
||||||
// Renew service instance with default bearer authMethod now that we have the token
|
const data = AuthTokenSchema.parse(res.data);
|
||||||
this.service = new DefaultApi(
|
|
||||||
createConfiguration({
|
|
||||||
baseServer: new ServerConfiguration<Record<string, never>>(url, {}),
|
|
||||||
authMethods: {
|
|
||||||
bearer: `Bearer ${res.token}`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Store the token, and other things for later
|
// Store the token, and other things for later
|
||||||
this.setConfig({ connector: 'rest', token: res.token });
|
this.setConfig({
|
||||||
|
connector: 'rest',
|
||||||
|
token: data.token,
|
||||||
|
url: this.url,
|
||||||
|
});
|
||||||
|
this.token = data.token;
|
||||||
|
|
||||||
return { success: true, error: null };
|
return { success: true, error: null };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -77,10 +110,17 @@ export class RestConnector implements VegaConnector {
|
|||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
try {
|
try {
|
||||||
const res = await this.service.keysGet();
|
const res = await this.request('keys', {
|
||||||
return res.keys;
|
method: 'get',
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${this.token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = GetKeysSchema.parse(res.data);
|
||||||
|
|
||||||
|
return data.keys;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
|
||||||
// keysGet failed, its likely that the session has expired so remove the token from storage
|
// keysGet failed, its likely that the session has expired so remove the token from storage
|
||||||
this.clearConfig();
|
this.clearConfig();
|
||||||
return null;
|
return null;
|
||||||
@ -89,7 +129,12 @@ export class RestConnector implements VegaConnector {
|
|||||||
|
|
||||||
async disconnect() {
|
async disconnect() {
|
||||||
try {
|
try {
|
||||||
await this.service.authTokenDelete();
|
await this.request('auth/token', {
|
||||||
|
method: 'delete',
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${this.token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} finally {
|
} finally {
|
||||||
@ -101,28 +146,26 @@ export class RestConnector implements VegaConnector {
|
|||||||
|
|
||||||
async sendTx(body: TransactionSubmission) {
|
async sendTx(body: TransactionSubmission) {
|
||||||
try {
|
try {
|
||||||
const res = await this.service.commandSyncPost(body);
|
const res = await this.request('command/sync', {
|
||||||
return res;
|
method: 'post',
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${this.token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 401) {
|
||||||
|
// User rejected
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = TransactionResponseSchema.parse(res.data);
|
||||||
|
|
||||||
|
return data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return this.handleSendTxError(err);
|
return {
|
||||||
}
|
error: 'Failed to fetch',
|
||||||
}
|
};
|
||||||
|
|
||||||
private handleSendTxError(err: unknown) {
|
|
||||||
const unexpectedError = { error: 'Something went wrong' };
|
|
||||||
|
|
||||||
if (isServiceError(err)) {
|
|
||||||
if (err.code === 401) {
|
|
||||||
return { error: 'User rejected' };
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return JSON.parse(err.body ?? '');
|
|
||||||
} catch {
|
|
||||||
return unexpectedError;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return unexpectedError;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,18 +189,29 @@ export class RestConnector implements VegaConnector {
|
|||||||
private clearConfig() {
|
private clearConfig() {
|
||||||
LocalStorage.removeItem(this.configKey);
|
LocalStorage.removeItem(this.configKey);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
interface ServiceError {
|
private async request(endpoint: Endpoint, options: RequestInit) {
|
||||||
code: number;
|
const fetchResult = await fetch(`${this.url}/${endpoint}`, {
|
||||||
body: string | undefined;
|
...options,
|
||||||
headers: object;
|
headers: {
|
||||||
}
|
...options.headers,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const isServiceError = (err: unknown): err is ServiceError => {
|
// auth/token delete doesnt return json
|
||||||
// Some responses don't contain body object
|
if (endpoint === 'auth/token' && options.method === 'delete') {
|
||||||
if (typeof err === 'object' && err !== null && 'code' in err) {
|
const textResult = await fetchResult.text();
|
||||||
return true;
|
return {
|
||||||
|
status: fetchResult.status,
|
||||||
|
data: textResult,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const jsonResult = await fetchResult.json();
|
||||||
|
return {
|
||||||
|
status: fetchResult.status,
|
||||||
|
data: jsonResult,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
};
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import type { VegaKey } from '../wallet-types';
|
||||||
import type {
|
import type {
|
||||||
VegaKey,
|
TransactionSubmission,
|
||||||
TransactionResponse,
|
TransactionResponse,
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
} from '../wallet-types';
|
||||||
import type { TransactionSubmission } from '../wallet-types';
|
|
||||||
|
|
||||||
type ErrorResponse =
|
type ErrorResponse =
|
||||||
| {
|
| {
|
||||||
|
@ -1,18 +1,10 @@
|
|||||||
import type {
|
import type { TransactionError, VegaKey } from './wallet-types';
|
||||||
VegaKey,
|
|
||||||
TransactionResponse,
|
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
|
||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import type { VegaConnector } from './connectors';
|
import type { VegaConnector } from './connectors';
|
||||||
import type { TransactionSubmission } from './wallet-types';
|
import type {
|
||||||
|
TransactionSubmission,
|
||||||
export type SendTxError =
|
TransactionResponse,
|
||||||
| {
|
} from './wallet-types';
|
||||||
error: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
errors: object;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface VegaKeyExtended extends VegaKey {
|
export interface VegaKeyExtended extends VegaKey {
|
||||||
name: string;
|
name: string;
|
||||||
@ -40,7 +32,7 @@ export interface VegaWalletContextShape {
|
|||||||
/** Send a transaction to the network, only order submissions for now */
|
/** Send a transaction to the network, only order submissions for now */
|
||||||
sendTx: (
|
sendTx: (
|
||||||
tx: TransactionSubmission
|
tx: TransactionSubmission
|
||||||
) => Promise<TransactionResponse | SendTxError> | null;
|
) => Promise<TransactionResponse | TransactionError> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VegaWalletContext = createContext<
|
export const VegaWalletContext = createContext<
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
import { act, fireEvent, render, screen } from '@testing-library/react';
|
||||||
import type { VegaKey } from '@vegaprotocol/vegawallet-service-api-client';
|
import type { VegaKey } from './wallet-types';
|
||||||
import { RestConnector } from './connectors';
|
import { RestConnector } from './connectors';
|
||||||
import { useVegaWallet } from './use-vega-wallet';
|
import { useVegaWallet } from './use-vega-wallet';
|
||||||
import { VegaWalletProvider } from './provider';
|
import { VegaWalletProvider } from './provider';
|
||||||
|
@ -3,7 +3,7 @@ import type { VegaWalletContextShape } from './context';
|
|||||||
import { VegaWalletContext } from './context';
|
import { VegaWalletContext } from './context';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { useVegaTransaction, VegaTxStatus } from './use-vega-transaction';
|
import { useVegaTransaction, VegaTxStatus } from './use-vega-transaction';
|
||||||
import type { OrderSubmissionBody } from '@vegaprotocol/vegawallet-service-api-client';
|
import type { OrderSubmissionBody } from './wallet-types';
|
||||||
|
|
||||||
const defaultWalletContext = {
|
const defaultWalletContext = {
|
||||||
keypair: null,
|
keypair: null,
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import type { TransactionSubmission } from './wallet-types';
|
import type { TransactionError, TransactionSubmission } from './wallet-types';
|
||||||
import { useVegaWallet } from './use-vega-wallet';
|
import { useVegaWallet } from './use-vega-wallet';
|
||||||
import type { SendTxError } from './context';
|
|
||||||
import { VegaTransactionDialog } from './vega-transaction-dialog';
|
import { VegaTransactionDialog } from './vega-transaction-dialog';
|
||||||
import type { Intent } from '@vegaprotocol/ui-toolkit';
|
import type { Intent } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ export enum VegaTxStatus {
|
|||||||
|
|
||||||
export interface VegaTxState {
|
export interface VegaTxState {
|
||||||
status: VegaTxStatus;
|
status: VegaTxStatus;
|
||||||
error: object | null;
|
error: TransactionError | null;
|
||||||
txHash: string | null;
|
txHash: string | null;
|
||||||
signature: string | null;
|
signature: string | null;
|
||||||
dialogOpen: boolean;
|
dialogOpen: boolean;
|
||||||
@ -49,7 +48,7 @@ export const useVegaTransaction = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleError = useCallback(
|
const handleError = useCallback(
|
||||||
(error: SendTxError) => {
|
(error: TransactionError) => {
|
||||||
setTransaction({ error, status: VegaTxStatus.Error });
|
setTransaction({ error, status: VegaTxStatus.Error });
|
||||||
},
|
},
|
||||||
[setTransaction]
|
[setTransaction]
|
||||||
@ -76,23 +75,23 @@ export const useVegaTransaction = () => {
|
|||||||
const res = await sendTx(tx);
|
const res = await sendTx(tx);
|
||||||
|
|
||||||
if (res === null) {
|
if (res === null) {
|
||||||
setTransaction({ status: VegaTxStatus.Default });
|
// User rejected
|
||||||
return null;
|
reset();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if ('errors' in res) {
|
|
||||||
|
if (isError(res)) {
|
||||||
handleError(res);
|
handleError(res);
|
||||||
} else if ('error' in res) {
|
return;
|
||||||
if (res.error === 'User rejected') {
|
}
|
||||||
reset();
|
|
||||||
} else {
|
if (res.tx?.signature?.value && res.txHash) {
|
||||||
handleError(res);
|
|
||||||
}
|
|
||||||
} else if (res.tx?.signature?.value && res.txHash) {
|
|
||||||
setTransaction({
|
setTransaction({
|
||||||
status: VegaTxStatus.Pending,
|
status: VegaTxStatus.Pending,
|
||||||
txHash: res.txHash,
|
txHash: res.txHash,
|
||||||
signature: res.tx.signature.value,
|
signature: res.tx.signature.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
signature: res.tx.signature?.value,
|
signature: res.tx.signature?.value,
|
||||||
};
|
};
|
||||||
@ -125,3 +124,14 @@ export const useVegaTransaction = () => {
|
|||||||
TransactionDialog,
|
TransactionDialog,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isError = (error: unknown): error is TransactionError => {
|
||||||
|
if (
|
||||||
|
error !== null &&
|
||||||
|
typeof error === 'object' &&
|
||||||
|
('error' in error || 'errors' in error)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
@ -57,7 +57,7 @@ describe('VegaTransactionDialog', () => {
|
|||||||
{...props}
|
{...props}
|
||||||
transaction={{
|
transaction={{
|
||||||
...props.transaction,
|
...props.transaction,
|
||||||
error: { message: 'rejected' },
|
error: { error: 'rejected' },
|
||||||
status: VegaTxStatus.Error,
|
status: VegaTxStatus.Error,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import get from 'lodash/get';
|
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
|
import { Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
@ -71,16 +70,18 @@ export const VegaDialog = ({ transaction }: VegaDialogProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (transaction.status === VegaTxStatus.Error) {
|
if (transaction.status === VegaTxStatus.Error) {
|
||||||
return (
|
let content = null;
|
||||||
<div data-testid={transaction.status}>
|
|
||||||
{transaction.error && (
|
if (transaction.error) {
|
||||||
<pre className="text-ui break-all whitespace-pre-wrap">
|
if ('errors' in transaction.error) {
|
||||||
{get(transaction.error, 'error') ??
|
content = transaction.error.errors['*'].map((e) => <p>{e}</p>);
|
||||||
JSON.stringify(transaction.error, null, 2)}
|
} else if ('error' in transaction.error) {
|
||||||
</pre>
|
content = <p>{transaction.error.error}</p>;
|
||||||
)}
|
} else {
|
||||||
</div>
|
content = <p>{t('Something went wrong')}</p>;
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
return <div data-testid={transaction.status}>{content}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transaction.status === VegaTxStatus.Pending) {
|
if (transaction.status === VegaTxStatus.Pending) {
|
||||||
|
@ -1,12 +1,88 @@
|
|||||||
import type {
|
import type { z } from 'zod';
|
||||||
DelegateSubmissionBody,
|
import type { GetKeysSchema, TransactionResponseSchema } from './connectors';
|
||||||
OrderCancellationBody,
|
import type { IterableElement } from 'type-fest';
|
||||||
OrderSubmissionBody,
|
|
||||||
UndelegateSubmissionBody,
|
interface BaseTransaction {
|
||||||
VoteSubmissionBody,
|
pubKey: string;
|
||||||
WithdrawSubmissionBody,
|
propagate: boolean;
|
||||||
OrderAmendmentBody,
|
}
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
|
||||||
|
export interface DelegateSubmissionBody extends BaseTransaction {
|
||||||
|
delegateSubmission: {
|
||||||
|
nodeId: string;
|
||||||
|
amount: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UndelegateSubmissionBody extends BaseTransaction {
|
||||||
|
undelegateSubmission: {
|
||||||
|
nodeId: string;
|
||||||
|
amount: string;
|
||||||
|
method: 'METHOD_NOW' | 'METHOD_AT_END_OF_EPOCH';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderSubmissionBody extends BaseTransaction {
|
||||||
|
orderSubmission: {
|
||||||
|
marketId: string;
|
||||||
|
reference?: string;
|
||||||
|
type: VegaWalletOrderType;
|
||||||
|
side: VegaWalletOrderSide;
|
||||||
|
timeInForce: VegaWalletOrderTimeInForce;
|
||||||
|
size: string;
|
||||||
|
price?: string;
|
||||||
|
expiresAt?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderCancellationBody extends BaseTransaction {
|
||||||
|
orderCancellation: {
|
||||||
|
orderId: string;
|
||||||
|
marketId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrderAmendmentBody extends BaseTransaction {
|
||||||
|
orderAmendment: {
|
||||||
|
marketId: string;
|
||||||
|
orderId: string;
|
||||||
|
reference?: string;
|
||||||
|
timeInForce: VegaWalletOrderTimeInForce;
|
||||||
|
sizeDelta?: number;
|
||||||
|
// Note this is soon changing to price?: string
|
||||||
|
price?: {
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
// Note this is soon changing to expiresAt?: number
|
||||||
|
expiresAt?: {
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VoteSubmissionBody extends BaseTransaction {
|
||||||
|
voteSubmission: {
|
||||||
|
value: VegaWalletVoteValue;
|
||||||
|
proposalId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WithdrawSubmissionBody extends BaseTransaction {
|
||||||
|
withdrawSubmission: {
|
||||||
|
amount: string;
|
||||||
|
asset: string;
|
||||||
|
ext: {
|
||||||
|
erc20: {
|
||||||
|
receiverAddress: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VegaWalletVoteValue {
|
||||||
|
Yes = 'VALUE_YES',
|
||||||
|
No = 'VALUE_NO',
|
||||||
|
}
|
||||||
|
|
||||||
export enum VegaWalletOrderType {
|
export enum VegaWalletOrderType {
|
||||||
Market = 'TYPE_MARKET',
|
Market = 'TYPE_MARKET',
|
||||||
@ -36,3 +112,17 @@ export type TransactionSubmission =
|
|||||||
| DelegateSubmissionBody
|
| DelegateSubmissionBody
|
||||||
| UndelegateSubmissionBody
|
| UndelegateSubmissionBody
|
||||||
| OrderAmendmentBody;
|
| OrderAmendmentBody;
|
||||||
|
|
||||||
|
export type TransactionResponse = z.infer<typeof TransactionResponseSchema>;
|
||||||
|
export type GetKeysResponse = z.infer<typeof GetKeysSchema>;
|
||||||
|
export type VegaKey = IterableElement<GetKeysResponse['keys']>;
|
||||||
|
|
||||||
|
export type TransactionError =
|
||||||
|
| {
|
||||||
|
errors: {
|
||||||
|
'*': string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
error: string;
|
||||||
|
};
|
||||||
|
@ -69,6 +69,18 @@ const getProps = (
|
|||||||
ethTx: EthTxState,
|
ethTx: EthTxState,
|
||||||
ethUrl: string
|
ethUrl: string
|
||||||
) => {
|
) => {
|
||||||
|
const renderVegaTxError = () => {
|
||||||
|
if (vegaTx.error) {
|
||||||
|
if ('errors' in vegaTx.error) {
|
||||||
|
return vegaTx.error.errors['*'].map((e) => <p>{e}</p>);
|
||||||
|
} else if ('error' in vegaTx.error) {
|
||||||
|
return <p>{vegaTx.error.error}</p>;
|
||||||
|
} else {
|
||||||
|
return <p>{t('Something went wrong')}</p>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
const vegaTxPropsMap: Record<VegaTxStatus, DialogProps> = {
|
const vegaTxPropsMap: Record<VegaTxStatus, DialogProps> = {
|
||||||
[VegaTxStatus.Default]: {
|
[VegaTxStatus.Default]: {
|
||||||
title: '',
|
title: '',
|
||||||
@ -80,15 +92,7 @@ const getProps = (
|
|||||||
title: t('Withdrawal transaction failed'),
|
title: t('Withdrawal transaction failed'),
|
||||||
icon: <Icon name="warning-sign" size={20} />,
|
icon: <Icon name="warning-sign" size={20} />,
|
||||||
intent: Intent.Danger,
|
intent: Intent.Danger,
|
||||||
children: (
|
children: <Step>{renderVegaTxError()}</Step>,
|
||||||
<Step>
|
|
||||||
{vegaTx.error && (
|
|
||||||
<pre className="text-ui break-all whitespace-pre-wrap">
|
|
||||||
{JSON.stringify(vegaTx.error, null, 2)}
|
|
||||||
</pre>
|
|
||||||
)}
|
|
||||||
</Step>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
[VegaTxStatus.Requested]: {
|
[VegaTxStatus.Requested]: {
|
||||||
title: t('Confirm withdrawal'),
|
title: t('Confirm withdrawal'),
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
"@sentry/react": "^6.19.2",
|
"@sentry/react": "^6.19.2",
|
||||||
"@sentry/tracing": "^6.19.2",
|
"@sentry/tracing": "^6.19.2",
|
||||||
"@testing-library/user-event": "^14.2.1",
|
"@testing-library/user-event": "^14.2.1",
|
||||||
"@vegaprotocol/vegawallet-service-api-client": "0.4.15",
|
|
||||||
"@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",
|
||||||
|
32
yarn.lock
32
yarn.lock
@ -6693,15 +6693,6 @@
|
|||||||
"@typescript-eslint/types" "5.22.0"
|
"@typescript-eslint/types" "5.22.0"
|
||||||
eslint-visitor-keys "^3.0.0"
|
eslint-visitor-keys "^3.0.0"
|
||||||
|
|
||||||
"@vegaprotocol/vegawallet-service-api-client@0.4.15":
|
|
||||||
version "0.4.15"
|
|
||||||
resolved "https://registry.yarnpkg.com/@vegaprotocol/vegawallet-service-api-client/-/vegawallet-service-api-client-0.4.15.tgz#b303fec121b9b334a678161a6f66b360aeed5f0d"
|
|
||||||
integrity sha512-YwJkUgFvFqpA1xPYQ30ILGddgzjwD9lclsu1GvwK2AUX/8e3iUcXyr37wLd/t8mDZ7P3Zb2AsuLJP8uZ6E1GHQ==
|
|
||||||
dependencies:
|
|
||||||
es6-promise "^4.2.4"
|
|
||||||
url-parse "^1.4.3"
|
|
||||||
whatwg-fetch "^3.0.0"
|
|
||||||
|
|
||||||
"@walletconnect/browser-utils@^1.7.7":
|
"@walletconnect/browser-utils@^1.7.7":
|
||||||
version "1.7.7"
|
version "1.7.7"
|
||||||
resolved "https://registry.yarnpkg.com/@walletconnect/browser-utils/-/browser-utils-1.7.7.tgz#4ae0db1ddf49be179ea556af842db3b7afce973d"
|
resolved "https://registry.yarnpkg.com/@walletconnect/browser-utils/-/browser-utils-1.7.7.tgz#4ae0db1ddf49be179ea556af842db3b7afce973d"
|
||||||
@ -11543,11 +11534,6 @@ es6-object-assign@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
||||||
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
||||||
|
|
||||||
es6-promise@^4.2.4:
|
|
||||||
version "4.2.8"
|
|
||||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
|
||||||
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
|
||||||
|
|
||||||
es6-shim@^0.35.5:
|
es6-shim@^0.35.5:
|
||||||
version "0.35.6"
|
version "0.35.6"
|
||||||
resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0"
|
resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0"
|
||||||
@ -18378,11 +18364,6 @@ querystring@^0.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd"
|
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd"
|
||||||
integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==
|
integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==
|
||||||
|
|
||||||
querystringify@^2.1.1:
|
|
||||||
version "2.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
|
||||||
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
|
|
||||||
|
|
||||||
queue-microtask@^1.2.2:
|
queue-microtask@^1.2.2:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||||
@ -21666,14 +21647,6 @@ url-loader@^4.1.1:
|
|||||||
mime-types "^2.1.27"
|
mime-types "^2.1.27"
|
||||||
schema-utils "^3.0.0"
|
schema-utils "^3.0.0"
|
||||||
|
|
||||||
url-parse@^1.4.3:
|
|
||||||
version "1.5.10"
|
|
||||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
|
|
||||||
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
|
|
||||||
dependencies:
|
|
||||||
querystringify "^2.1.1"
|
|
||||||
requires-port "^1.0.0"
|
|
||||||
|
|
||||||
url@^0.11.0, url@~0.11.0:
|
url@^0.11.0, url@~0.11.0:
|
||||||
version "0.11.0"
|
version "0.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
|
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
|
||||||
@ -22293,11 +22266,6 @@ whatwg-encoding@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
iconv-lite "0.6.3"
|
iconv-lite "0.6.3"
|
||||||
|
|
||||||
whatwg-fetch@^3.0.0:
|
|
||||||
version "3.6.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
|
|
||||||
integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
|
|
||||||
|
|
||||||
whatwg-mimetype@^2.3.0:
|
whatwg-mimetype@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
||||||
|
Loading…
Reference in New Issue
Block a user