fix(wallet): fetch new keys without re-connecting the wallet (#3467)
This commit is contained in:
parent
e4a51061a3
commit
911e927ab0
@ -23,7 +23,8 @@ const generateJsx = (context: VegaWalletContextShape) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
it('Not connected', () => {
|
describe('VegaWalletConnectButton', () => {
|
||||||
|
it('should fire dialog when not connected', () => {
|
||||||
render(generateJsx({ pubKey: null } as VegaWalletContextShape));
|
render(generateJsx({ pubKey: null } as VegaWalletContextShape));
|
||||||
|
|
||||||
const button = screen.getByTestId('connect-vega-wallet');
|
const button = screen.getByTestId('connect-vega-wallet');
|
||||||
@ -32,12 +33,14 @@ it('Not connected', () => {
|
|||||||
expect(mockUpdateDialogOpen).toHaveBeenCalled();
|
expect(mockUpdateDialogOpen).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Connected', async () => {
|
it('should retrieve keys when connected', async () => {
|
||||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||||
|
const pubKey2 = { publicKey: '123456__123457', name: 'test2' };
|
||||||
render(
|
render(
|
||||||
generateJsx({
|
generateJsx({
|
||||||
pubKey: pubKey.publicKey,
|
pubKey: pubKey.publicKey,
|
||||||
pubKeys: [pubKey],
|
pubKeys: [pubKey],
|
||||||
|
fetchPubKeys: () => Promise.resolve([pubKey, pubKey2]),
|
||||||
} as VegaWalletContextShape)
|
} as VegaWalletContextShape)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -46,3 +49,18 @@ it('Connected', async () => {
|
|||||||
userEvent.click(button);
|
userEvent.click(button);
|
||||||
expect(mockUpdateDialogOpen).not.toHaveBeenCalled();
|
expect(mockUpdateDialogOpen).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fetch keys when connected', async () => {
|
||||||
|
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||||
|
const context = {
|
||||||
|
pubKey: pubKey.publicKey,
|
||||||
|
pubKeys: [pubKey],
|
||||||
|
} as VegaWalletContextShape;
|
||||||
|
render(generateJsx(context));
|
||||||
|
|
||||||
|
const button = screen.getByTestId('manage-vega-wallet');
|
||||||
|
expect(button).toHaveTextContent(truncateByChars(pubKey.publicKey));
|
||||||
|
userEvent.click(button);
|
||||||
|
expect(mockUpdateDialogOpen).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -29,7 +29,7 @@ const MobileWalletButton = ({
|
|||||||
isConnected?: boolean;
|
isConnected?: boolean;
|
||||||
activeKey?: PubKey;
|
activeKey?: PubKey;
|
||||||
}) => {
|
}) => {
|
||||||
const { pubKeys, selectPubKey, disconnect } = useVegaWallet();
|
const { pubKeys, selectPubKey, disconnect, fetchPubKeys } = useVegaWallet();
|
||||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||||
(store) => store.openVegaWalletDialog
|
(store) => store.openVegaWalletDialog
|
||||||
);
|
);
|
||||||
@ -46,9 +46,12 @@ const MobileWalletButton = ({
|
|||||||
openVegaWalletDialog();
|
openVegaWalletDialog();
|
||||||
setDrawerOpen(false);
|
setDrawerOpen(false);
|
||||||
} else {
|
} else {
|
||||||
|
if (fetchPubKeys) {
|
||||||
|
fetchPubKeys();
|
||||||
|
}
|
||||||
setDrawerOpen(!drawerOpen);
|
setDrawerOpen(!drawerOpen);
|
||||||
}
|
}
|
||||||
}, [drawerOpen, isConnected, openVegaWalletDialog]);
|
}, [drawerOpen, fetchPubKeys, isConnected, openVegaWalletDialog]);
|
||||||
|
|
||||||
const iconClass = drawerOpen
|
const iconClass = drawerOpen
|
||||||
? 'hidden'
|
? 'hidden'
|
||||||
@ -145,8 +148,14 @@ export const VegaWalletConnectButton = () => {
|
|||||||
(store) => store.openVegaWalletDialog
|
(store) => store.openVegaWalletDialog
|
||||||
);
|
);
|
||||||
const openTransferDialog = useTransferDialog((store) => store.open);
|
const openTransferDialog = useTransferDialog((store) => store.open);
|
||||||
const { pubKey, pubKeys, selectPubKey, disconnect, isReadOnly } =
|
const {
|
||||||
useVegaWallet();
|
pubKey,
|
||||||
|
pubKeys,
|
||||||
|
selectPubKey,
|
||||||
|
disconnect,
|
||||||
|
isReadOnly,
|
||||||
|
fetchPubKeys,
|
||||||
|
} = useVegaWallet();
|
||||||
const isConnected = pubKey !== null;
|
const isConnected = pubKey !== null;
|
||||||
|
|
||||||
const activeKey = useMemo(() => {
|
const activeKey = useMemo(() => {
|
||||||
@ -162,7 +171,12 @@ export const VegaWalletConnectButton = () => {
|
|||||||
trigger={
|
trigger={
|
||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger
|
||||||
data-testid="manage-vega-wallet"
|
data-testid="manage-vega-wallet"
|
||||||
onClick={() => setDropdownOpen((curr) => !curr)}
|
onClick={() => {
|
||||||
|
if (fetchPubKeys) {
|
||||||
|
fetchPubKeys();
|
||||||
|
}
|
||||||
|
setDropdownOpen(!dropdownOpen);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{activeKey && (
|
{activeKey && (
|
||||||
<span className="uppercase">{activeKey.name}</span>
|
<span className="uppercase">{activeKey.name}</span>
|
||||||
|
@ -31,6 +31,9 @@ export interface VegaWalletContextShape {
|
|||||||
/** Transaction payload */
|
/** Transaction payload */
|
||||||
transaction: Transaction
|
transaction: Transaction
|
||||||
) => Promise<TransactionResponse | null>;
|
) => Promise<TransactionResponse | null>;
|
||||||
|
|
||||||
|
/** Fetch public keys */
|
||||||
|
fetchPubKeys?: () => Promise<PubKey[] | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VegaWalletContext = createContext<
|
export const VegaWalletContext = createContext<
|
||||||
|
@ -50,6 +50,7 @@ describe('VegaWalletProvider', () => {
|
|||||||
connect: expect.any(Function),
|
connect: expect.any(Function),
|
||||||
disconnect: expect.any(Function),
|
disconnect: expect.any(Function),
|
||||||
sendTx: expect.any(Function),
|
sendTx: expect.any(Function),
|
||||||
|
fetchPubKeys: expect.any(Function || undefined),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect
|
// Connect
|
||||||
@ -77,14 +78,47 @@ describe('VegaWalletProvider', () => {
|
|||||||
expect(spyOnSend).toHaveBeenCalledWith(mockPubKeys[1].publicKey, {});
|
expect(spyOnSend).toHaveBeenCalledWith(mockPubKeys[1].publicKey, {});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fetch new keypairs', async () => {
|
||||||
|
const { result } = setup();
|
||||||
|
|
||||||
|
// Default state
|
||||||
|
expect(result.current).toEqual({
|
||||||
|
pubKey: null,
|
||||||
|
pubKeys: null,
|
||||||
|
isReadOnly: false,
|
||||||
|
selectPubKey: expect.any(Function),
|
||||||
|
connect: expect.any(Function),
|
||||||
|
disconnect: expect.any(Function),
|
||||||
|
sendTx: expect.any(Function),
|
||||||
|
fetchPubKeys: expect.any(Function),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
await act(async () => {
|
||||||
|
result.current.connect(restConnector);
|
||||||
|
result.current.selectPubKey(mockPubKeys[0].publicKey);
|
||||||
|
});
|
||||||
|
expect(spyOnConnect).toHaveBeenCalled();
|
||||||
|
expect(result.current.pubKeys).toHaveLength(mockPubKeys.length);
|
||||||
|
expect(result.current.pubKey).toBe(mockPubKeys[0].publicKey);
|
||||||
|
|
||||||
|
// Fetch pub keys
|
||||||
|
mockPubKeys.push({ publicKey: '333', name: 'public key 3' });
|
||||||
|
await act(async () => {
|
||||||
|
result.current.fetchPubKeys && result.current.fetchPubKeys();
|
||||||
|
});
|
||||||
|
expect(result.current.pubKeys).toHaveLength(mockPubKeys.length);
|
||||||
|
});
|
||||||
|
|
||||||
it('persists selected pubkey and disconnects', async () => {
|
it('persists selected pubkey and disconnects', async () => {
|
||||||
const { result } = setup();
|
const { result } = setup();
|
||||||
expect(result.current.pubKey).toBe(null);
|
expect(result.current.pubKey).toBe(null);
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
result.current.connect(restConnector);
|
result.current.connect(restConnector);
|
||||||
|
result.current.selectPubKey(mockPubKeys[0].publicKey);
|
||||||
});
|
});
|
||||||
expect(result.current.pubKey).toBe(mockPubKeys[1].publicKey);
|
expect(result.current.pubKey).toBe(mockPubKeys[0].publicKey);
|
||||||
|
|
||||||
// Disconnect
|
// Disconnect
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
@ -21,7 +21,7 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
|||||||
const [pubKey, setPubKey] = useState<string | null>(null);
|
const [pubKey, setPubKey] = useState<string | null>(null);
|
||||||
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
|
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
|
||||||
|
|
||||||
// Arary of pubkeys retrieved from the connector
|
// Array of public keys retrieved from the connector
|
||||||
const [pubKeys, setPubKeys] = useState<PubKey[] | null>(null);
|
const [pubKeys, setPubKeys] = useState<PubKey[] | null>(null);
|
||||||
|
|
||||||
// Reference to the current connector instance
|
// Reference to the current connector instance
|
||||||
@ -32,6 +32,28 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
|||||||
LocalStorage.setItem(WALLET_KEY, pk);
|
LocalStorage.setItem(WALLET_KEY, pk);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const fetchPubKeys = useCallback(async () => {
|
||||||
|
if (!connector.current) {
|
||||||
|
throw new Error('No connector');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const keys = await connector.current.connect();
|
||||||
|
|
||||||
|
if (keys?.length) {
|
||||||
|
setPubKeys(keys);
|
||||||
|
setIsReadOnly(connector.current instanceof ViewConnector);
|
||||||
|
return keys;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof WalletClientError) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const connect = useCallback(async (c: VegaConnector) => {
|
const connect = useCallback(async (c: VegaConnector) => {
|
||||||
connector.current = c;
|
connector.current = c;
|
||||||
try {
|
try {
|
||||||
@ -95,8 +117,18 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
|
|||||||
connect,
|
connect,
|
||||||
disconnect,
|
disconnect,
|
||||||
sendTx,
|
sendTx,
|
||||||
|
fetchPubKeys,
|
||||||
};
|
};
|
||||||
}, [isReadOnly, pubKey, pubKeys, selectPubKey, connect, disconnect, sendTx]);
|
}, [
|
||||||
|
isReadOnly,
|
||||||
|
pubKey,
|
||||||
|
pubKeys,
|
||||||
|
selectPubKey,
|
||||||
|
connect,
|
||||||
|
disconnect,
|
||||||
|
sendTx,
|
||||||
|
fetchPubKeys,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VegaWalletContext.Provider value={contextValue}>
|
<VegaWalletContext.Provider value={contextValue}>
|
||||||
|
@ -30,13 +30,13 @@ export const useJsonRpcConnect = (onConnect: () => void) => {
|
|||||||
// Check if wallet is configured for the same chain as the app
|
// Check if wallet is configured for the same chain as the app
|
||||||
setStatus(Status.GettingChainId);
|
setStatus(Status.GettingChainId);
|
||||||
|
|
||||||
// Dont throw in when cypress is running as trading app relies on
|
// Do not throw in when cypress is running as trading app relies on
|
||||||
// mocks which result in a mismatch between chainId for app and
|
// mocks which result in a mismatch between chainId for app and
|
||||||
// chainId for wallet
|
// chainId for wallet
|
||||||
if (!('Cypress' in window)) {
|
if (!('Cypress' in window)) {
|
||||||
const chainIdResult = await connector.getChainId();
|
const chainIdResult = await connector.getChainId();
|
||||||
if (chainIdResult.chainID !== appChainId) {
|
if (chainIdResult.chainID !== appChainId) {
|
||||||
// Throw wallet error for consitent error handling
|
// Throw wallet error for consistent error handling
|
||||||
throw ClientErrors.WRONG_NETWORK;
|
throw ClientErrors.WRONG_NETWORK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user