93 lines
2.9 KiB
JavaScript
93 lines
2.9 KiB
JavaScript
import { useLocalStorageState } from './utils';
|
|
import { clusterApiUrl, Connection } from '@solana/web3.js';
|
|
import React, { useContext, useMemo, useEffect } from 'react';
|
|
import { setCache, useAsyncData } from './fetch-loop';
|
|
import tuple from 'immutable-tuple';
|
|
|
|
export const ENDPOINTS = [
|
|
{
|
|
name: 'mainnet-beta',
|
|
endpoint: clusterApiUrl('mainnet-beta'),
|
|
},
|
|
{ name: 'testnet', endpoint: clusterApiUrl('testnet') },
|
|
{ name: 'devnet', endpoint: clusterApiUrl('devnet') },
|
|
{ name: 'localnet', endpoint: 'http://127.0.0.1:8899' },
|
|
];
|
|
|
|
const ConnectionContext = React.createContext(null);
|
|
|
|
export function ConnectionProvider({ children }) {
|
|
const [endpoint, setEndpoint] = useLocalStorageState(
|
|
'connectionEndpts',
|
|
clusterApiUrl('mainnet-beta'),
|
|
);
|
|
|
|
const connection = useMemo(() => new Connection(endpoint, 'recent'), [
|
|
endpoint,
|
|
]);
|
|
|
|
// The websocket library solana/web3.js uses closes its websocket connection when the subscription list
|
|
// is empty after opening its first time, preventing subsequent subscriptions from receiving responses.
|
|
// This is a hack to prevent the list from every getting empty
|
|
useEffect(() => {
|
|
const id = connection.onSignature(
|
|
'do not worry, this is expected to yield warning logs',
|
|
(result) => {
|
|
console.log(
|
|
'Received onSignature responses from does-not-exist',
|
|
result,
|
|
);
|
|
},
|
|
);
|
|
return () => connection.removeSignatureListener(id);
|
|
}, [connection]);
|
|
|
|
return (
|
|
<ConnectionContext.Provider value={{ endpoint, setEndpoint, connection }}>
|
|
{children}
|
|
</ConnectionContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useConnection() {
|
|
return useContext(ConnectionContext).connection;
|
|
}
|
|
|
|
export function useConnectionConfig() {
|
|
const context = useContext(ConnectionContext);
|
|
return { endpoint: context.endpoint, setEndpoint: context.setEndpoint };
|
|
}
|
|
|
|
export function useAccountInfo(publicKey) {
|
|
const connection = useConnection();
|
|
const cacheKey = tuple(connection, publicKey?.toBase58());
|
|
const [accountInfo, loaded] = useAsyncData(
|
|
async () => (publicKey ? connection.getAccountInfo(publicKey) : null),
|
|
cacheKey,
|
|
{ refreshInterval: 60000000 },
|
|
);
|
|
useEffect(() => {
|
|
if (!publicKey) {
|
|
return () => {};
|
|
}
|
|
let previousData = null;
|
|
// TODO: Just pipe the websocket data instead of re-fetching over REST
|
|
const id = connection.onAccountChange(publicKey, (e) => {
|
|
if (e.data) {
|
|
if (previousData && !previousData.equals(e.data)) {
|
|
setCache(cacheKey, e);
|
|
}
|
|
previousData = e.data;
|
|
}
|
|
});
|
|
return () => connection.removeAccountChangeListener(id);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [connection, publicKey?.toBase58(), cacheKey]);
|
|
return [accountInfo, loaded];
|
|
}
|
|
|
|
export function useAccountData(publicKey) {
|
|
const [accountInfo] = useAccountInfo(publicKey);
|
|
return accountInfo && accountInfo.data;
|
|
}
|