diff --git a/packages/jellyfish-api-core/__tests__/category/spv/listHtlcsOutputs.test.ts b/packages/jellyfish-api-core/__tests__/category/spv/listHtlcsOutputs.test.ts new file mode 100644 index 0000000000..a45cb7f40e --- /dev/null +++ b/packages/jellyfish-api-core/__tests__/category/spv/listHtlcsOutputs.test.ts @@ -0,0 +1,76 @@ +import { MasterNodeRegTestContainer } from '@defichain/testcontainers' +import { RpcApiError } from '@defichain/jellyfish-api-core' +import { ContainerAdapterClient } from '../../container_adapter_client' +import BigNumber from 'bignumber.js' + +describe('Spv', () => { + const container = new MasterNodeRegTestContainer() + const client = new ContainerAdapterClient(container) + + beforeAll(async () => { + await container.start() + await container.call('spv_fundaddress', [await container.call('spv_getnewaddress')]) // Funds 1 BTC + }) + + afterAll(async () => { + await container.stop() + }) + + it('should listHtlcOutputs', async () => { + const pubKeyA = await container.call('spv_getaddresspubkey', [await container.call('spv_getnewaddress')]) + const pubKeyB = await container.call('spv_getaddresspubkey', [await container.call('spv_getnewaddress')]) + const timeout = 10 + const seed = 'aba5f7e9aecf6ec4372c8a1e49562680d066da4655ee8b4bb01640479fffeaa8' + const seedhash = 'df95183883789f237977543885e1f82ddc045a3ba90c8f25b43a5b797a35d20e' + + const htlc = await client.spv.createHtlc(pubKeyA, pubKeyB, { timeout: `${timeout}`, seedhash }) + await container.generate(1) + const fund = await container.call('spv_sendtoaddress', [htlc.address, 0.1]) // Funds HTLC address + const claim = await client.spv.claimHtlc( + htlc.address, + await container.call('spv_getnewaddress'), + { seed } + ) + + const list = await client.spv.listHtlcOutputs() + expect(list.length).toStrictEqual(1) + expect(list[0]).toStrictEqual({ + txid: fund.txid, + vout: expect.any(Number), + amount: new BigNumber(0.1), + address: htlc.address, + confirms: 1, + spent: { + txid: claim.txid, + confirms: 1 + } + }) + }) + + it('listHtlcOutputs should return empty list when called with a new address', async () => { + const pubKeyA = await container.call('spv_getaddresspubkey', [await container.call('spv_getnewaddress')]) + const pubKeyB = await container.call('spv_getaddresspubkey', [await container.call('spv_getnewaddress')]) + const timeout = 10 + const seed = 'aba5f7e9aecf6ec4372c8a1e49562680d066da4655ee8b4bb01640479fffeaa8' + const seedhash = 'df95183883789f237977543885e1f82ddc045a3ba90c8f25b43a5b797a35d20e' + + const htlc = await client.spv.createHtlc(pubKeyA, pubKeyB, { timeout: `${timeout}`, seedhash }) + await container.generate(1) + await container.call('spv_sendtoaddress', [htlc.address, 0.1]) // Funds HTLC address + await client.spv.claimHtlc( + htlc.address, + await container.call('spv_getnewaddress'), + { seed } + ) + + const list = await client.spv.listHtlcOutputs(await container.call('spv_getnewaddress')) + expect(list.length).toStrictEqual(0) + }) + + it('should not listHtlcOutputs with invalid public address', async () => { + const promise = client.spv.listHtlcOutputs('XXXX') + + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow("RpcApiError: 'Invalid address', code: -5, method: spv_listhtlcoutputs") + }) +}) diff --git a/packages/jellyfish-api-core/src/category/spv.ts b/packages/jellyfish-api-core/src/category/spv.ts index 47599cba8e..5cd5f0d1f8 100644 --- a/packages/jellyfish-api-core/src/category/spv.ts +++ b/packages/jellyfish-api-core/src/category/spv.ts @@ -95,6 +95,18 @@ export class Spv { async claimHtlc (scriptAddress: string, destinationAddress: string, options: ClaimHtlcOptions): Promise { return await this.client.call('spv_claimhtlc', [scriptAddress, destinationAddress, options.seed, options.feeRate], 'bignumber') } + + /** + * List all outputs related to HTLC addresses in the wallet. + * + * @param {string | undefined} [scriptAddress] HTLC address to filter result + * @return {Promise} + */ + async listHtlcOutputs (scriptAddress?: string): Promise { + return await this.client.call('spv_listhtlcoutputs', [scriptAddress], { + amount: 'bignumber' + }) + } } export interface ReceivedByAddressInfo { @@ -154,3 +166,25 @@ export interface ClaimHtlcOptions { /** Fee rate in satoshis per KB */ feeRate?: BigNumber } + +export interface SpentInfo { + /** The transaction id */ + txid: string + /** Number of spent confirmations */ + confirms: number +} + +export interface ListHtlcsOutputsResult { + /** The transaction id */ + txid: string + /** Output relating to the HTLC address */ + vout: number + /** Total amount of BTC recieved by the address */ + amount: BigNumber + /** HTLC address */ + address: string + /** Number of confirmations */ + confirms: number + /** Object containing spent info */ + spent: SpentInfo +} diff --git a/website/docs/jellyfish/api/spv.md b/website/docs/jellyfish/api/spv.md index 1d65c04ae0..2f69e2bfea 100644 --- a/website/docs/jellyfish/api/spv.md +++ b/website/docs/jellyfish/api/spv.md @@ -128,3 +128,27 @@ interface SendMessageResult { sendmessage: string } ``` + +## listHtlcOutputs + +List all outputs related to HTLC addresses in the wallet. + +```ts title="client.spv.listHtlcOutputs()" +interface spv { + listHtlcOutputs (scriptAddress?: string): Promise +} + +interface SpentInfo { + txid: string + confirms: number +} + +interface ListHtlcsOutputsResult { + txid: string + vout: number + amount: BigNumber + address: string + confirms: number + spent: SpentInfo +} +```