diff --git a/lib/client/client.ts b/lib/client/client.ts index 35382df..c41bf4e 100644 --- a/lib/client/client.ts +++ b/lib/client/client.ts @@ -122,7 +122,10 @@ export interface FutarchyBalancesClient { passTokenUrl?: string, failTokenUrl?: string ): Promise; - watchTokenBalance(tokenWithPDA: TokenWithPDA): Observable; + watchTokenBalance( + tokenWithPDA: TokenWithPDA, + authToken?: string + ): Observable; } export interface FutarchyMarketsClient { diff --git a/lib/client/indexer/balances.ts b/lib/client/indexer/balances.ts index 0c2a61d..817e8a3 100644 --- a/lib/client/indexer/balances.ts +++ b/lib/client/indexer/balances.ts @@ -14,7 +14,7 @@ import { ProposalState } from "@/types/proposals"; import { FutarchyRPCBalancesClient } from "../rpc"; -import { Observable, catchError, of, switchMap } from "rxjs"; +import { Observable, catchError, from, of, switchMap } from "rxjs"; import { Client as GQLWebSocketClient } from "graphql-ws"; import { Client as IndexerGraphQLClient } from "./__generated__"; import { @@ -32,14 +32,17 @@ export class FutarchyIndexerBalancesClient implements FutarchyBalancesClient { private rpcBalancesClient: FutarchyRPCBalancesClient; private graphqlWSClient: GQLWebSocketClient; private graphqlClient: IndexerGraphQLClient; + private balancesApiURL: string; constructor( rpcBalancesClient: FutarchyRPCBalancesClient, graphqlWSClient: GQLWebSocketClient, - graphqlClient: IndexerGraphQLClient + graphqlClient: IndexerGraphQLClient, + balancesApiURL: string ) { this.rpcBalancesClient = rpcBalancesClient; this.graphqlWSClient = graphqlWSClient; this.graphqlClient = graphqlClient; + this.balancesApiURL = balancesApiURL; } /** @@ -230,25 +233,68 @@ export class FutarchyIndexerBalancesClient implements FutarchyBalancesClient { return this.rpcBalancesClient.fetchTokenBalance(pda, token); } - watchTokenBalance(tokenWithPDA: TokenWithPDA): Observable { - // how do we initially fetch from RPC and then - const indexerObservable = this.watchTokenAcctForArgs( - { - where: { - token_acct: { _eq: tokenWithPDA.pda.toBase58() } + watchTokenBalance( + tokenWithPDA: TokenWithPDA, + authToken: string + ): Observable { + const url = `${this.balancesApiURL}/watch-token-balance`; + + const postData = { + tokenAcct: tokenWithPDA.pda.toBase58() + }; + + const headers = { + "Content-Type": "application/json", + Authorization: `Bearer ${authToken}` + }; + + // Make the POST request using fetch and convert the promise to an observable + const apiCall$ = from( + fetch(url, { + method: "POST", + headers: headers, + body: JSON.stringify(postData) + }).then((response) => { + if (!response.ok) { + throw new Error( + "balances API (asset-watcher) returned an error for trying to watch this balance... " + + response.text() + + " " + + response.statusText + ); } - }, - tokenWithPDA.token + return response.json(); + }) ); - return indexerObservable.pipe( - catchError((_) => { - const rpcObservable = - this.rpcBalancesClient.watchTokenBalance(tokenWithPDA); - return rpcObservable; + // Call the initial API to trigger watching on account + return apiCall$.pipe( + catchError((error) => { + console.error("Error triggering watch on account:", error); + // Fallback to RPC observable in case of an error + return this.rpcBalancesClient.watchTokenBalance(tokenWithPDA); }), - switchMap((value) => { - return of(value); + switchMap(() => { + // After triggering the watch, fetch from RPC + const indexerObservable = this.watchTokenAcctForArgs( + { + where: { + token_acct: { _eq: tokenWithPDA.pda.toBase58() } + } + }, + tokenWithPDA.token + ); + + return indexerObservable.pipe( + catchError((_) => { + const rpcObservable = + this.rpcBalancesClient.watchTokenBalance(tokenWithPDA); + return rpcObservable; + }), + switchMap((value) => { + return of(value); + }) + ); }) ); } diff --git a/lib/client/indexer/indexerClient.ts b/lib/client/indexer/indexerClient.ts index 26c0856..665ddd1 100644 --- a/lib/client/indexer/indexerClient.ts +++ b/lib/client/indexer/indexerClient.ts @@ -19,7 +19,8 @@ export class FutarchyIndexerClient implements FutarchyClient { constructor( rpcClient: FutarchyRPCClient, indexerURL: string, - indexerWSURL: string + indexerWSURL: string, + balancesApiURL: string ) { // TODO how can we batch these queries?? const options = { @@ -58,7 +59,8 @@ export class FutarchyIndexerClient implements FutarchyClient { this.balances = new FutarchyIndexerBalancesClient( rpcClient.balances, this.wsClient, - graphqlClient + graphqlClient, + balancesApiURL ); this.markets = new FutarchyIndexerMarketsClient( rpcClient.markets.openbook, @@ -110,8 +112,14 @@ export class FutarchyIndexerClient implements FutarchyClient { static make( rpcClient: FutarchyRPCClient, indexerURL: string, - indexerWSURL: string + indexerWSURL: string, + balancesApiURL: string ) { - return new FutarchyIndexerClient(rpcClient, indexerURL, indexerWSURL); + return new FutarchyIndexerClient( + rpcClient, + indexerURL, + indexerWSURL, + balancesApiURL + ); } } diff --git a/lib/client/rpc/dao.ts b/lib/client/rpc/dao.ts index 101792a..2a56a05 100644 --- a/lib/client/rpc/dao.ts +++ b/lib/client/rpc/dao.ts @@ -12,6 +12,7 @@ import { enrichTokenMetadata } from "@/tokens"; import { PublicKey } from "@solana/web3.js"; import { createSlug } from "@/utils"; import { Autocrat, IDL as AUTOCRAT_V0_3_IDL } from "@/idl/autocrat_v0.3"; +import { AUTOCRAT_PROGRAM_ID } from "@metadaoproject/futarchy"; export class FutarchyRPCDaoClient implements FutarchyDaoClient { private futarchyProtocols: FutarchyProtocol[]; @@ -138,7 +139,7 @@ export class FutarchyRPCDaoClient implements FutarchyDaoClient { ): Promise<{ base: BN; quote: BN } | undefined> { const autocrat = new Program( AUTOCRAT_V0_3_IDL, - "autoQP9RmUNkzzKRXsMkWicDVZ3h29vvyMDcAYjCxxg ", + AUTOCRAT_PROGRAM_ID, this.rpcProvider ); const currentDao = daoAggregate.daos @@ -173,17 +174,21 @@ export class FutarchyRPCDaoClient implements FutarchyDaoClient { ); const tokens = await Promise.all( - tokenAccounts.value.map(async (accountInfo) => { - const tokenAmount = accountInfo.account.data.parsed.info.tokenAmount; - const token = await enrichTokenMetadata( - accountInfo.account.data.parsed.info.mint, - this.rpcProvider - ); - return { - token: token, - balance: tokenAmount.uiAmount - }; - }) + tokenAccounts.value + .filter((accountInfo) => + PublicKey.isOnCurve(accountInfo.account.data.parsed.info.mint) + ) + .map(async (accountInfo) => { + const tokenAmount = accountInfo.account.data.parsed.info.tokenAmount; + const token = await enrichTokenMetadata( + new PublicKey(accountInfo.account.data.parsed.info.mint), + this.rpcProvider + ); + return { + token: token, + balance: tokenAmount.uiAmount + }; + }) ); return { diff --git a/package.json b/package.json index ccb48f2..aca05c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@metadaoproject/futarchy-sdk", - "version": "3.0.0-alpha.15", + "version": "3.0.0-alpha.16", "main": "dist", "scripts": { "preinstall": "npx only-allow pnpm",