diff --git a/apps/examples/README.md b/apps/examples/README.md --- a/apps/examples/README.md +++ b/apps/examples/README.md @@ -32,7 +32,13 @@ [] Collating inputs and outputs for sending XEC [] Collating inputs and outputs for sending eTokens [] Building and broadcasting transactions -[] Querying eToken genesis details and current stats + +[x] [Querying eToken details - getTokenDetails()](scripts/getTokenDetails.js) + +Usage: `npm run getTokenDetails <token id>` + +Example: `npm run getTokenDetails 861dede36f7f73f0af4e979fc3a3f77f37d53fe27be4444601150c21619635f4` + [] Querying holders of a particular eToken [] Querying blockchain info [] Using websockets to listen for confirmation of a transaction diff --git a/apps/examples/mocks/chronikMock.js b/apps/examples/mocks/chronikMock.js --- a/apps/examples/mocks/chronikMock.js +++ b/apps/examples/mocks/chronikMock.js @@ -19,6 +19,7 @@ blockchainInfo: {}, txHistory: [], tx: {}, + token: {}, }; self.mockedMethods = { p2pkh: {}, p2sh: {} }; @@ -27,6 +28,14 @@ self.block = function (blockHashOrHeight) { return self.mockedResponses.block[blockHashOrHeight]; }; + self.token = async function (tokenId) { + const mockedTxResponse = self.mockedResponses.token[tokenId]; + // If the user set this response to be an error, throw it + if (mockedTxResponse instanceof Error) { + throw mockedTxResponse; + } + return self.mockedResponses.token[tokenId]; + }; self.blockchainInfo = function () { return self.mockedResponses.blockchainInfo; }; diff --git a/apps/examples/package.json b/apps/examples/package.json --- a/apps/examples/package.json +++ b/apps/examples/package.json @@ -13,6 +13,7 @@ "coverage": "nyc mocha", "junit": "mocha test --reporter mocha-junit-reporter", "getDetailsFromTxid": "node scripts/getDetailsFromTxid.js", - "getTxHistoryFromAddress": "node scripts/getTxHistoryFromAddress.js" + "getTxHistoryFromAddress": "node scripts/getTxHistoryFromAddress.js", + "getTokenDetails": "node scripts/getTokenDetails" } } diff --git a/apps/examples/scripts/getTokenDetails.js b/apps/examples/scripts/getTokenDetails.js new file mode 100644 --- /dev/null +++ b/apps/examples/scripts/getTokenDetails.js @@ -0,0 +1,37 @@ +// Copyright (c) 2023 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +'use strict'; + +/* + Returns genesis info and stats corresponding to a given eToken ID + + @param {object} Chronik client instance + @param {string} eToken ID + @returns {object} slpTxData and tokenStats JSON response from chronik + @throws {error} on chronik error + @usage: + const { ChronikClient } = require('chronik-client'); + const chronik = new ChronikClient('https://chronik.fabien.cash'); + const tokenInfo = await getTokenDetails(chronik, 'fd9a775...fce0e'); +*/ +async function getTokenDetails(chronik, tokenId) { + const tokenDetails = await chronik.token(tokenId); + return tokenDetails; +} + +// Executed via 'npm run getTokenDetails <token id>' +(async () => { + // extract args provided at CLI + const argsFromCli = process.argv.slice(2); + const tokenId = argsFromCli[0]; + + // instantiate chronik-client + const { ChronikClient } = require('chronik-client'); + const chronik = new ChronikClient('https://chronik.fabien.cash'); + + const tokenDetails = await getTokenDetails(chronik, tokenId); + console.log(JSON.stringify(tokenDetails, null, 2)); +})(); + +module.exports.getTokenDetails = getTokenDetails; diff --git a/apps/examples/test/getTokenDetails.test.js b/apps/examples/test/getTokenDetails.test.js new file mode 100644 --- /dev/null +++ b/apps/examples/test/getTokenDetails.test.js @@ -0,0 +1,57 @@ +// Copyright (c) 2023 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +'use strict'; +const assert = require('assert'); +const { getTokenDetails } = require('../scripts/getTokenDetails'); +const { mockToken } = require('../mocks/chronikResponses'); + +// Mock chronik +const { MockChronikClient } = require('../mocks/chronikMock'); + +describe('App dev example code: getTokenDetails.js', function () { + it('getTokenDetails() correctly returns a token JSON response object', async function () { + const tokenId = + '861dede36f7f73f0af4e979fc3a3f77f37d53fe27be4444601150c21619635f4'; + + // Initialize chronik mock + const mockedChronik = new MockChronikClient(); + + // Tell mockedChronik what response we expect + mockedChronik.setMock('token', { + input: tokenId, + output: mockToken, + }); + + const result = await getTokenDetails(mockedChronik, tokenId); + + // Check that the token details match + assert.deepEqual(result, mockToken); + }); + + it('getTokenDetails throws error from the chronik.token() call for an invalid token id', async function () { + // Initialize chronik mock + const mockedChronik = new MockChronikClient(); + const invalidTokenId = + '861dede36f7f73f0af4e979INVALIDDDDDe4444601150c21619635f4'; + const expectedChronikError = new Error( + `(token-txid-not-found): Token txid not found: ${invalidTokenId}`, + ); + + // Tell mockedChronik what responses we expect + mockedChronik.setMock('token', { + input: invalidTokenId, + output: expectedChronikError, + }); + + await assert.rejects( + async () => { + await getTokenDetails(mockedChronik, invalidTokenId); + }, + { + name: 'Error', + message: `(token-txid-not-found): Token txid not found: ${invalidTokenId}`, + }, + ); + }); +});