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 @@ -1,3 +1,5 @@ +import BigNumber from 'bignumber.js'; + // Chronik methods export const getUtxosSingleHashChronik = async (chronik, hash160) => { @@ -67,3 +69,142 @@ const flatUtxos = allUtxos.flat(); return flatUtxos; }; + +const getSlpBalancesAndUtxosFromChronik = chronikUtxos => { + /* + + Convert chronik utxos (returned by getUtxosChronik function, above) to match + shape of existing slpBalancesAndUtxos object + + This means sequestering eToken utxos from non-eToken utxos + + For legacy reasons, the term "SLP" is still sometimes used to describe an eToken + + So, SLP utxos === eToken utxos, it's just a semantics difference here + + */ + + const nonSlpUtxos = []; + const slpUtxos = []; + for (let i = 0; i < chronikUtxos.length; i += 1) { + // Construct nonSlpUtxos and slpUtxos arrays + const thisUtxo = chronikUtxos[i]; + const isEtoken = typeof thisUtxo.slpToken !== 'undefined'; + if (isEtoken) { + slpUtxos.push(thisUtxo); + } else { + nonSlpUtxos.push(thisUtxo); + } + } + + // Iterate over the slpUtxos to create the 'tokens' object + let tokensById = {}; + + slpUtxos.forEach(slpUtxo => { + /* + Note that a wallet could have many eToken utxos all belonging to the same eToken + For example, a user could have 100 of a certain eToken, but this is composed of + four utxos, one for 17, one for 50, one for 30, one for 3 + */ + + // Start with the existing object for this particular token, if it exists + let token = tokensById[slpUtxo.slpMeta.tokenId]; + + if (token) { + if (slpUtxo.slpToken.amount) { + token.balance = token.balance.plus( + new BigNumber(slpUtxo.slpToken.amount), + ); + } + } else { + // If it does not exist, create it + token = {}; + token.tokenId = slpUtxo.slpMeta.tokenId; + if (slpUtxo.slpToken.amount) { + token.balance = new BigNumber(slpUtxo.slpToken.amount); + } else { + token.balance = new BigNumber(0); + } + tokensById[slpUtxo.slpMeta.tokenId] = token; + } + }); + + const tokens = Object.values(tokensById); + const chronikSlpBalancesAndUtxos = { slpUtxos, nonSlpUtxos, tokens }; + return chronikSlpBalancesAndUtxos; +}; + +const returnGetTokenInfoChronikPromise = tokenId => { + return new Promise((resolve, reject) => { + getTxDetailsChronik(tokenId).then( + result => { + const thisTokenInfo = result.slpTxData.genesisInfo; + thisTokenInfo.tokenId = tokenId; + // You only want the genesis info for tokenId + resolve(thisTokenInfo); + }, + err => { + reject(err); + }, + ); + }); +}; + +const addTokenInfo = async tokens => { + // for each token, get the genesis info + // parse token qty by decimal + const getTokenInfoPromises = []; + for (let i = 0; i < tokens.length; i += 1) { + const thisTokenId = tokens[i].tokenId; + const thisTokenInfoPromise = + returnGetTokenInfoChronikPromise(thisTokenId); + getTokenInfoPromises.push(thisTokenInfoPromise); + } + let tokenInfoArray = await Promise.all(getTokenInfoPromises); + // TODO iterate through tokens and add the required tokeninfo + // note: mb it's better if you organize the token info so it's accessible by tokenId index for this process + + // NB: tokenInfoArray should be in the same order as tokens, by tokenId + // Do not assume this + if (tokens.length !== tokenInfoArray.length) { + console.log( + `ERROR: tokenInfoArray length is ${tokenInfoArray.length}, while tokens length is ${tokens.length}`, + ); + } + for (let i = 0; i < tokens.length; i += 1) { + const thisToken = tokens[i]; + const thisTokenId = thisToken.tokenId; + tokenInfoArrayLoop: for (let j = 0; j < tokenInfoArray.length; j += 1) { + const tokenInfoForTokenId = tokenInfoArray[j].tokenId; + if (thisTokenId === tokenInfoForTokenId) { + const thisGenesisInfo = tokenInfoArray[j]; + // Add this info to the utxo + tokens[i].info = thisGenesisInfo; + // Adjust the token balance for tokenDecimals + const tokenDecimals = thisGenesisInfo.decimals; + // Adjust tokenQty per decimal places + /* + console.log( + `starting balance for ${thisTokenId} with ${tokenDecimals} decimals`, + tokens[i].balance.toString(), + ); + */ + tokens[i].balance = tokens[i].balance.shiftedBy( + -1 * tokenDecimals, + ); + /* + console.log( + `ending balance for ${thisTokenId}`, + tokens[i].balance.toString(), + ); + */ + + // you won't need it again, so remove it from tokenInfoArray + tokenInfoArray.slice(j, 1); + // do not iterate through the rest of tokenInfoArray once you have found what you are looking for + break tokenInfoArrayLoop; + } + } + } + return tokens; +};