From 3f64c6590e2bbb289849e73d2027287d9786dc12 Mon Sep 17 00:00:00 2001 From: Erdi Maden Date: Thu, 23 May 2024 15:23:31 -0500 Subject: [PATCH] Implementing getWallet functionality (#27) * Adding getWallet functionality --- src/coinbase/tests/user_test.ts | 117 +++++++++++++++++++++++++----- src/coinbase/tests/utils.ts | 8 +- src/coinbase/tests/wallet_test.ts | 81 ++++++++++++--------- src/coinbase/types.ts | 26 ++++++- src/coinbase/user.ts | 35 ++++++++- src/coinbase/wallet.ts | 86 +++++++++++++++++----- 6 files changed, 276 insertions(+), 77 deletions(-) diff --git a/src/coinbase/tests/user_test.ts b/src/coinbase/tests/user_test.ts index b54a2071..6d06b536 100644 --- a/src/coinbase/tests/user_test.ts +++ b/src/coinbase/tests/user_test.ts @@ -1,24 +1,24 @@ -import * as fs from "fs"; -import * as crypto from "crypto"; import * as bip39 from "bip39"; +import * as crypto from "crypto"; +import * as fs from "fs"; +import { ArgumentError } from "../errors"; import { + AddressList, + Address as AddressModel, User as UserModel, Wallet as WalletModel, - Address as AddressModel, - AddressList, } from "./../../client/api"; -import { User } from "./../user"; import { Coinbase } from "./../coinbase"; +import { SeedData, WalletData } from "./../types"; +import { User } from "./../user"; +import { Wallet } from "./../wallet"; import { - mockFn, - walletsApiMock, addressesApiMock, - newAddressModel, + mockReturnRejectedValue, mockReturnValue, + newAddressModel, + walletsApiMock, } from "./utils"; -import { SeedData, WalletData } from "./../types"; -import { Wallet } from "./../wallet"; -import { ArgumentError } from "../errors"; describe("User Class", () => { let mockUserModel: UserModel; @@ -67,7 +67,6 @@ describe("User Class", () => { Coinbase.apiClients.wallet!.getWallet = mockReturnValue(mockWalletModel); Coinbase.apiClients.address = addressesApiMock; Coinbase.apiClients.address!.listAddresses = mockReturnValue(mockAddressList); - Coinbase.apiClients.address!.getAddress = mockReturnValue(mockAddressModel); user = new User(mockUserModel); walletData = { walletId: walletId, seed: bip39.generateMnemonic() }; importedWallet = await user.importWallet(walletData); @@ -76,7 +75,6 @@ describe("User Class", () => { expect(Coinbase.apiClients.wallet!.getWallet).toHaveBeenCalledTimes(1); expect(Coinbase.apiClients.address!.listAddresses).toHaveBeenCalledWith(walletId); expect(Coinbase.apiClients.address!.listAddresses).toHaveBeenCalledTimes(1); - expect(Coinbase.apiClients.address!.getAddress).toHaveBeenCalledTimes(1); }); it("should import an exported wallet", async () => { @@ -94,7 +92,6 @@ describe("User Class", () => { describe(".saveWallet", () => { let seed: string; - let addressCount: number; let walletId: string; let mockSeedWallet: Wallet; let savedWallet: Wallet; @@ -102,7 +99,6 @@ describe("User Class", () => { beforeAll(async () => { walletId = crypto.randomUUID(); seed = "86fc9fba421dcc6ad42747f14132c3cd975bd9fb1454df84ce5ea554f2542fbe"; - addressCount = 1; mockAddressModel = { address_id: "0xdeadbeef", wallet_id: walletId, @@ -123,7 +119,7 @@ describe("User Class", () => { privateKeyEncoding: { type: "pkcs8", format: "pem" }, publicKeyEncoding: { type: "spki", format: "pem" }, }).privateKey; - mockSeedWallet = await Wallet.init(mockWalletModel, seed, addressCount); + mockSeedWallet = await Wallet.init(mockWalletModel, seed, [mockAddressModel]); }); afterEach(async () => { @@ -133,7 +129,6 @@ describe("User Class", () => { it("should save the Wallet data when encryption is false", async () => { savedWallet = user.saveWallet(mockSeedWallet); expect(savedWallet).toBe(mockSeedWallet); - expect(Coinbase.apiClients.address!.getAddress).toHaveBeenCalledTimes(1); const storedSeedData = fs.readFileSync(Coinbase.backupFilePath); const walletSeedData = JSON.parse(storedSeedData.toString()); expect(walletSeedData[walletId].encrypted).toBe(false); @@ -145,7 +140,6 @@ describe("User Class", () => { it("should save the Wallet data when encryption is true", async () => { savedWallet = user.saveWallet(mockSeedWallet, true); expect(savedWallet).toBe(mockSeedWallet); - expect(Coinbase.apiClients.address!.getAddress).toHaveBeenCalledTimes(1); const storedSeedData = fs.readFileSync(Coinbase.backupFilePath); const walletSeedData = JSON.parse(storedSeedData.toString()); expect(walletSeedData[walletId].encrypted).toBe(true); @@ -254,7 +248,6 @@ describe("User Class", () => { Coinbase.apiClients.wallet!.getWallet = mockReturnValue(walletModelWithDefaultAddress); Coinbase.apiClients.address = addressesApiMock; Coinbase.apiClients.address!.listAddresses = mockReturnValue(addressListModel); - Coinbase.apiClients.address!.getAddress = mockReturnValue(addressModel); const wallets = await user.loadWallets(); const wallet = wallets[walletId]; @@ -288,4 +281,90 @@ describe("User Class", () => { await expect(user.loadWallets()).rejects.toThrow(new ArgumentError("Malformed backup data")); }); }); + + describe(".getWallets", () => { + let user: User; + let walletId: string; + let walletModelWithDefaultAddress: WalletModel; + let addressListModel: AddressList; + + beforeEach(() => { + jest.clearAllMocks(); + walletId = crypto.randomUUID(); + const addressModel1: AddressModel = newAddressModel(walletId); + const addressModel2: AddressModel = newAddressModel(walletId); + walletModelWithDefaultAddress = { + id: walletId, + network_id: Coinbase.networkList.BaseSepolia, + default_address: addressModel1, + }; + addressListModel = { + data: [addressModel1, addressModel2], + has_more: false, + next_page: "", + total_count: 1, + }; + Coinbase.apiClients.wallet = walletsApiMock; + Coinbase.apiClients.address = addressesApiMock; + const mockUserModel: UserModel = { + id: "12345", + } as UserModel; + user = new User(mockUserModel); + }); + + it("should raise an error when the Wallet API call fails", async () => { + Coinbase.apiClients.wallet!.listWallets = mockReturnRejectedValue(new Error("API Error")); + await expect(user.getWallets()).rejects.toThrow(new Error("API Error")); + expect(Coinbase.apiClients.wallet!.listWallets).toHaveBeenCalledTimes(1); + expect(Coinbase.apiClients.address!.listAddresses).toHaveBeenCalledTimes(0); + expect(Coinbase.apiClients.wallet!.listWallets).toHaveBeenCalledWith(10, ""); + }); + + it("should raise an error when the Address API call fails", async () => { + Coinbase.apiClients.wallet!.listWallets = mockReturnValue({ + data: [walletModelWithDefaultAddress], + has_more: false, + next_page: "", + total_count: 1, + }); + Coinbase.apiClients.address!.listAddresses = mockReturnRejectedValue(new Error("API Error")); + await expect(user.getWallets()).rejects.toThrow(new Error("API Error")); + expect(Coinbase.apiClients.wallet!.listWallets).toHaveBeenCalledTimes(1); + expect(Coinbase.apiClients.address!.listAddresses).toHaveBeenCalledTimes(1); + }); + + it("should return an empty list of Wallets when the User has no Wallets", async () => { + Coinbase.apiClients.wallet!.listWallets = mockReturnValue({ + data: [], + has_more: false, + next_page: "", + total_count: 0, + }); + const wallets = await user.getWallets(); + expect(wallets.length).toBe(0); + expect(Coinbase.apiClients.wallet!.listWallets).toHaveBeenCalledTimes(1); + expect(Coinbase.apiClients.address!.listAddresses).toHaveBeenCalledTimes(0); + }); + + it("should return the list of Wallets", async () => { + Coinbase.apiClients.wallet!.listWallets = mockReturnValue({ + data: [walletModelWithDefaultAddress], + has_more: false, + next_page: "", + total_count: 2, + }); + Coinbase.apiClients.address!.listAddresses = mockReturnValue(addressListModel); + const wallets = await user.getWallets(); + expect(wallets.length).toBe(1); + expect(wallets[0].getId()).toBe(walletId); + expect(wallets[0].getAddresses().length).toBe(2); + expect(Coinbase.apiClients.wallet!.listWallets).toHaveBeenCalledTimes(1); + expect(Coinbase.apiClients.address!.listAddresses).toHaveBeenCalledTimes(1); + expect(Coinbase.apiClients.address!.listAddresses).toHaveBeenCalledWith( + walletId, + Wallet.MAX_ADDRESSES, + ); + expect(Coinbase.apiClients.wallet!.listWallets).toHaveBeenCalledWith(10, ""); + }); + }); }); diff --git a/src/coinbase/tests/utils.ts b/src/coinbase/tests/utils.ts index af12ce20..085f173f 100644 --- a/src/coinbase/tests/utils.ts +++ b/src/coinbase/tests/utils.ts @@ -11,12 +11,17 @@ import { } from "../../client"; import { BASE_PATH } from "../../client/base"; import { Coinbase } from "../coinbase"; -import { registerAxiosInterceptors } from "../utils"; +import { convertStringToHex, registerAxiosInterceptors } from "../utils"; +import { HDKey } from "@scure/bip32"; export const mockFn = (...args) => jest.fn(...args) as any; export const mockReturnValue = data => jest.fn().mockResolvedValue({ data }); export const mockReturnRejectedValue = data => jest.fn().mockRejectedValue(data); +export const getAddressFromHDKey = (hdKey: HDKey): string => { + return new ethers.Wallet(convertStringToHex(hdKey.privateKey!)).address; +}; + export const walletId = randomUUID(); export const generateRandomHash = (length = 8) => { @@ -121,6 +126,7 @@ export const usersApiMock = { export const walletsApiMock = { getWallet: jest.fn(), createWallet: jest.fn(), + listWallets: jest.fn(), listWalletBalances: jest.fn(), getWalletBalance: jest.fn(), }; diff --git a/src/coinbase/tests/wallet_test.ts b/src/coinbase/tests/wallet_test.ts index c17d602a..2ac9b194 100644 --- a/src/coinbase/tests/wallet_test.ts +++ b/src/coinbase/tests/wallet_test.ts @@ -1,22 +1,25 @@ +import { HDKey } from "@scure/bip32"; +import * as bip39 from "bip39"; import { randomUUID } from "crypto"; +import Decimal from "decimal.js"; +import { Address } from "../address"; import { Coinbase } from "../coinbase"; +import { ArgumentError } from "../errors"; import { Wallet } from "../wallet"; import { AddressBalanceList, Address as AddressModel, - Wallet as WalletModel, Balance as BalanceModel, + Wallet as WalletModel, } from "./../../client"; -import { ArgumentError } from "../errors"; import { addressesApiMock, + getAddressFromHDKey, mockFn, mockReturnValue, newAddressModel, walletsApiMock, } from "./utils"; -import { Address } from "../address"; -import Decimal from "decimal.js"; describe("Wallet Class", () => { let wallet, walletModel, walletId; @@ -69,20 +72,37 @@ describe("Wallet Class", () => { it("should return the correct default address", async () => { expect(wallet.defaultAddress()?.getId()).toBe(walletModel.default_address.address_id); }); + + it("should return true for canSign when the wallet is initialized without a seed", async () => { + expect(wallet.canSign()).toBe(true); + }); + + it("should create new address and update the existing address list", async () => { + const newAddress = await wallet.createAddress(); + expect(newAddress).toBeInstanceOf(Address); + expect(wallet.getAddresses().length).toBe(2); + expect(wallet.getAddress(newAddress.getId()).getId()).toBe(newAddress.getId()); + expect(Coinbase.apiClients.address!.createAddress).toHaveBeenCalledTimes(2); + }); }); describe(".init", () => { const existingSeed = "hidden assault maple cheap gentle paper earth surprise trophy guide room tired"; + const baseWallet = HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(existingSeed)); + const wallet1 = baseWallet.deriveChild(0); + const address1 = getAddressFromHDKey(wallet1); + const wallet2 = baseWallet.deriveChild(1); + const address2 = getAddressFromHDKey(wallet2); const addressList = [ { - address_id: "0x23626702fdC45fc75906E535E38Ee1c7EC0C3213", + address_id: address1, network_id: Coinbase.networkList.BaseSepolia, public_key: "0x032c11a826d153bb8cf17426d03c3ffb74ea445b17362f98e1536f22bcce720772", wallet_id: walletId, }, { - address_id: "0x770603171A98d1CD07018F7309A1413753cA0018", + address_id: address2, network_id: Coinbase.networkList.BaseSepolia, public_key: "0x03c3379b488a32a432a4dfe91cc3a28be210eddc98b2005bb59a4cf4ed0646eb56", wallet_id: walletId, @@ -91,29 +111,7 @@ describe("Wallet Class", () => { beforeEach(async () => { jest.clearAllMocks(); - const getAddress = jest.fn(); - addressList.forEach(() => { - getAddress.mockImplementationOnce((wallet_id, address_id) => { - return Promise.resolve({ - data: { - address_id, - network_id: Coinbase.networkList.BaseSepolia, - public_key: "0x03c3379b488a32a432a4dfe91cc3a28be210eddc98b2005bb59a4cf4ed0646eb56", - wallet_id, - }, - }); - }); - }); - Coinbase.apiClients.address!.getAddress = getAddress; - wallet = await Wallet.init(walletModel, existingSeed, 2); - expect(Coinbase.apiClients.address!.getAddress).toHaveBeenCalledTimes(2); - addressList.forEach((address, callIndex) => { - expect(Coinbase.apiClients.address!.getAddress).toHaveBeenNthCalledWith( - callIndex + 1, - walletId, - address.address_id, - ); - }); + wallet = await Wallet.init(walletModel, existingSeed, addressList); }); it("should return a Wallet instance", async () => { @@ -133,7 +131,19 @@ describe("Wallet Class", () => { }); it("should derive the correct number of addresses", async () => { - expect(wallet.addresses.length).toBe(2); + expect(wallet.getAddresses().length).toBe(2); + }); + + it("should derive the correct addresses", async () => { + expect(wallet.getAddress(address1)).toBe(wallet.addresses[0]); + expect(wallet.getAddress(address2)).toBe(wallet.addresses[1]); + }); + + it("should create new address and update the existing address list", async () => { + const newAddress = await wallet.createAddress(); + expect(newAddress).toBeInstanceOf(Address); + expect(wallet.getAddresses().length).toBe(3); + expect(wallet.getAddress(newAddress.getId()).getId()).toBe(newAddress.getId()); }); it("should return the correct string representation", async () => { @@ -153,7 +163,6 @@ describe("Wallet Class", () => { let walletModel: WalletModel; let seedWallet: Wallet; const seed = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; - const addressCount = 1; beforeAll(async () => { walletId = randomUUID(); @@ -167,22 +176,24 @@ describe("Wallet Class", () => { Coinbase.apiClients.address!.getAddress = mockFn(() => { return { data: addressModel }; }); - seedWallet = await Wallet.init(walletModel, seed, addressCount); + seedWallet = await Wallet.init(walletModel, seed); }); it("exports the Wallet data", () => { const walletData = seedWallet.export(); - expect(Coinbase.apiClients.address!.getAddress).toHaveBeenCalledTimes(1); expect(walletData.walletId).toBe(seedWallet.getId()); expect(walletData.seed).toBe(seed); }); it("allows for re-creation of a Wallet", async () => { const walletData = seedWallet.export(); - const newWallet = await Wallet.init(walletModel, walletData.seed, addressCount); - expect(Coinbase.apiClients.address!.getAddress).toHaveBeenCalledTimes(2); + const newWallet = await Wallet.init(walletModel, walletData.seed); expect(newWallet).toBeInstanceOf(Wallet); }); + + it("should return true for canSign when the wallet is initialized with a seed", () => { + expect(wallet.canSign()).toBe(true); + }); }); describe("#defaultAddress", () => { diff --git a/src/coinbase/types.ts b/src/coinbase/types.ts index ca3db4c2..9ab7ceac 100644 --- a/src/coinbase/types.ts +++ b/src/coinbase/types.ts @@ -13,6 +13,7 @@ import { User as UserModel, Wallet as WalletModel, Transfer as TransferModel, + WalletList, } from "./../client/api"; /** @@ -49,7 +50,10 @@ export type WalletAPIClient = { * @throws {RequiredError} If the required parameter is not provided. * @throws {APIError} If the request fails. */ - listWalletBalances(walletId: string, options?): AxiosPromise; + listWalletBalances( + walletId: string, + options?: RawAxiosRequestConfig, + ): AxiosPromise; /** * List the balances of all of the addresses in the wallet aggregated by asset. @@ -59,7 +63,10 @@ export type WalletAPIClient = { * @throws {RequiredError} If the required parameter is not provided. * @throws {APIError} If the request fails. */ - listWalletBalances(walletId: string, options?): AxiosPromise; + listWalletBalances( + walletId: string, + options?: RawAxiosRequestConfig, + ): AxiosPromise; /** * Get the aggregated balance of an asset across all of the addresses in the wallet. @@ -75,6 +82,21 @@ export type WalletAPIClient = { assetId: string, options?: RawAxiosRequestConfig, ): AxiosPromise; + + /** + * List wallets belonging to the user. + * + * @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\'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 {APIError} If the request fails. + * @throws {RequiredError} If the required parameter is not provided. + */ + listWallets( + limit?: number, + page?: string, + options?: RawAxiosRequestConfig, + ): AxiosPromise; }; /** diff --git a/src/coinbase/user.ts b/src/coinbase/user.ts index b34057b4..5501626d 100644 --- a/src/coinbase/user.ts +++ b/src/coinbase/user.ts @@ -1,7 +1,7 @@ import * as fs from "fs"; import * as crypto from "crypto"; import { WalletData, SeedData } from "./types"; -import { User as UserModel } from "./../client/api"; +import { User as UserModel, Address as AddressModel, Wallet as WalletModel } from "./../client/api"; import { Wallet } from "./wallet"; import { Coinbase } from "./coinbase"; import { ArgumentError } from "./errors"; @@ -90,6 +90,36 @@ export class User { return wallet; } + /** + * Lists the Wallets belonging to the User. + * + * @param pageSize - The number of Wallets to return per page. Defaults to 10 + * @param nextPageToken - The token for the next page of Wallets + * @returns The list of Wallets. + */ + public async getWallets(pageSize: number = 10, nextPageToken: string = ""): Promise { + const addressModelMap: { [key: string]: AddressModel[] } = {}; + const walletList = await Coinbase.apiClients.wallet!.listWallets(pageSize, nextPageToken); + const walletsModels: WalletModel[] = []; + + for (const wallet of walletList.data.data) { + walletsModels.push(wallet); + } + for (const wallet of walletsModels) { + const addressList = await Coinbase.apiClients.address!.listAddresses( + wallet.id!, + Wallet.MAX_ADDRESSES, + ); + addressModelMap[wallet.id!] = addressList.data.data; + } + + return await Promise.all( + walletsModels.map(async wallet => { + return await Wallet.init(wallet, "", addressModelMap[wallet.id!]); + }), + ); + } + /** * Loads all wallets belonging to the User with backup persisted to the local file system. * @@ -145,9 +175,8 @@ export class User { public async importWallet(data: WalletData): Promise { const walletModel = await Coinbase.apiClients.wallet!.getWallet(data.walletId); const addressList = await Coinbase.apiClients.address!.listAddresses(data.walletId); - const addressCount = addressList.data!.total_count; - return Wallet.init(walletModel.data, data.seed, addressCount); + return Wallet.init(walletModel.data, data.seed, addressList.data.data); } /** diff --git a/src/coinbase/wallet.ts b/src/coinbase/wallet.ts index fa74fb19..ffa100a0 100644 --- a/src/coinbase/wallet.ts +++ b/src/coinbase/wallet.ts @@ -25,8 +25,10 @@ export class Wallet { private master: HDKey; private seed: string; private addresses: Address[] = []; + private addressModels: AddressModel[] = []; private readonly addressPathPrefix = "m/44'/60'/0'/0"; private addressIndex = 0; + static MAX_ADDRESSES = 20; /** * Private constructor to prevent direct instantiation outside of factory method. Use Wallet.init instead. @@ -35,12 +37,19 @@ export class Wallet { * @param model - The wallet model object. * @param master - The HD master key. * @param seed - The seed to use for the Wallet. Expects a 32-byte hexadecimal with no 0x prefix. + * @param addressModels - The models of the addresses already registered with the Wallet. * @hideconstructor */ - private constructor(model: WalletModel, master: HDKey, seed: string) { + private constructor( + model: WalletModel, + master: HDKey, + seed: string, + addressModels: AddressModel[] = [], + ) { this.model = model; this.master = master; this.seed = seed; + this.addressModels = addressModels; } /** @@ -74,7 +83,7 @@ export class Wallet { * @constructs Wallet * @param model - The underlying Wallet model object * @param seed - The seed to use for the Wallet. Expects a 32-byte hexadecimal with no 0x prefix. If not provided, a new seed will be generated. - * @param addressCount - The number of addresses already registered for the Wallet. + * @param addressModels - The models of the addresses already registered with the Wallet. If not provided, the Wallet will derive the first default address. * @throws {ArgumentError} If the model or client is not provided. * @throws {InternalError} - If address derivation or caching fails. * @throws {APIError} - If the request fails. @@ -83,7 +92,7 @@ export class Wallet { public static async init( model: WalletModel, seed: string = "", - addressCount: number = 0, + addressModels: AddressModel[] = [], ): Promise { if (!model) { throw new ArgumentError("Wallet model cannot be empty"); @@ -92,15 +101,10 @@ export class Wallet { seed = bip39.generateMnemonic(); } const master = HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(seed)); - - const wallet = new Wallet(model, master, seed); - - if (addressCount > 0) { - for (let i = 0; i < addressCount; i++) { - await wallet.deriveAddress(); - } + const wallet = new Wallet(model, master, seed, addressModels); + if (addressModels.length > 0) { + wallet.deriveAddresses(addressModels); } - return wallet; } @@ -130,9 +134,10 @@ export class Wallet { /** * Creates a new Address in the Wallet. * + * @returns The new Address. * @throws {APIError} - If the address creation fails. */ - private async createAddress(): Promise { + public async createAddress(): Promise
{ const key = this.deriveKey(); const attestation = this.createAttestation(key); const publicKey = convertStringToHex(key.publicKey!); @@ -143,6 +148,7 @@ export class Wallet { }; const response = await Coinbase.apiClients.address!.createAddress(this.model.id!, payload); this.cacheAddress(response!.data); + return new Address(response!.data); } /** @@ -187,15 +193,52 @@ export class Wallet { /** * Derives an already registered Address in the Wallet. * + * @param addressMap - The map of registered Address IDs + * @param addressModel - The Address model * @throws {InternalError} - If address derivation fails. * @throws {APIError} - If the request fails. * @returns A promise that resolves when the address is derived. */ - private async deriveAddress(): Promise { - const key = this.deriveKey(); - const wallet = new ethers.Wallet(convertStringToHex(key.privateKey!)); - const response = await Coinbase.apiClients.address!.getAddress(this.model.id!, wallet.address); - this.cacheAddress(response.data); + private async deriveAddress( + addressMap: { [key: string]: boolean }, + addressModel: AddressModel, + ): Promise { + const key = this.master.publicKey ? this.deriveKey() : null; + if (key) { + const wallet = new ethers.Wallet(convertStringToHex(key.privateKey!)); + if (addressMap[wallet.address]) { + throw new InternalError("Invalid address"); + } + } + this.cacheAddress(addressModel); + } + + /** + * Derives the registered Addresses in the Wallet. + * + * @param addresses - The models of the addresses already registered with the + */ + public async deriveAddresses(addresses: AddressModel[]): Promise { + const addressMap = this.buildAddressMap(addresses); + for (const address of addresses) { + this.deriveAddress(addressMap, address); + } + } + + /** + * Builds a Hash of the registered Addresses. + * + * @param addressModels - The models of the addresses already registered with the Wallet. + * @returns The Hash of registered Addresses + */ + private buildAddressMap(addressModels: AddressModel[]): { [key: string]: boolean } { + const addressMap: { [key: string]: boolean } = {}; + + addressModels?.forEach(addressModel => { + addressMap[addressModel.address_id] = true; + }); + + return addressMap; } /** @@ -222,6 +265,15 @@ export class Wallet { }); } + /** + * Returns the list of Addresses in the Wallet. + * + * @returns The list of Addresses. + */ + public getAddresses(): Address[] { + return this.addresses; + } + /** * Returns the list of balances of this Wallet. Balances are aggregated across all Addresses in the Wallet. *