From 6b2373aebe6f9d7d608981d7e6a5360b28bb38a3 Mon Sep 17 00:00:00 2001 From: bartosz-lipinski <264380+bartosz-lipinski@users.noreply.github.com> Date: Wed, 16 Dec 2020 23:42:19 -0600 Subject: [PATCH] fix: account subscription --- src/actions/borrow.tsx | 2 +- src/contexts/accounts.tsx | 49 +++++++++++++++++++++++---------------- src/utils/eventEmitter.ts | 26 ++++----------------- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/src/actions/borrow.tsx b/src/actions/borrow.tsx index 2ba6c9e..741e229 100644 --- a/src/actions/borrow.tsx +++ b/src/actions/borrow.tsx @@ -111,7 +111,7 @@ export const borrow = async ( } notify({ - message: "Adding Liquidity...", + message: "Borrowing funds...", description: "Please review transactions to approve.", type: "warn", }); diff --git a/src/contexts/accounts.tsx b/src/contexts/accounts.tsx index 05bae40..a62b6ca 100644 --- a/src/contexts/accounts.tsx +++ b/src/contexts/accounts.tsx @@ -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 { useWallet } from "./wallet"; 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 { chunks } from "./../utils/utils"; import { EventEmitter } from "./../utils/eventEmitter"; +import { TokenClass } from "typescript"; const AccountsContext = React.createContext(null); -const accountEmitter = new EventEmitter(); - const pendingCalls = new Map>(); const genericCache = new Map(); @@ -141,8 +140,10 @@ export const cache = { return; } + const isNew = !genericCache.has(address); + genericCache.set(address, account); - cache.emitter.raiseCacheUpdated(address, deserialize); + cache.emitter.raiseCacheUpdated(address, isNew, deserialize); return account; }, get: (pubKey: string | PublicKey) => { @@ -217,8 +218,10 @@ const UseNativeAccount = () => { (account) => { const wrapped = wrapNativeAccount(wallet.publicKey, account); if (wrapped !== undefined && wallet) { - cache.registerParser(wallet.publicKey?.toBase58(), TokenAccountParser); - genericCache.set(wallet.publicKey?.toBase58(), wrapped as TokenAccount); + const id = wallet.publicKey?.toBase58(); + cache.registerParser(id, TokenAccountParser); + genericCache.set(id, wrapped as TokenAccount); + cache.emitter.raiseCacheUpdated(id, false, TokenAccountParser); } }, [wallet] @@ -291,6 +294,23 @@ export function AccountsProvider({ children = null as any }) { setUserAccounts(accounts); }, [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; useEffect(() => { 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 - // 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( programIds().token, (info) => { @@ -311,22 +332,10 @@ export function AccountsProvider({ children = null as any }) { if (info.accountInfo.data.length === AccountLayout.span) { 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); 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" diff --git a/src/utils/eventEmitter.ts b/src/utils/eventEmitter.ts index d798ee9..599c1dc 100644 --- a/src/utils/eventEmitter.ts +++ b/src/utils/eventEmitter.ts @@ -1,20 +1,14 @@ import { EventEmitter as Emitter } from "eventemitter3"; -export class AccountUpdateEvent { - static type = "AccountUpdate"; - id: string; - constructor(id: string) { - this.id = id; - } -} - export class CacheUpdateEvent { static type = "CacheUpdate"; id: string; parser: any; - constructor(id: string, parser: any) { + isNew: boolean; + constructor(id: string, isNew: boolean, parser: any) { this.id = id; this.parser = parser; + this.isNew = isNew; } } @@ -35,27 +29,17 @@ export class EventEmitter { 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) { this.emitter.on(CacheUpdateEvent.type, callback); return () => this.emitter.removeListener(CacheUpdateEvent.type, callback); } - raiseAccountUpdated(id: string) { - this.emitter.emit(AccountUpdateEvent.type, new AccountUpdateEvent(id)); - } - raiseMarketUpdated(ids: Set) { this.emitter.emit(MarketUpdateEvent.type, new MarketUpdateEvent(ids)); } - raiseCacheUpdated(id: string, parser: any) { - this.emitter.emit(CacheUpdateEvent.type, new CacheUpdateEvent(id, parser)); + raiseCacheUpdated(id: string, isNew: boolean, parser: any) { + this.emitter.emit(CacheUpdateEvent.type, new CacheUpdateEvent(id, isNew, parser)); } }