Skip to content

Commit

Permalink
Implement ObservableQueryBitcoinBalance
Browse files Browse the repository at this point in the history
  • Loading branch information
piatoss3612 committed Feb 27, 2025
1 parent 1a311fb commit ba5f7ed
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/stores-bitcoin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./account";
export * from "./queries";
142 changes: 142 additions & 0 deletions packages/stores-bitcoin/src/queries/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import {
ChainGetter,
IObservableQueryBalanceImpl,
QuerySharedContext,
} from "@keplr-wallet/stores";
import { computed, makeObservable } from "mobx";
import { AppCurrency } from "@keplr-wallet/types";
import { DenomHelper } from "@keplr-wallet/common";
import { CoinPretty, Int } from "@keplr-wallet/unit";
import { ObservableBitcoinIndexerQuery } from "./bitcoin-indexer";
import { AddressDetails } from "./types";

export class ObservableQueryBitcoinBalanceImpl
extends ObservableBitcoinIndexerQuery<AddressDetails>
implements IObservableQueryBalanceImpl
{
constructor(
sharedContext: QuerySharedContext,
chainId: string,
chainGetter: ChainGetter,
protected readonly denomHelper: DenomHelper,
protected readonly address: string
) {
super(sharedContext, chainId, chainGetter, `address/${address}`);

makeObservable(this);
}

/**
* Confirmed balance is the sum of all the confirmed transactions in the chain.
*/
@computed
get confirmedBalance(): CoinPretty {
if (!this.response || !this.response.data) {
return new CoinPretty(this.currency, new Int(0));
}

const data = this.response.data;

return new CoinPretty(
this.currency,
new Int(data.chain_stats.funded_txo_sum - data.chain_stats.spent_txo_sum)
);
}

/**
* Unconfirmed balance is the sum of all the unconfirmed transactions in the mempool.
*/
@computed
get unconfirmedBalance(): CoinPretty {
if (!this.response || !this.response.data) {
return new CoinPretty(this.currency, new Int(0));
}

const data = this.response.data;
return new CoinPretty(
this.currency,
new Int(
data.mempool_stats.funded_txo_sum - data.mempool_stats.spent_txo_sum
)
);
}

/**
* balance is the sum of confirmed and unconfirmed balances.
*/
@computed
get balance(): CoinPretty {
if (!this.response || !this.response.data) {
return new CoinPretty(this.currency, new Int(0));
}

const data = this.response.data;
return new CoinPretty(
this.currency,
new Int(
data.chain_stats.funded_txo_sum -
data.chain_stats.spent_txo_sum +
data.mempool_stats.funded_txo_sum -
data.mempool_stats.spent_txo_sum
)
);
}

@computed
get currency(): AppCurrency {
const denom = this.denomHelper.denom;

const modularChainInfo = this.chainGetter.getModularChain(this.chainId);
if (!("bitcoin" in modularChainInfo)) {
throw new Error(`${this.chainId} is not bitcoin chain`);
}

const modularChainInfoImpl = this.chainGetter.getModularChainInfoImpl(
this.chainId
);

const currencies = modularChainInfoImpl.getCurrencies("bitcoin");
const currency = currencies.find((cur) => cur.coinMinimalDenom === denom);

if (!currency) {
throw new Error(`Unknown currency: ${denom}`);
}

return currency;
}
}

export class ObservableQueryBitcoinBalance {
protected map: Map<string, ObservableQueryBitcoinBalanceImpl> = new Map();

constructor(protected readonly sharedContext: QuerySharedContext) {}

getBalance(
chainId: string,
chainGetter: ChainGetter,
address: string,
minimalDenom: string
): IObservableQueryBalanceImpl | undefined {
const key = `${chainId}/${address}/${minimalDenom}`;
const prior = this.map.get(key);
if (prior) {
return prior;
}

const denomHelper = new DenomHelper(minimalDenom);
const modularChainInfo = chainGetter.getModularChain(chainId);
if (!("bitcoin" in modularChainInfo)) {
return;
}

const impl = new ObservableQueryBitcoinBalanceImpl(
this.sharedContext,
chainId,
chainGetter,
denomHelper,
address
);
this.map.set(key, impl);
return impl;
}
}
7 changes: 6 additions & 1 deletion packages/stores-bitcoin/src/queries/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChainGetter, QuerySharedContext } from "@keplr-wallet/stores";
import { DeepReadonly } from "utility-types";
import { ObservableQueryBitcoinBalance } from "./balance";
export class BitcoinQueriesStore {
protected map: Map<string, BitcoinQueriesStoreImpl> = new Map();

Expand Down Expand Up @@ -28,10 +29,14 @@ export class BitcoinQueriesStore {
}

class BitcoinQueriesStoreImpl {
public readonly queryBitcoinBalance: DeepReadonly<ObservableQueryBitcoinBalance>;

constructor(
protected readonly sharedContext: QuerySharedContext,
protected readonly chainId: string,
protected readonly chainGetter: ChainGetter,
protected readonly tokenContractListURL: string
) {}
) {
this.queryBitcoinBalance = new ObservableQueryBitcoinBalance(sharedContext);
}
}
13 changes: 13 additions & 0 deletions packages/stores-bitcoin/src/queries/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface TxoStats {
funded_txo_count: number;
funded_txo_sum: number;
spent_txo_count: number;
spent_txo_sum: number;
tx_count: number;
}

export interface AddressDetails {
address: string;
chain_stats: TxoStats;
mempool_stats: TxoStats;
}

0 comments on commit ba5f7ed

Please sign in to comment.