fix: account subscription

This commit is contained in:
bartosz-lipinski 2020-12-16 23:42:19 -06:00
parent c9b20ed09b
commit 6b2373aebe
3 changed files with 35 additions and 42 deletions

View File

@ -111,7 +111,7 @@ export const borrow = async (
} }
notify({ notify({
message: "Adding Liquidity...", message: "Borrowing funds...",
description: "Please review transactions to approve.", description: "Please review transactions to approve.",
type: "warn", type: "warn",
}); });

View File

@ -1,4 +1,4 @@
import React, { useCallback, useContext, useEffect, useState } from "react"; import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
import { useConnection } from "./connection"; import { useConnection } from "./connection";
import { useWallet } from "./wallet"; import { useWallet } from "./wallet";
import { AccountInfo, Connection, PublicKey } from "@solana/web3.js"; import { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
@ -7,11 +7,10 @@ import { AccountLayout, u64, MintInfo, MintLayout } from "@solana/spl-token";
import { TokenAccount } from "./../models"; import { TokenAccount } from "./../models";
import { chunks } from "./../utils/utils"; import { chunks } from "./../utils/utils";
import { EventEmitter } from "./../utils/eventEmitter"; import { EventEmitter } from "./../utils/eventEmitter";
import { TokenClass } from "typescript";
const AccountsContext = React.createContext<any>(null); const AccountsContext = React.createContext<any>(null);
const accountEmitter = new EventEmitter();
const pendingCalls = new Map<string, Promise<ParsedAccountBase>>(); const pendingCalls = new Map<string, Promise<ParsedAccountBase>>();
const genericCache = new Map<string, ParsedAccountBase>(); const genericCache = new Map<string, ParsedAccountBase>();
@ -141,8 +140,10 @@ export const cache = {
return; return;
} }
const isNew = !genericCache.has(address);
genericCache.set(address, account); genericCache.set(address, account);
cache.emitter.raiseCacheUpdated(address, deserialize); cache.emitter.raiseCacheUpdated(address, isNew, deserialize);
return account; return account;
}, },
get: (pubKey: string | PublicKey) => { get: (pubKey: string | PublicKey) => {
@ -217,8 +218,10 @@ const UseNativeAccount = () => {
(account) => { (account) => {
const wrapped = wrapNativeAccount(wallet.publicKey, account); const wrapped = wrapNativeAccount(wallet.publicKey, account);
if (wrapped !== undefined && wallet) { if (wrapped !== undefined && wallet) {
cache.registerParser(wallet.publicKey?.toBase58(), TokenAccountParser); const id = wallet.publicKey?.toBase58();
genericCache.set(wallet.publicKey?.toBase58(), wrapped as TokenAccount); cache.registerParser(id, TokenAccountParser);
genericCache.set(id, wrapped as TokenAccount);
cache.emitter.raiseCacheUpdated(id, false, TokenAccountParser);
} }
}, },
[wallet] [wallet]
@ -291,6 +294,23 @@ export function AccountsProvider({ children = null as any }) {
setUserAccounts(accounts); setUserAccounts(accounts);
}, [nativeAccount, wallet, tokenAccounts, selectUserAccounts]); }, [nativeAccount, wallet, tokenAccounts, selectUserAccounts]);
useEffect(() => {
const subs: number[] = [];
cache.emitter.onCache((args) => {
if(args.isNew) {
let id = args.id;
let deserialize = args.parser;
connection.onAccountChange(new PublicKey(id), (info) => {
cache.add(id, info, deserialize);
});
}
});
return () => {
subs.forEach(id => connection.removeAccountChangeListener(id));
}
}, [connection]);
const publicKey = wallet?.publicKey; const publicKey = wallet?.publicKey;
useEffect(() => { useEffect(() => {
if (!connection || !publicKey) { if (!connection || !publicKey) {
@ -301,7 +321,8 @@ export function AccountsProvider({ children = null as any }) {
}); });
// This can return different types of accounts: token-account, mint, multisig // This can return different types of accounts: token-account, mint, multisig
// TODO: web3.js expose ability to filter. discuss filter syntax // TODO: web3.js expose ability to filter.
// this should use only filter syntax to only get accounts that are owned by user
const tokenSubID = connection.onProgramAccountChange( const tokenSubID = connection.onProgramAccountChange(
programIds().token, programIds().token,
(info) => { (info) => {
@ -311,22 +332,10 @@ export function AccountsProvider({ children = null as any }) {
if (info.accountInfo.data.length === AccountLayout.span) { if (info.accountInfo.data.length === AccountLayout.span) {
const data = deserializeAccount(info.accountInfo.data); const data = deserializeAccount(info.accountInfo.data);
if (PRECACHED_OWNERS.has(data.owner.toBase58()) || cache.get(id)) { if (PRECACHED_OWNERS.has(data.owner.toBase58())) {
cache.add(id, info.accountInfo, TokenAccountParser); cache.add(id, info.accountInfo, TokenAccountParser);
setTokenAccounts(selectUserAccounts()); setTokenAccounts(selectUserAccounts());
accountEmitter.raiseAccountUpdated(id);
} }
} else if (info.accountInfo.data.length === MintLayout.span) {
if (cache.get(id)) {
cache.add(id, info.accountInfo, MintParser);
accountEmitter.raiseAccountUpdated(id);
}
accountEmitter.raiseAccountUpdated(id);
}
if (genericCache.has(id)) {
cache.add(new PublicKey(id), info.accountInfo);
} }
}, },
"singleGossip" "singleGossip"

View File

@ -1,20 +1,14 @@
import { EventEmitter as Emitter } from "eventemitter3"; import { EventEmitter as Emitter } from "eventemitter3";
export class AccountUpdateEvent {
static type = "AccountUpdate";
id: string;
constructor(id: string) {
this.id = id;
}
}
export class CacheUpdateEvent { export class CacheUpdateEvent {
static type = "CacheUpdate"; static type = "CacheUpdate";
id: string; id: string;
parser: any; parser: any;
constructor(id: string, parser: any) { isNew: boolean;
constructor(id: string, isNew: boolean, parser: any) {
this.id = id; this.id = id;
this.parser = parser; this.parser = parser;
this.isNew = isNew;
} }
} }
@ -35,27 +29,17 @@ export class EventEmitter {
return () => this.emitter.removeListener(MarketUpdateEvent.type, callback); return () => this.emitter.removeListener(MarketUpdateEvent.type, callback);
} }
onAccount(callback: (args: AccountUpdateEvent) => void) {
this.emitter.on(AccountUpdateEvent.type, callback);
return () => this.emitter.removeListener(AccountUpdateEvent.type, callback);
}
onCache(callback: (args: CacheUpdateEvent) => void) { onCache(callback: (args: CacheUpdateEvent) => void) {
this.emitter.on(CacheUpdateEvent.type, callback); this.emitter.on(CacheUpdateEvent.type, callback);
return () => this.emitter.removeListener(CacheUpdateEvent.type, callback); return () => this.emitter.removeListener(CacheUpdateEvent.type, callback);
} }
raiseAccountUpdated(id: string) {
this.emitter.emit(AccountUpdateEvent.type, new AccountUpdateEvent(id));
}
raiseMarketUpdated(ids: Set<string>) { raiseMarketUpdated(ids: Set<string>) {
this.emitter.emit(MarketUpdateEvent.type, new MarketUpdateEvent(ids)); this.emitter.emit(MarketUpdateEvent.type, new MarketUpdateEvent(ids));
} }
raiseCacheUpdated(id: string, parser: any) { raiseCacheUpdated(id: string, isNew: boolean, parser: any) {
this.emitter.emit(CacheUpdateEvent.type, new CacheUpdateEvent(id, parser)); this.emitter.emit(CacheUpdateEvent.type, new CacheUpdateEvent(id, isNew, parser));
} }
} }