Page MenuHomePhabricator

D12367.id36037.diff
No OneTemporary

D12367.id36037.diff

diff --git a/web/cashtab/src/components/Airdrop/Airdrop.js b/web/cashtab/src/components/Airdrop/Airdrop.js
--- a/web/cashtab/src/components/Airdrop/Airdrop.js
+++ b/web/cashtab/src/components/Airdrop/Airdrop.js
@@ -26,6 +26,7 @@
convertToEcashPrefix,
convertEcashtoEtokenAddr,
} from 'utils/cashMethods';
+import { getMintAddress } from 'utils/chronik';
import {
isValidTokenId,
isValidXecAirdrop,
@@ -255,6 +256,7 @@
// extract the eToken mint address
let genesisTx;
try {
+ // bch-api approach
genesisTx = await bchObj.RawTransactions.getRawTransaction(
formData.tokenId,
true,
@@ -270,12 +272,43 @@
passLoadingStatus(false);
return;
}
+
+ // Chronik approach
+ let mintEtokenAddressChronik;
+ try {
+ mintEtokenAddressChronik = await getMintAddress(
+ chronik,
+ bchObj,
+ formData.tokenId,
+ );
+ } catch (err) {
+ console.log(`Error in getMintAddress`, err);
+ errorNotification(
+ null,
+ 'Unable to retrieve minting address for eToken ID: ' +
+ formData.tokenId,
+ 'getMintAddress Error',
+ );
+ setIsAirdropCalcModalVisible(false);
+ passLoadingStatus(false);
+ return;
+ }
+
+ // bch-api address conversion
const mintEcashAddress = convertToEcashPrefix(
genesisTx.vout[1].scriptPubKey.addresses[0],
); //vout[0] is always the OP_RETURN output
const mintEtokenAddress =
convertEcashtoEtokenAddr(mintEcashAddress);
+ // Compare
+ if (mintEtokenAddressChronik === mintEtokenAddress) {
+ console.log(
+ `Chronik and bch-api got the same minting address`,
+ mintEtokenAddressChronik,
+ );
+ }
+
// remove the mint address from the recipients list
airdropList.delete(mintEtokenAddress);
}
diff --git a/web/cashtab/src/utils/__mocks__/chronikMintTxs.js b/web/cashtab/src/utils/__mocks__/chronikMintTxs.js
new file mode 100644
--- /dev/null
+++ b/web/cashtab/src/utils/__mocks__/chronikMintTxs.js
@@ -0,0 +1,251 @@
+export const mintingTxTabCash = {
+ txid: '50d8292c6255cda7afc6c8566fed3cf42a2794e9619740fe8f4c95431271410e',
+ version: 2,
+ inputs: [
+ {
+ prevOut: {
+ txid: 'be38b0488679e25823b7a72b925ac695a7b486e7f78122994b913f3079b0b939',
+ outIdx: 2,
+ },
+ inputScript:
+ '483045022100e28006843eb071ec6d8dd105284f2ca625a28f4dc85418910b59a5ab13fc6c2002205921fb12b541d1cd1a63e7e012aca5735df3398525f64bac04337d21029413614121034509251caa5f01e2787c436949eb94d71dcc451bcde5791ae5b7109255f5f0a3',
+ outputScript: '76a914b8d9512d2adf8b4e70c45c26b6b00d75c28eaa9688ac',
+ value: '91048',
+ sequenceNo: 4294967295,
+ slpBurn: {
+ token: {
+ amount: '0',
+ isMintBaton: false,
+ },
+ tokenId:
+ 'bd1acc4c986de57af8d6d2a64aecad8c30ee80f37ae9d066d758923732ddc9ba',
+ },
+ },
+ ],
+ outputs: [
+ {
+ value: '0',
+ outputScript:
+ '6a04534c500001010747454e455349530354424307746162636173681768747470733a2f2f636173687461626170702e636f6d2f4c0001000102080000000000000064',
+ },
+ {
+ value: '546',
+ outputScript: '76a914b8d9512d2adf8b4e70c45c26b6b00d75c28eaa9688ac',
+ slpToken: {
+ amount: '100',
+ isMintBaton: false,
+ },
+ spentBy: {
+ txid: '618d0dd8c0c5fa5a34c6515c865dd72bb76f8311cd6ee9aef153bab20dabc0e6',
+ outIdx: 1,
+ },
+ },
+ {
+ value: '546',
+ outputScript: '76a914b8d9512d2adf8b4e70c45c26b6b00d75c28eaa9688ac',
+ slpToken: {
+ amount: '0',
+ isMintBaton: true,
+ },
+ },
+ {
+ value: '89406',
+ outputScript: '76a914b8d9512d2adf8b4e70c45c26b6b00d75c28eaa9688ac',
+ spentBy: {
+ txid: '618d0dd8c0c5fa5a34c6515c865dd72bb76f8311cd6ee9aef153bab20dabc0e6',
+ outIdx: 0,
+ },
+ },
+ ],
+ lockTime: 0,
+ slpTxData: {
+ slpMeta: {
+ tokenType: 'FUNGIBLE',
+ txType: 'GENESIS',
+ tokenId:
+ '50d8292c6255cda7afc6c8566fed3cf42a2794e9619740fe8f4c95431271410e',
+ },
+ genesisInfo: {
+ tokenTicker: 'TBC',
+ tokenName: 'tabcash',
+ tokenDocumentUrl: 'https://cashtabapp.com/',
+ tokenDocumentHash: '',
+ decimals: 0,
+ },
+ },
+ block: {
+ height: 674143,
+ hash: '000000000000000034c77993a35c74fe2dddace27198681ca1e89e928d0c2fff',
+ timestamp: '1613859311',
+ },
+ timeFirstSeen: '0',
+ size: 336,
+ isCoinbase: false,
+ network: 'XEC',
+};
+export const mintingHash160TabCash = 'b8d9512d2adf8b4e70c45c26b6b00d75c28eaa96';
+export const mintingAddressBchFormatTabCash =
+ 'bitcoincash:qzudj5fd9t0cknnsc3wzdd4sp46u9r42jcnqnwfss0';
+export const mintingAddressTabCash =
+ 'etoken:qzudj5fd9t0cknnsc3wzdd4sp46u9r42jcynw8ydj0';
+
+export const mintingTxPoW = {
+ txid: 'f36e1b3d9a2aaf74f132fef3834e9743b945a667a4204e761b85f2e7b65fd41a',
+ version: 2,
+ inputs: [
+ {
+ prevOut: {
+ txid: '33938d6bd403e4ffef94de3e9e2ba487f095dcba3544ac8fad4a93808cea0116',
+ outIdx: 1,
+ },
+ inputScript:
+ '483045022100dad1d237b541b4a4d29197dbb01fa9755c2e17bbafb42855f38442b428f0df6b02205772d3fb00b7a053b07169e1534770c091fce42b9e1d63199f46ff89856b3fc6412102ceb4a6eca1eec20ff8e7780326932e8d8295489628c7f2ec9acf8f37f639235e',
+ outputScript: '76a91485bab3680833cd9b3cc60953344fa740a2235bbd88ac',
+ value: '49998867',
+ sequenceNo: 4294967295,
+ },
+ ],
+ outputs: [
+ {
+ value: '0',
+ outputScript:
+ '6a04534c500001010747454e4553495303504f571850726f6f666f6657726974696e672e636f6d20546f6b656e2168747470733a2f2f7777772e70726f6f666f6677726974696e672e636f6d2f32364c0001004c000800000000000f4240',
+ },
+ {
+ value: '546',
+ outputScript: '76a91485bab3680833cd9b3cc60953344fa740a2235bbd88ac',
+ slpToken: {
+ amount: '1000000',
+ isMintBaton: false,
+ },
+ spentBy: {
+ txid: '69238630eb9e6a9864bf6970ff5d326800cea41a819feebecfe1a6f0ed651f5c',
+ outIdx: 1,
+ },
+ },
+ {
+ value: '49997563',
+ outputScript: '76a91485bab3680833cd9b3cc60953344fa740a2235bbd88ac',
+ spentBy: {
+ txid: '3c665488929f852d93a5dfb6e4b4df7bc8f7a25fb4a2480d39e3de7a30437f69',
+ outIdx: 0,
+ },
+ },
+ ],
+ lockTime: 0,
+ slpTxData: {
+ slpMeta: {
+ tokenType: 'FUNGIBLE',
+ txType: 'GENESIS',
+ tokenId:
+ 'f36e1b3d9a2aaf74f132fef3834e9743b945a667a4204e761b85f2e7b65fd41a',
+ },
+ genesisInfo: {
+ tokenTicker: 'POW',
+ tokenName: 'ProofofWriting.com Token',
+ tokenDocumentUrl: 'https://www.proofofwriting.com/26',
+ tokenDocumentHash: '',
+ decimals: 0,
+ },
+ },
+ block: {
+ height: 685949,
+ hash: '0000000000000000436e71d5291d2fb067decc838dcb85a99ff6da1d28b89fad',
+ timestamp: '1620712051',
+ },
+ timeFirstSeen: '0',
+ size: 329,
+ isCoinbase: false,
+ network: 'XEC',
+};
+export const mintingHash160PoW = '85bab3680833cd9b3cc60953344fa740a2235bbd';
+export const mintingAddressBchFormatPoW =
+ 'bitcoincash:qzzm4vmgpqeumxeuccy4xdz05aq2yg6mh52phz3zy5';
+export const mintingAddressPoW =
+ 'etoken:qzzm4vmgpqeumxeuccy4xdz05aq2yg6mh5aj2tulx5';
+
+export const mintingTxAlita = {
+ txid: '54dc2ecd5251f8dfda4c4f15ce05272116b01326076240e2b9cc0104d33b1484',
+ version: 2,
+ inputs: [
+ {
+ prevOut: {
+ txid: '72eeff7b43dc066164d92e4c3fece47af3a40e89d46e893df1647cd29dd9f1e3',
+ outIdx: 0,
+ },
+ inputScript:
+ '473044022075166617aa473e86c72f34a5576029eb8766a035b481864ebc75759155efcce00220147e2d7e662123bd728fac700f109a245a0278959f65fc402a1e912e0a5732004121034cdb43b7a1277c4d818dc177aaea4e0bed5d464d240839d5488a278b716facd5',
+ outputScript: '76a914f5f740bc76e56b77bcab8b4d7f888167f416fc6888ac',
+ value: '1000',
+ sequenceNo: 4294967295,
+ },
+ {
+ prevOut: {
+ txid: '46b6f61ca026e243d55668bf304df6a21e1fcb2113943cc6bd1fdeceaae85612',
+ outIdx: 2,
+ },
+ inputScript:
+ '4830450221009e98db4b91441190bb7e4745b9f249201d0b54c81c0a816af5f3491ffb21a7e902205a4d1347a5a9133c14e4f55319af00f1df836eba6552f30b44640e9373f4cabf4121034cdb43b7a1277c4d818dc177aaea4e0bed5d464d240839d5488a278b716facd5',
+ outputScript: '76a914f5f740bc76e56b77bcab8b4d7f888167f416fc6888ac',
+ value: '750918004',
+ sequenceNo: 4294967295,
+ },
+ ],
+ outputs: [
+ {
+ value: '0',
+ outputScript:
+ '6a04534c500001010747454e4553495305416c69746105416c6974610a616c6974612e636173684c0001044c00080000befe6f672000',
+ },
+ {
+ value: '546',
+ outputScript: '76a914f5f740bc76e56b77bcab8b4d7f888167f416fc6888ac',
+ slpToken: {
+ amount: '210000000000000',
+ isMintBaton: false,
+ },
+ spentBy: {
+ txid: '2c336374c05f1c8f278d2a1d5f3195a17fe1bc50189ff67c9769a6afcd908ea9',
+ outIdx: 1,
+ },
+ },
+ {
+ value: '750917637',
+ outputScript: '76a914f5f740bc76e56b77bcab8b4d7f888167f416fc6888ac',
+ spentBy: {
+ txid: 'ca70157d5cf6275e0a36adbc3fabf671e3987f343cb35ec4ee7ed5c8d37b3233',
+ outIdx: 0,
+ },
+ },
+ ],
+ lockTime: 0,
+ slpTxData: {
+ slpMeta: {
+ tokenType: 'FUNGIBLE',
+ txType: 'GENESIS',
+ tokenId:
+ '54dc2ecd5251f8dfda4c4f15ce05272116b01326076240e2b9cc0104d33b1484',
+ },
+ genesisInfo: {
+ tokenTicker: 'Alita',
+ tokenName: 'Alita',
+ tokenDocumentUrl: 'alita.cash',
+ tokenDocumentHash: '',
+ decimals: 4,
+ },
+ },
+ block: {
+ height: 756373,
+ hash: '00000000000000000d62f1b66c08f0976bcdec2f08face2892ae4474b50100d9',
+ timestamp: '1662611972',
+ },
+ timeFirstSeen: '1662611666',
+ size: 436,
+ isCoinbase: false,
+ network: 'XEC',
+};
+export const mintingHash160Alita = 'f5f740bc76e56b77bcab8b4d7f888167f416fc68';
+export const mintingAddressBchFormatAlita =
+ 'bitcoincash:qr6lws9uwmjkkaau4w956lugs9nlg9hudqf8w5lusm';
+export const mintingAddressAlita =
+ 'etoken:qr6lws9uwmjkkaau4w956lugs9nlg9hudq75najpjm';
diff --git a/web/cashtab/src/utils/__tests__/chronik.test.js b/web/cashtab/src/utils/__tests__/chronik.test.js
--- a/web/cashtab/src/utils/__tests__/chronik.test.js
+++ b/web/cashtab/src/utils/__tests__/chronik.test.js
@@ -9,6 +9,7 @@
flattenChronikTxHistory,
sortAndTrimChronikTxHistory,
parseChronikTx,
+ getMintAddress,
} from 'utils/chronik';
import {
mockChronikUtxos,
@@ -54,6 +55,20 @@
mockTokenBurnWithDecimalsTx,
mockReceivedEtokenTx,
} from '../__mocks__/chronikTxHistory';
+import {
+ mintingTxTabCash,
+ mintingAddressTabCash,
+ mintingAddressBchFormatTabCash,
+ mintingHash160TabCash,
+ mintingTxPoW,
+ mintingAddressPoW,
+ mintingAddressBchFormatPoW,
+ mintingHash160PoW,
+ mintingTxAlita,
+ mintingAddressAlita,
+ mintingAddressBchFormatAlita,
+ mintingHash160Alita,
+} from '../__mocks__/chronikMintTxs';
import { ChronikClient } from 'chronik-client';
import { when } from 'jest-when';
import BCHJS from '@psf/bch-js';
@@ -733,3 +748,98 @@
replyAddress: 'ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6',
});
});
+
+it(`getMintAddress successfully parses chronik.tx response to determine mint address for TabCash token`, async () => {
+ // Initialize chronik
+ const chronik = new ChronikClient(
+ 'https://FakeChronikUrlToEnsureMocksOnly.com',
+ );
+ const BCH = new BCHJS({
+ restURL: 'https://FakeBchApiUrlToEnsureMocksOnly.com',
+ });
+ /*
+ Mock the API response from chronik.tx('tokenId') called
+ in returnGetTokenInfoChronikPromise -- for each tokenId used
+ */
+ chronik.tx = jest.fn();
+
+ when(chronik.tx)
+ .calledWith(mintingTxTabCash.txid)
+ .mockResolvedValue(mintingTxTabCash);
+
+ // This function needs to be mocked as bch-js functions that require Buffer types do not work in jest environment
+ BCH.Address.hash160ToCash = jest
+ .fn()
+ .mockReturnValue(mintingAddressBchFormatTabCash);
+
+ expect(await getMintAddress(chronik, BCH, mintingTxTabCash.txid)).toBe(
+ mintingAddressTabCash,
+ );
+
+ // spy on mintingHash160
+ expect(BCH.Address.hash160ToCash).toHaveBeenCalledWith(
+ mintingHash160TabCash,
+ );
+});
+
+it(`getMintAddress successfully parses chronik.tx response to determine mint address for PoW token`, async () => {
+ // Initialize chronik
+ const chronik = new ChronikClient(
+ 'https://FakeChronikUrlToEnsureMocksOnly.com',
+ );
+ const BCH = new BCHJS({
+ restURL: 'https://FakeBchApiUrlToEnsureMocksOnly.com',
+ });
+ /*
+ Mock the API response from chronik.tx('tokenId') called
+ in returnGetTokenInfoChronikPromise -- for each tokenId used
+ */
+ chronik.tx = jest.fn();
+
+ when(chronik.tx)
+ .calledWith(mintingTxPoW.txid)
+ .mockResolvedValue(mintingTxPoW);
+
+ // This function needs to be mocked as bch-js functions that require Buffer types do not work in jest environment
+ BCH.Address.hash160ToCash = jest
+ .fn()
+ .mockReturnValue(mintingAddressBchFormatPoW);
+
+ expect(await getMintAddress(chronik, BCH, mintingTxPoW.txid)).toBe(
+ mintingAddressPoW,
+ );
+
+ // spy on mintingHash160
+ expect(BCH.Address.hash160ToCash).toHaveBeenCalledWith(mintingHash160PoW);
+});
+
+it(`getMintAddress successfully parses chronik.tx response to determine mint address for Alita token`, async () => {
+ // Initialize chronik
+ const chronik = new ChronikClient(
+ 'https://FakeChronikUrlToEnsureMocksOnly.com',
+ );
+ const BCH = new BCHJS({
+ restURL: 'https://FakeBchApiUrlToEnsureMocksOnly.com',
+ });
+ /*
+ Mock the API response from chronik.tx('tokenId') called
+ in returnGetTokenInfoChronikPromise -- for each tokenId used
+ */
+ chronik.tx = jest.fn();
+
+ when(chronik.tx)
+ .calledWith(mintingTxAlita.txid)
+ .mockResolvedValue(mintingTxAlita);
+
+ // This function needs to be mocked as bch-js functions that require Buffer types do not work in jest environment
+ BCH.Address.hash160ToCash = jest
+ .fn()
+ .mockReturnValue(mintingAddressBchFormatAlita);
+
+ expect(await getMintAddress(chronik, BCH, mintingTxAlita.txid)).toBe(
+ mintingAddressAlita,
+ );
+
+ // spy on mintingHash160
+ expect(BCH.Address.hash160ToCash).toHaveBeenCalledWith(mintingHash160Alita);
+});
diff --git a/web/cashtab/src/utils/chronik.js b/web/cashtab/src/utils/chronik.js
--- a/web/cashtab/src/utils/chronik.js
+++ b/web/cashtab/src/utils/chronik.js
@@ -6,6 +6,8 @@
convertToEncryptStruct,
getHashArrayFromWallet,
getUtxoWif,
+ convertEcashtoEtokenAddr,
+ convertToEcashPrefix,
} from 'utils/cashMethods';
import ecies from 'ecies-lite';
import wif from 'wif';
@@ -902,3 +904,44 @@
txHistoryNewTokensToCache,
};
};
+
+export const getMintAddress = async (chronik, BCH, tokenId) => {
+ let genesisTx;
+ let mintingHash160;
+ try {
+ genesisTx = await chronik.tx(tokenId);
+ // get the minting address chronik
+ // iterate over the inputs
+ const { outputs } = genesisTx;
+ for (let i = 0; i < outputs.length; i += 1) {
+ const thisOutput = outputs[i];
+ // Check to see if this output has eTokens
+ if (
+ thisOutput &&
+ thisOutput.slpToken &&
+ typeof thisOutput.slpToken !== 'undefined' &&
+ thisOutput.slpToken.amount &&
+ Number(thisOutput.slpToken.amount) > 0
+ ) {
+ // then this is the minting address
+ const thisOutputHash160 = thisOutput.outputScript;
+ mintingHash160 = thisOutputHash160.substring(
+ thisOutputHash160.indexOf('76a914') + '76a914'.length,
+ thisOutputHash160.lastIndexOf('88ac'),
+ );
+ }
+ }
+ const mintingAdressBchFormat =
+ BCH.Address.hash160ToCash(mintingHash160);
+ const mintEcashAddressChronik = convertToEcashPrefix(
+ mintingAdressBchFormat,
+ );
+ const mintEtokenAddressChronik = convertEcashtoEtokenAddr(
+ mintEcashAddressChronik,
+ );
+ return mintEtokenAddressChronik;
+ } catch (err) {
+ console.log(`Error in getMintAddress`, err);
+ return err;
+ }
+};

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 26, 10:34 (1 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5571928
Default Alt Text
D12367.id36037.diff (17 KB)

Event Timeline