Skip to content

Commit

Permalink
[Balance] fetch historical balance (#147)
Browse files Browse the repository at this point in the history
* [chore] Update package-lock.json (#132)

* [Balance] fetch historical balance

* formatting

* fix lint

* add max size for fetching all history

* rebase error

* tweak naming

* address comments

* move list historical balance test under address

* address comment

* fix lint

* fix IDE change

* update changelog

* add struct for ListHistoricalBalancesOptions

* fix format

* fix lint

* change log version

* address comments

---------

Co-authored-by: John Peterson <[email protected]>
  • Loading branch information
xinyu-li-cb and John-peterson-coinbase authored Aug 12, 2024
1 parent 23d4d80 commit e38d836
Show file tree
Hide file tree
Showing 10 changed files with 3,295 additions and 2,903 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Coinbase Node.js SDK Changelog

## [0.0.15]
## [0.0.16]

### Added
### Added

- Add Function `listHistoricalBalances` for `Address` for fetching historical balances for an asset

## [0.0.15]

### Added

- USD value conversion details to the StakingReward object
- Gasless USDC Sends
Expand Down
27 changes: 25 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 74 additions & 1 deletion src/coinbase/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import { Asset } from "./asset";
import { Balance } from "./balance";
import { BalanceMap } from "./balance_map";
import { FaucetTransaction } from "./faucet_transaction";
import { Amount, StakeOptionsMode } from "./types";
import { HistoricalBalance } from "./historical_balance";
import {
Amount,
StakeOptionsMode,
ListHistoricalBalancesResult,
ListHistoricalBalancesOptions,
} from "./types";
import { formatDate, getWeekBackDate } from "./utils";
import { StakingRewardFormat } from "../client";
import { StakingReward } from "./staking_reward";
Expand All @@ -13,6 +19,8 @@ import { StakingReward } from "./staking_reward";
* A representation of a blockchain address, which is a user-controlled account on a network.
*/
export class Address {
private static MAX_HISTORICAL_BALANCE = 1000;

protected networkId: string;
protected id: string;

Expand Down Expand Up @@ -79,6 +87,71 @@ export class Address {
return Balance.fromModelAndAssetId(response.data, assetId).amount;
}

/**
* Returns the historical balances of the provided asset.
*
* @param options - The options to list historical balances.
* @param options.assetId - The asset ID.
* @param options.limit - A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.
* @param options.page - A cursor for pagination across multiple pages of results. Don\&#39;t include this parameter on the first call. Use the next_page value returned in a previous response to request subsequent results.
* @returns The list of historical balance of the asset and next page token.
*/
public async listHistoricalBalances({
assetId,
limit,
page,
}: ListHistoricalBalancesOptions): Promise<ListHistoricalBalancesResult> {
const historyList: HistoricalBalance[] = [];

if (limit !== undefined) {
const response = await Coinbase.apiClients.externalAddress!.listAddressHistoricalBalance(
this.getNetworkId(),
this.getId(),
Asset.primaryDenomination(assetId),
limit,
page ? page : undefined,
);

response.data.data.forEach(historicalBalanceModel => {
const historicalBalance = HistoricalBalance.fromModel(historicalBalanceModel);
historyList.push(historicalBalance);
});

return {
historicalBalances: historyList,
nextPageToken: response.data.next_page,
};
}

const queue: string[] = [""];
while (queue.length > 0 && historyList.length < Address.MAX_HISTORICAL_BALANCE) {
const page = queue.shift();
const response = await Coinbase.apiClients.externalAddress!.listAddressHistoricalBalance(
this.getNetworkId(),
this.getId(),
Asset.primaryDenomination(assetId),
100,
page ? page : undefined,
);

response.data.data.forEach(historicalBalanceModel => {
const historicalBalance = HistoricalBalance.fromModel(historicalBalanceModel);
historyList.push(historicalBalance);
});

if (response.data.has_more) {
if (response.data.next_page) {
queue.push(response.data.next_page);
}
}
}

return {
historicalBalances: historyList,
nextPageToken: "",
};
}

/**
* Lists the staking rewards for the address.
*
Expand Down
44 changes: 44 additions & 0 deletions src/coinbase/historical_balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Decimal from "decimal.js";
import { HistoricalBalance as HistoricalBalanceModel } from "../client";
import { Asset } from "./asset";

/** A representation of historical balance. */
export class HistoricalBalance {
public readonly amount: Decimal;
public readonly blockHash: string;
public readonly blockHeight: Decimal;
public readonly asset: Asset;

/**
* Private constructor to prevent direct instantiation outside of the factory methods.
*
* @ignore
* @param {Decimal} amount - The amount of the balance.
* @param {Decimal} blockHeight - The block height at which the balance was recorded.
* @param {string} blockHash - The block hash at which the balance was recorded
* @param {string} asset - The asset we want to fetch.
* @hideconstructor
*/
private constructor(amount: Decimal, blockHeight: Decimal, blockHash: string, asset: Asset) {
this.amount = amount;
this.blockHeight = blockHeight;
this.blockHash = blockHash;
this.asset = asset;
}

/**
* Converts a HistoricalBalanceModel into a HistoricalBalance object.
*
* @param {HistoricalBalanceModel} model - The historical balance model object.
* @returns {HistoricalBalance} The HistoricalBalance object.
*/
public static fromModel(model: HistoricalBalanceModel): HistoricalBalance {
const asset = Asset.fromModel(model.asset);
return new HistoricalBalance(
asset.fromAtomicAmount(new Decimal(model.amount)),
new Decimal(model.block_height),
model.block_hash,
asset,
);
}
}
40 changes: 40 additions & 0 deletions src/coinbase/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Address as AddressModel,
AddressList,
AddressBalanceList,
AddressHistoricalBalanceList,
Balance,
CreateAddressRequest,
CreateWalletRequest,
Expand Down Expand Up @@ -34,6 +35,7 @@ import {
} from "./../client/api";
import { Address } from "./address";
import { Wallet } from "./wallet";
import { HistoricalBalance } from "./historical_balance";

export type AssetAPIClient = {
/**
Expand Down Expand Up @@ -334,6 +336,27 @@ export type ExternalAddressAPIClient = {
options?: RawAxiosRequestConfig,
): AxiosPromise<Balance>;

/**
* List the historical balance of an asset in a specific address.
*
* @summary Get address balance history for asset
* @param networkId - The ID of the blockchain network
* @param addressId - The ID of the address to fetch the historical balance for.
* @param assetId - The symbol of the asset to fetch the historical balance for.
* @param limit - A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.
* @param page - A cursor for pagination across multiple pages of results. Don\&#39;t include this parameter on the first call. Use the next_page value returned in a previous response to request subsequent results.
* @param options - Override http request option.
* @throws {RequiredError}
*/
listAddressHistoricalBalance(
networkId: string,
addressId: string,
assetId: string,
limit?: number,
page?: string,
options?: RawAxiosRequestConfig,
): AxiosPromise<AddressHistoricalBalanceList>;

/**
* Request faucet funds to be sent to external address.
*
Expand Down Expand Up @@ -770,3 +793,20 @@ export type CreateTradeOptions = {
timeoutSeconds?: number;
intervalSeconds?: number;
};

/**
* Options for listing historical balances of an address.
*/
export type ListHistoricalBalancesOptions = {
assetId: string;
limit?: number;
page?: string;
};

/**
* Result of ListHistoricalBalances.
*/
export type ListHistoricalBalancesResult = {
historicalBalances: HistoricalBalance[];
nextPageToken: string;
};
Loading

0 comments on commit e38d836

Please sign in to comment.