Skip to content

Commit

Permalink
Merge branch 'main' into feat/seconds-to-duration
Browse files Browse the repository at this point in the history
  • Loading branch information
peterpeterparker authored Nov 14, 2023
2 parents f86c071 + 8a6f5d9 commit 0df8d5c
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 7 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

## Features

- add support for ICP Index canister to library `@dfinity/ledger-icp`. New `IndexCanister` functions: `accountBalance`.
- add support for ICP Index canister to library `@dfinity/ledger-icp`. New `IndexCanister` functions: `accountBalance` and `getTransactions`.
- expose few types - notably `BlockHeight` - for library `@dfinity/ledger-icp`.
- support new fields from swap canister response types: `min_direct_participation_icp_e8s`, `max_direct_participation_icp_e8s` and `neurons_fund_participation`.
- support new fields in the `CreateServiceNervousSystem` proposal action `maximum_direct_participation_icp`, `minimum_direct_participation_icp` and `neurons_fund_participation`.
Expand Down
25 changes: 22 additions & 3 deletions packages/ledger-icp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,20 +241,21 @@ Returns the index of the block containing the tx if it was successful.

### :factory: IndexCanister

[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger-icp/src/index.canister.ts#L9)
[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger-icp/src/index.canister.ts#L19)

#### Methods

- [create](#gear-create)
- [accountBalance](#gear-accountbalance)
- [getTransactions](#gear-gettransactions)

##### :gear: create

| Method | Type |
| -------- | --------------------------------------------------------------------------------------------- |
| `create` | `({ canisterId: optionsCanisterId, ...options }: CanisterOptions<_SERVICE>) => IndexCanister` |

[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger-icp/src/index.canister.ts#L10)
[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger-icp/src/index.canister.ts#L20)

##### :gear: accountBalance

Expand All @@ -270,7 +271,25 @@ Parameters:
- `params.accountIdentifier`: The account identifier provided either as hex string or as an AccountIdentifier.
- `params.certified`: query or update call.

[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger-icp/src/index.canister.ts#L35)
[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger-icp/src/index.canister.ts#L45)

##### :gear: getTransactions

Returns the transactions and balance of an ICP account.

| Method | Type |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `getTransactions` | `({ certified, accountIdentifier, start, maxResults: max_results, }: GetTransactionsParams) => Promise<GetAccountIdentifierTransactionsResponse>` |

Parameters:

- `params`: The parameters to get the transactions.
- `params.certified`: query or update call.
- `params.accountIdentifier`: The account identifier provided either as hex string or as an AccountIdentifier.
- `params.start`: If set then the results will start from the next most recent transaction id after start (start won't be included). If not provided, then the results will start from the most recent transaction id.
- `params.maxResults`: Maximum number of transactions to fetch.

[:link: Source](https://github.com/dfinity/ic-js/tree/main/packages/ledger-icp/src/index.canister.ts#L64)

<!-- TSDOC_END -->

Expand Down
1 change: 1 addition & 0 deletions packages/ledger-icp/src/errors/index.errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class IndexError extends Error {}
152 changes: 151 additions & 1 deletion packages/ledger-icp/src/index.canister.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { ActorSubclass } from "@dfinity/agent";
import { mock } from "jest-mock-extended";
import { _SERVICE as IndexService } from "../candid/index";
import {
GetAccountIdentifierTransactionsError,
GetAccountIdentifierTransactionsResponse,
_SERVICE as IndexService,
} from "../candid/index";
import { IndexCanister } from "./index.canister";
import { mockAccountIdentifier } from "./mocks/ledger.mock";

Expand Down Expand Up @@ -71,4 +75,150 @@ describe("IndexCanister", () => {
).toThrowError();
});
});

describe("getTransactions", () => {
const transactionsMock = {
Ok: {
balance: 1234n,
transactions: [{ id: 1n }, { id: 2n }],
oldest_tx_id: [],
} as GetAccountIdentifierTransactionsResponse,
};

it("returns transactions with query call", async () => {
const service = mock<ActorSubclass<IndexService>>();
service.get_account_identifier_transactions.mockResolvedValue(
transactionsMock,
);
const index = IndexCanister.create({
serviceOverride: service,
});

const transactions = await index.getTransactions({
accountIdentifier: mockAccountIdentifier,
certified: false,
maxResults: 10n,
});

expect(transactions).toEqual(transactionsMock.Ok);
expect(service.get_account_identifier_transactions).toBeCalledWith({
account_identifier: mockAccountIdentifier.toHex(),
max_results: 10n,
start: [],
});
});

it("returns transactions with update call", async () => {
const service = mock<ActorSubclass<IndexService>>();
service.get_account_identifier_transactions.mockResolvedValue(
transactionsMock,
);
const index = IndexCanister.create({
certifiedServiceOverride: service,
});

const transactions = await index.getTransactions({
accountIdentifier: mockAccountIdentifier,
certified: true,
maxResults: 10n,
});

expect(transactions).toEqual(transactionsMock.Ok);
expect(service.get_account_identifier_transactions).toBeCalledWith({
account_identifier: mockAccountIdentifier.toHex(),
max_results: 10n,
start: [],
});
});

it("returns transactions with account identifier as hex", async () => {
const service = mock<ActorSubclass<IndexService>>();
service.get_account_identifier_transactions.mockResolvedValue(
transactionsMock,
);
const index = IndexCanister.create({
serviceOverride: service,
});

const transactions = await index.getTransactions({
accountIdentifier: mockAccountIdentifier.toHex(),
certified: false,
maxResults: 10n,
});

expect(transactions).toEqual(transactionsMock.Ok);
expect(service.get_account_identifier_transactions).toBeCalledWith({
account_identifier: mockAccountIdentifier.toHex(),
max_results: 10n,
start: [],
});
});

it("query transactions from start", async () => {
const service = mock<ActorSubclass<IndexService>>();
service.get_account_identifier_transactions.mockResolvedValue(
transactionsMock,
);
const index = IndexCanister.create({
serviceOverride: service,
});

const transactions = await index.getTransactions({
accountIdentifier: mockAccountIdentifier.toHex(),
certified: false,
maxResults: 10n,
start: 3n,
});

expect(transactions).toEqual(transactionsMock.Ok);
expect(service.get_account_identifier_transactions).toBeCalledWith({
account_identifier: mockAccountIdentifier.toHex(),
max_results: 10n,
start: [3n],
});
});

it("throws errors", async () => {
const transactionsErrorMock = {
Err: {
message: "Test error",
} as GetAccountIdentifierTransactionsError,
};

const service = mock<ActorSubclass<IndexService>>();
service.get_account_identifier_transactions.mockResolvedValue(
transactionsErrorMock,
);
const index = IndexCanister.create({
serviceOverride: service,
});

expect(() =>
index.getTransactions({
accountIdentifier: mockAccountIdentifier.toHex(),
certified: false,
maxResults: 10n,
}),
).rejects.toThrowError();
});

it("should bubble errors", () => {
const service = mock<ActorSubclass<IndexService>>();
service.get_account_identifier_transactions.mockImplementation(() => {
throw new Error();
});

const index = IndexCanister.create({
serviceOverride: service,
});

expect(() =>
index.getTransactions({
accountIdentifier: mockAccountIdentifier.toHex(),
certified: false,
maxResults: 10n,
}),
).rejects.toThrowError();
});
});
});
46 changes: 44 additions & 2 deletions packages/ledger-icp/src/index.canister.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { Canister, createServices, type CanisterOptions } from "@dfinity/utils";
import type { _SERVICE as IndexService } from "../candid/index";
import {
Canister,
createServices,
toNullable,
type CanisterOptions,
} from "@dfinity/utils";
import type {
GetAccountIdentifierTransactionsResponse,
_SERVICE as IndexService,
} from "../candid/index";
import { idlFactory as certifiedIdlFactory } from "../candid/ledger.certified.idl";
import { idlFactory } from "../candid/ledger.idl";
import { MAINNET_INDEX_CANISTER_ID } from "./constants/canister_ids";
import { IndexError } from "./errors/index.errors";
import type { GetTransactionsParams } from "./types/index.params";
import type { AccountBalanceParams } from "./types/ledger.params";
import { paramToAccountIdentifier } from "./utils/params.utils";

Expand Down Expand Up @@ -39,4 +49,36 @@ export class IndexCanister extends Canister<IndexService> {
this.caller({ certified }).get_account_identifier_balance(
paramToAccountIdentifier(accountIdentifier).toHex(),
);

/**
* Returns the transactions and balance of an ICP account.
*
* @param {GetTransactionsParams} params The parameters to get the transactions.
* @param {boolean} params.certified query or update call.
* @param {AccountIdentifierParam} params.accountIdentifier The account identifier provided either as hex string or as an AccountIdentifier.
* @param {bigint} params.start If set then the results will start from the next most recent transaction id after start (start won't be included). If not provided, then the results will start from the most recent transaction id.
* @param {bigint} params.maxResults Maximum number of transactions to fetch.
* @returns {Promise<GetAccountIdentifierTransactionsResponse>} The transactions, balance and the transaction id of the oldest transaction the account has.
* @throws {@link IndexError}
*/
getTransactions = async ({
certified,
accountIdentifier,
start,
maxResults: max_results,
}: GetTransactionsParams): Promise<GetAccountIdentifierTransactionsResponse> => {
const response = await this.caller({
certified,
}).get_account_identifier_transactions({
account_identifier: paramToAccountIdentifier(accountIdentifier).toHex(),
start: toNullable(start),
max_results,
});

if ("Err" in response) {
throw new IndexError(response.Err.message);
}

return response.Ok;
};
}
1 change: 1 addition & 0 deletions packages/ledger-icp/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export type * from "../candid/index";
export { AccountIdentifier, SubAccount } from "./account_identifier";
export * from "./errors/ledger.errors";
export { IndexCanister } from "./index.canister";
Expand Down
8 changes: 8 additions & 0 deletions packages/ledger-icp/src/types/index.params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { QueryParams } from "@dfinity/utils";
import type { AccountIdentifierParam } from "./ledger.params";

export type GetTransactionsParams = {
maxResults: bigint;
start?: bigint;
accountIdentifier: AccountIdentifierParam;
} & QueryParams;

0 comments on commit 0df8d5c

Please sign in to comment.