+
+
${getFormattedTime(time)}
+
-
-
-
${getFormattedTime(time)}
-
- ${receiver ? html`
-
- `: ''}
- ${amount ? html`
-
-
Amount
- ${formatAmount(amount)}
-
- `: ''}
+ ${finalReceiver ? html`
+
-
-
FLO Data
-
${floData}
-
Block
-
${blockHeight}
-
Block Confirmations
-
${confirmations}
- ${nftHash ? html`
-
NFT hash
-
- `: ''}
+ ` : ''}
+ ${finalAmount ? html`
+
+
Amount
+ ${formatAmount(finalAmount)}
-
- ${subTransactions?.length ? html`
-
-
-
Sub Transactions
-
These are Off-chain transactions that are triggered by above transaction
-
-
${renderedSubTransactions}
-
- `: ''}
+ ` : ''}
- `);
- getRef("page_title").textContent = "Transaction";
- } catch (e) {
- console.error(e)
- renderElem(getRef("page_container"), html`${render.errorPage(e)}`);
- }
- })
+
+
FLO Data
+
${floData}
+
Block
+
${finalBlock}
+
Block Confirmations
+
${confirmations}
+ ${nftHash ? html`
+
NFT hash
+
+ ` : ''}
+
+
+ ${subTransactions?.length ? html`
+
+
+
Sub Transactions
+
These are Off-chain transactions that are triggered by above transaction
+
+
${renderedSubTransactions}
+
+ ` : ''}
+
+ `);
+
+ getRef("page_title").textContent = "Transaction";
+ } catch (e) {
+ console.error("๐ฅ Exception in route:", e);
+ renderElem(getRef("page_container"), html`${render.errorPage(e)}`);
+ }
+});
+
+
+
+
+
+
router.addRoute('404', state => {
renderElem(getRef("page_container"), html`${render.errorPage('404 Not Found')}`);
})
@@ -2010,79 +2250,169 @@
return str.replace(/ /g, "-");
}
- function renderTransactions(transactions = []) {
- let parsedTxs = parseTransactions(transactions);
- let groupedTxs = new Map();
+function renderTransactions(transactions = []) {
+ console.log("๐ก Raw token transactions (input):", transactions);
- parsedTxs.forEach((tx) => {
- const { hash, transactionTrigger } = tx;
- const key = hash || transactionTrigger;
+ // โ
Step 1: Merge nested fields and apply fallbacks
+ const mergedTxs = transactions.map((tx, i) => {
+ const merged = {
+ ...(tx.transactionDetails || {}),
+ ...(tx.parsedFloData || {}),
+ ...tx
+ };
- if (!groupedTxs.has(key)) {
- groupedTxs.set(key, {
- sourceTransaction: {},
- offChainTransactions: [],
- type: transactionTrigger ? 'compoundTransaction' : undefined,
- });
- }
-
- if (hash) {
- groupedTxs.get(key).sourceTransaction = tx;
- } else {
- const { tokenIdentification, senderAddress, receiverAddress, onChain, tokenAmount, time, contractName } = tx;
- groupedTxs.get(key).offChainTransactions.push({
- tokenIdentification,
- receiverAddress,
- tokenAmount,
- });
- if (time)
- groupedTxs.get(key).sourceTransaction.time = time
- if (contractName)
- groupedTxs.get(key).sourceTransaction.contractName = contractName
- if (senderAddress)
- groupedTxs.get(key).sourceTransaction.contractAddress = senderAddress
- }
- });
-
- // Convert the Map to an array of values for sorting
- const sortedTxs = [...groupedTxs.values()].sort((a, b) => b.sourceTransaction.time - a.sourceTransaction.time);
-
- // Replace parsedTxs with sortedTxs
- parsedTxs = sortedTxs.map((tx) => {
- if (tx.type === 'compoundTransaction') {
- return {
- ...tx.sourceTransaction,
- offChainTransactions: tx.offChainTransactions,
- type: tx.type,
- };
- }
- return tx.sourceTransaction;
- });
- const renderedTransactions = parsedTxs.map(tx => {
- switch (tx.type) {
- case 'tokentransfer':
- case 'nfttransfer':
- return render.tokenTransferCard(tx)
- case 'contractdeposit':
- return render.contractDepositCard(tx)
- case 'contracttransfer':
- return render.contractTransferCard(tx);
- case 'tokenincorp':
- case 'nftincorp':
- return render.tokenCreationCard(tx);
- case 'contractincorp':
- return render.contractCreationCard(tx);
- case 'contracttrigger':
- return render.contractTriggerCard(tx);
- case 'offChainTransfer':
- return render.offChainTransferCard(tx);
- case 'compoundTransaction':
- return render.compoundTransactionCard(tx)
- }
- })
- return html`${renderedTransactions.length ? renderedTransactions : html`
No transactions found
`}`
+ if (merged.onChain === undefined) {
+ merged.onChain = true;
+ console.log(`๐ฉน TX #${i}: Defaulted onChain to true`);
}
+ if (!merged.transactionTrigger && (
+ merged.contractName || (merged.floData || "").includes('@')
+ )) {
+ merged.transactionTrigger = merged.txid || merged.hash;
+ console.log(`๐งท TX #${i}: Injected transactionTrigger = ${merged.transactionTrigger}`);
+ }
+
+ console.log(`๐ TX #${i} after merge:`, merged);
+ return merged;
+ });
+
+ console.log("๐ง Merged Transactions (post-merge):", mergedTxs);
+
+ // โ
Step 2: Parse
+ let parsedTxs = parseTransactions(mergedTxs);
+ console.log("๐งฉ Parsed Transactions (from parser):", parsedTxs);
+
+ // โ
Step 3: Grouping
+ let groupedTxs = new Map();
+
+ parsedTxs.forEach((tx, i) => {
+ console.log(`๐ฆ Grouping TX #${i}:`, tx);
+ const { hash, transactionTrigger, type } = tx;
+ const key = hash || transactionTrigger;
+
+ if (!groupedTxs.has(key)) {
+ groupedTxs.set(key, {
+ sourceTransaction: undefined,
+ offChainTransactions: [],
+ });
+ console.log(`๐ Created new group for key: ${key}`);
+ }
+
+ const group = groupedTxs.get(key);
+
+ if (hash || (type === 'trigger' && !tx.onChain)) {
+ group.sourceTransaction = tx;
+ console.log(`๐ Set sourceTransaction for group ${key}:`, tx);
+ } else if (type?.includes('tokenswap') || type === 'offChainTransfer') {
+ group.offChainTransactions.push(tx);
+ console.log(`๐ Added to offChainTransactions for group ${key}:`, tx);
+
+ if (!group.sourceTransaction) group.sourceTransaction = {};
+ if (!group.sourceTransaction.time && tx.time)
+ group.sourceTransaction.time = tx.time;
+ if (!group.sourceTransaction.contractName && tx.contractName)
+ group.sourceTransaction.contractName = tx.contractName;
+ if (!group.sourceTransaction.contractAddress && tx.senderAddress)
+ group.sourceTransaction.contractAddress = tx.senderAddress;
+ } else {
+ group.sourceTransaction = tx;
+ console.log(`๐ Set default sourceTransaction for group ${key}:`, tx);
+ }
+ });
+
+ // โ
Step 3.5: Ensure fallback sourceTransaction
+ for (const [key, group] of groupedTxs.entries()) {
+ if (!group.sourceTransaction && group.offChainTransactions.length) {
+ group.sourceTransaction = group.offChainTransactions[0];
+ console.log(`๐ง Fallback sourceTransaction assigned for group ${key}`);
+ }
+ }
+
+ // โ
Step 4: Flatten + sort
+ const sortedTxs = [...groupedTxs.values()].sort((a, b) =>
+ (b.sourceTransaction?.time || 0) - (a.sourceTransaction?.time || 0)
+ );
+ console.log("๐ Sorted Transactions:", sortedTxs);
+
+ parsedTxs = [];
+
+ sortedTxs.forEach((group, i) => {
+ const { sourceTransaction, offChainTransactions } = group;
+
+ if (offChainTransactions.length) {
+ console.log(`๐งฌ TX Group #${i} is compound:`, sourceTransaction);
+ parsedTxs.push({
+ ...sourceTransaction,
+ offChainTransactions,
+ type: 'compoundTransaction',
+ });
+
+ if (['trigger', 'contracttrigger'].includes(sourceTransaction?.type)) {
+ const triggerCard = {
+ ...sourceTransaction,
+ type: 'trigger',
+ };
+
+ if (!triggerCard.hash && triggerCard.transactionTrigger) {
+ triggerCard.hash = triggerCard.transactionTrigger;
+ console.log(`๐ TX Group #${i}: Fallback hash set for trigger = ${triggerCard.hash}`);
+ }
+
+ parsedTxs.push(triggerCard);
+ }
+ } else if (sourceTransaction) {
+ console.log(`โ
TX Group #${i} has source only:`, sourceTransaction);
+ parsedTxs.push(sourceTransaction);
+ } else {
+ console.warn(`โ TX Group #${i} has no sourceTransaction and was skipped`);
+ }
+ });
+
+ console.log("๐งฑ Final transactions for rendering (flattened):", parsedTxs);
+
+ // โ
Step 5: Rendering
+ const renderedTransactions = parsedTxs.map((tx, i) => {
+ console.log(`๐จ Rendering TX #${i} of type '${tx.type}':`, tx);
+ switch (tx.type) {
+ case 'tokentransfer':
+ case 'nfttransfer':
+ return render.tokenTransferCard(tx);
+ case 'contractdeposit':
+ return render.contractDepositCard(tx);
+ case 'contracttransfer':
+ return render.contractTransferCard(tx);
+ case 'tokenincorp':
+ case 'nftincorp':
+ return render.tokenCreationCard(tx);
+ case 'contractincorp':
+ return render.contractCreationCard(tx);
+ case 'trigger':
+ return render.contractTriggerCard(tx);
+ case 'offChainTransfer':
+ return render.offChainTransferCard(tx);
+ case 'compoundTransaction':
+ return render.compoundTransactionCard(tx);
+ default:
+ console.warn(`โ ๏ธ Unrecognized transaction type '${tx.type}'`, tx);
+ return html`
Unknown transaction
`;
+ }
+ });
+
+ console.log("๐ Final rendered HTML cards:", renderedTransactions);
+
+ return html`${renderedTransactions.length
+ ? renderedTransactions
+ : html`
No transactions found
`}`;
+}
+
+
+
+
+
+
+
+
getRef('suggestions').addEventListener('keyup', e => {
if (e.target.closest('.suggestion') && e.key === 'Enter') {
processNavbarSearch()
@@ -2102,13 +2432,23 @@
renderElem(getRef('suggestions'), html`${renderedSuggestions}`)
}, 100))
async function getBannerData() {
- const { systemTransactionCount, systemAddressCount } = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/info`)
- return {
- topToken: "RUPEE",
- totalTransactions: systemTransactionCount,
- walletAddresses: systemAddressCount,
+ try {
+ console.log("๐ก Fetching banner data from /api/v2/info...");
+ const { systemTransactionCount, systemAddressCount } =
+ await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/info`);
+ console.log("โ
Banner data received:", { systemTransactionCount, systemAddressCount });
+
+ return {
+ topToken: "RUPEE",
+ totalTransactions: systemTransactionCount,
+ walletAddresses: systemAddressCount,
+ };
+ } catch (err) {
+ console.error("โ Failed to fetch banner data:", err);
+ throw err;
}
}
+
let currentViewIndex = 0;
function changeView(e) {
const targetWrapper = e.target.dataset.target;
@@ -2118,355 +2458,624 @@
}
function getLatestTxs() {
+ console.log("๐ก Fetching latest transactions...");
return new Promise((resolve, reject) => {
fetchJson(`${floGlobals.tokenApiUrl}/api/v2/latestTransactionDetails?limit=4`)
.then(function (latestTxs) {
- resolve(latestTxs.latestTransactions)
- }).catch((err) => {
- reject(err)
+ console.log("โ
Latest transactions received:", latestTxs);
+ resolve(latestTxs.latestTransactions);
+ })
+ .catch((err) => {
+ console.error("โ Failed to fetch latest transactions:", err);
+ reject(err);
});
- })
+ });
}
+
function getTokenInfo(thisToken) {
+ console.log(`๐ก Fetching token info for: ${thisToken}`);
return new Promise((resolve, reject) => {
- fetchJson(
- `${floGlobals.tokenApiUrl}/api/v2/tokenInfo/${thisToken.toLowerCase()}`
- ).then(function (tokenInfo) {
- if (tokenInfo.result === "error")
- reject(tokenInfo.description);
- let associatedSC = {};
- tokenInfo.associatedSmartContracts.forEach((sc) => {
- associatedSC[`${sc.contractName}-${sc.contractAddress}`] = sc;
+ fetchJson(`${floGlobals.tokenApiUrl}/api/v2/tokenInfo/${thisToken.toLowerCase()}`)
+ .then(function (tokenInfo) {
+ console.log("โ
Token info received:", tokenInfo);
+ if (tokenInfo.result === "error") {
+ console.error("โ Token info error:", tokenInfo.description);
+ reject(tokenInfo.description);
+ return;
+ }
+ let associatedSC = {};
+ tokenInfo.associatedSmartContracts.forEach((sc) => {
+ associatedSC[`${sc.contractName}_${sc.contractAddress}`] = sc;
+ });
+ resolve({
+ token: tokenInfo["token"],
+ supply: tokenInfo["tokenSupply"],
+ incAddress: tokenInfo["incorporationAddress"],
+ associatedContracts: associatedSC,
+ blockchainReference: tokenInfo["blockchainReference"],
+ });
+ })
+ .catch((err) => {
+ console.error("โ Failed to fetch token info:", err);
+ reject(err);
});
- resolve({
- token: tokenInfo["token"],
- supply: tokenInfo["tokenSupply"],
- incAddress: tokenInfo["incorporationAddress"],
- associatedContracts: associatedSC,
- blockchainReference: tokenInfo["blockchainReference"],
- });
- }).catch((err) => {
- reject(err);
- });
- })
+ });
}
+
async function getTokenBalances(tokenName) {
- const tokenDetails = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/tokenBalances/` + tokenName)
- return tokenDetails.balances
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/tokenBalances/${tokenName}`;
+ console.log(`๐ก Fetching token balances from: ${url}`);
+ const tokenDetails = await fetchJson(url);
+ console.log("๐ฆ Token balances fetched:", tokenDetails);
+ return tokenDetails.balances;
+ } catch (error) {
+ console.error("โ Error fetching token balances:", error);
+ return [];
+ }
}
async function getTokenTransactions(tokenName) {
- const transactions = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/tokenTransactions/` + tokenName)
- return transactions.transactions
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/tokenTransactions/${tokenName}`;
+ console.log(`๐ก Fetching token transactions from: ${url}`);
+ const transactions = await fetchJson(url);
+ console.log("๐ฆ Token transactions fetched:", transactions);
+ return transactions.transactions;
+ } catch (error) {
+ console.error("โ Error fetching token transactions:", error);
+ return [];
+ }
}
+
async function getBlockInfo(thisBlock) {
- const info = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/blockDetails/${thisBlock}`);
- const { height, size, reward, hash, difficulty, nonce, tx } = info.blockDetails
- return {
- blockHeight: height,
- size: size,
- transactions: tx,
- reward: reward,
- hash: hash,
- difficulty: difficulty,
- nonce: nonce,
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/blockDetails/${thisBlock}`;
+ console.log(`๐ก Fetching block info from: ${url}`);
+ const info = await fetchJson(url);
+ console.log("๐ฆ Block info fetched:", info);
+
+ const { height, size, reward, hash, difficulty, nonce, tx } = info.blockDetails || {};
+
+ return {
+ blockHeight: height,
+ size: size,
+ transactions: tx,
+ reward: reward,
+ hash: hash,
+ difficulty: difficulty,
+ nonce: nonce,
+ };
+ } catch (error) {
+ console.error("โ Error fetching block info:", error);
+ return null;
}
}
async function getBlockTransactions(thisBlock) {
- const blockTransactions = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/blockTransactions/${thisBlock}`)
- return blockTransactions.transactions
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/blockTransactions/${thisBlock}`;
+ console.log(`๐ก Fetching block transactions from: ${url}`);
+ const blockTransactions = await fetchJson(url);
+ console.log("๐ฆ Block transactions fetched:", blockTransactions);
+ return blockTransactions.transactions || [];
+ } catch (error) {
+ console.error("โ Error fetching block transactions:", error);
+ return [];
+ }
}
async function getContractInfo(contract) {
- const info = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/smartContractInfo?contractName=${contract.name}&contractAddress=${contract.address}`);
- const {
- contractInfo: {
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/smartContractInfo?contractName=${contract.name}&contractAddress=${contract.address}`;
+ console.log(`๐ก Fetching smart contract info from: ${url}`);
+ const info = await fetchJson(url);
+ console.log("๐ฆ Smart contract info fetched:", info);
+
+ const {
+ contractInfo: {
+ contractType,
+ numberOfDeposits,
+ numberOfParticipants,
+ priceType,
+ oracle_address,
+ contractSubtype,
+ status,
+ expiryTime,
+ payeeAddress,
+ userChoices,
+ tokenIdentification,
+ acceptingToken,
+ sellingToken,
+ contractAmount,
+ minimumsubscriptionamount,
+ maximumsubscriptionamount,
+ totalHonorAmount,
+ totalParticipationAmount,
+ price,
+ currentDepositBalance
+ },
+ contractAddress,
+ contractName
+ } = info;
+
+ const details = {
+ contract: contractName,
+ contractAddress,
contractType,
+ contractSubtype,
+ status,
+ expiration: expiryTime,
+ payeeAddress,
+ userChoices,
+ token: tokenIdentification,
+ acceptingToken,
+ sellingToken,
+ participationFees: contractAmount,
+ minAmount: minimumsubscriptionamount,
+ maxAmount: maximumsubscriptionamount,
numberOfDeposits,
numberOfParticipants,
priceType,
oracle_address,
- contractSubtype,
- status,
- expiryTime,
- payeeAddress,
- userChoices,
- tokenIdentification,
- acceptingToken,
- sellingToken,
- contractAmount,
- minimumsubscriptionamount,
- maximumsubscriptionamount,
totalHonorAmount,
totalParticipationAmount,
price,
currentDepositBalance
- }, contractAddress,
- contractName
- } = info
- const details = {
- contract: contractName,
- contractAddress,
- contractType,
- contractSubtype,
- status,
- expiration: expiryTime,
- payeeAddress,
- userChoices,
- token: tokenIdentification,
- acceptingToken,
- sellingToken,
- participationFees: contractAmount,
- minAmount: minimumsubscriptionamount,
- maxAmount: maximumsubscriptionamount,
- numberOfDeposits,
- numberOfParticipants,
- priceType,
- oracle_address,
- totalHonorAmount,
- totalParticipationAmount,
- price,
- currentDepositBalance
+ };
+
+ const key = `${contractName}_${contractAddress}`;
+ console.log(`๐ง Caching contract info under key: ${key}`);
+ floGlobals.smartContractList[key] = {
+ ...details,
+ ...floGlobals.smartContractList[key]
+ };
+
+ return details;
+ } catch (error) {
+ console.error("โ Error fetching smart contract info:", error);
+ return null;
}
- floGlobals.smartContractList[`${contractName}-${contractAddress}`] = {
- ...details,
- ...floGlobals.smartContractList[`${contractName}-${contractAddress}`]
- }
- return details
}
+
async function getContractParticipants(contract) {
- const participants = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/smartContractParticipants?contractName=${contract.name}&contractAddress=${contract.address}`)
- return participants.participantInfo
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/smartContractParticipants?contractName=${contract.name}&contractAddress=${contract.address}`;
+ console.log(`๐ก Fetching contract participants from: ${url}`);
+ const participants = await fetchJson(url);
+ console.log("๐ฆ Participants response:", participants);
+ return participants.participantInfo || [];
+ } catch (error) {
+ console.error("โ Error fetching contract participants:", error);
+ return [];
+ }
}
async function getContractTransactions(contract) {
- const transactions = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/smartContractTransactions?contractName=${contract.name}&contractAddress=${contract.address}`)
- return transactions.contractTransactions
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/smartContractTransactions?contractName=${contract.name}&contractAddress=${contract.address}`;
+ console.log(`๐ก Fetching contract transactions from: ${url}`);
+ const transactions = await fetchJson(url);
+ console.log("๐ฆ Transactions response:", transactions);
+ return transactions.contractTransactions || [];
+ } catch (error) {
+ console.error("โ Error fetching contract transactions:", error);
+ return [];
+ }
}
async function getContractDeposits(contract) {
- const deposits = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/smartContractDeposits?contractName=${contract.name}&contractAddress=${contract.address}`)
- return deposits.depositInfo
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/smartContractDeposits?contractName=${contract.name}&contractAddress=${contract.address}`;
+ console.log(`๐ก Fetching contract deposits from: ${url}`);
+ const deposits = await fetchJson(url);
+ console.log("๐ฆ Deposits response:", deposits);
+ return deposits.depositInfo || [];
+ } catch (error) {
+ console.error("โ Error fetching contract deposits:", error);
+ return [];
+ }
}
+
function getReceiver(vin, vout) {
return vout.find(output => output.scriptPubKey.addresses.find(address => address !== vin[0].addr))?.scriptPubKey.addresses[0] || vin[0].addr
}
- function parseTransactions(txList) {
- if (!Array.isArray(txList))
- txList = [txList]
- let latestTxArray = [];
- txList.forEach(tx => {
- const {
- txid, blockHeight, vin, vout, time, receiverAddress, senderAddress, contractAddress, contractType,
- contractConditions: {
- expiryTime, accepting_token, selling_token, subtype, price,
- participationAmount, minimumsubscriptionamount, maximumsubscriptionamount
- } = {},
- contractAmount, type, tokenAmount, transferType, triggerCondition, userChoice, nftHash, depositAmount,
- contractName, tokenIdentification, transactionTrigger, onChain
- } = tx;
- let obj = {
- time
- };
- if (txid)
- obj["hash"] = txid;
- if (blockHeight)
- obj["blockHeight"] = blockHeight;
- if (onChain) {
- if (type === "smartContractPays") {
- // transaction is a FLO Smart Contract Committee trigger
- obj = Object.assign({}, obj, {
- hash: txid,
- blockHeight,
- contractName,
- contractAddress: receiverAddress,
- winningChoice: triggerCondition,
- committeeAddress: senderAddress,
- type: 'contracttrigger'
- });
- latestTxArray.push(obj);
- }
- else if (type === 'transfer') {
- if (transferType == "token" || transferType == 'nft') {
- obj = Object.assign({}, obj, {
- sender: senderAddress,
- receiver: receiverAddress,
- amount: tokenAmount,
- type: transferType == "token" ? "tokentransfer" : "nfttransfer",
- token: tokenIdentification,
- });
- latestTxArray.push(obj);
- } else if (transferType == 'smartContract') {
- // smart contract transfer
- obj = Object.assign({}, obj, {
- sender: senderAddress,
- receiver: receiverAddress,
- amount: tokenAmount,
- contractName,
- userChoice,
- type: "contracttransfer",
- token: tokenIdentification,
- });
- latestTxArray.push(obj);
- }
- } else if (type === 'tokenIncorporation') {
- // token incorporation
- // smart contract incorporation
- obj = Object.assign({}, obj, {
- incAddress: senderAddress,
- supply: tokenAmount,
- type: "tokenincorp",
- token: tokenIdentification,
- });
- latestTxArray.push(obj);
- } else if (type === 'smartContractIncorporation') {
- // smart contract incorporation
- // todo : add checks to determine obj for different types of smart contract incorporation
- if (subtype == 'tokenswap') {
- obj = Object.assign({}, obj, {
- contractName,
- incAddress: contractAddress,
- contractType,
- type: "contractincorp",
- sellingToken: selling_token,
- acceptingToken: accepting_token,
- price,
- });
- } else {
- obj = Object.assign({}, obj, {
- contractName,
- incAddress: contractAddress,
- contractType,
- expiration: expiryTime,
- participationFees: contractAmount,
- availableChoices: "",
- type: "contractincorp",
- minAmount: minimumsubscriptionamount,
- maxAmount: maximumsubscriptionamount,
- token: tokenIdentification,
- });
- }
- latestTxArray.push(obj);
- } else if (type === 'nftIncorporation') {
- // nft incorporation
- obj = Object.assign({}, obj, {
- incAddress: senderAddress,
- supply: tokenAmount,
- type: "nftincorp",
- nftHash,
- token: tokenIdentification,
- });
- latestTxArray.push(obj);
- } else if (type === 'smartContractDeposit') {
- // smart contract deposit
- obj = Object.assign({}, obj, {
- contractName,
- contractAddress,
- contractType,
- amount: depositAmount,
- type: "contractdeposit",
- sender: senderAddress,
- receiver: receiverAddress,
- token: tokenIdentification,
- });
- latestTxArray.push(obj);
- }
- } else {
- obj = Object.assign({}, obj, {
- transactionTrigger,
- contractName,
- contractAddress,
- onChain: false,
- type: 'offChainTransfer',
- senderAddress,
- receiverAddress,
- tokenAmount,
- tokenIdentification,
- time
- });
- latestTxArray.push(obj);
- }
- })
+const internalTransactionTypes = [
+ "tokenswapDepositSettlement",
+ "tokenswapParticipationSettlement",
+ "smartContractDepositReturn",
+ "tokenswapRefund",
+ "smartContractPays"
+];
- return latestTxArray;
+function parseTransactions(txList) {
+ if (!Array.isArray(txList)) txList = [txList];
+
+ console.log("๐ก Raw input to parseTransactions:", txList);
+ let latestTxArray = [];
+
+ txList.forEach((tx, index) => {
+ console.log(`๐ Processing TX #${index}:`, tx);
+
+ const {
+ txid, blockHeight, vin, vout, time,
+ receiverAddress, senderAddress,
+ contractAddress: rawContractAddress, contractType: rawContractType,
+ contractConditions: {
+ expiryTime, accepting_token, selling_token, subtype, price,
+ participationAmount, minimumsubscriptionamount, maximumsubscriptionamount
+ } = {},
+ contractAmount, type, tokenAmount, transferType,
+ triggerCondition, userChoice, nftHash, depositAmount,
+ contractName, tokenIdentification, transactionTrigger,
+ onChain
+ } = tx;
+
+ const contractAddress = rawContractAddress || receiverAddress || tx.contractAddress;
+ const contractType = rawContractType || tx.contractType;
+
+ let obj = { time };
+ if (txid) obj["hash"] = txid;
+ if (blockHeight) obj["blockHeight"] = blockHeight;
+
+ console.log(`๐งช TX Type: ${type}, OnChain: ${onChain}, TXID: ${txid}`);
+
+ if (type === "tokenswapParticipation") {
+ console.log("๐ Handling tokenswapParticipation");
+ obj = {
+ hash: txid,
+ sender: senderAddress,
+ receiver: receiverAddress,
+ amount: tokenAmount,
+ contractName,
+ userChoice,
+ type: "contracttransfer",
+ token: tokenIdentification,
+ transactionTrigger,
+ onChain: true,
+ time
+ };
+ console.log("โ
Parsed tokenswapParticipation:", obj);
+ latestTxArray.push(obj);
+ return;
}
- async function getAllBlocks(number) {
- const allBlocks = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/latestBlockDetails${number ? `?limit=${number}` : ''}`)
- return Object.values(allBlocks.latestBlocks).sort((a, b) => b.height - a.height)
- }
+ if (!onChain) {
+ console.log("๐ Off-chain transaction");
+ if (internalTransactionTypes.includes(type) || type === 'trigger') {
+ console.log("๐ฉ Recognized internal or trigger off-chain TX");
- async function getAllTxs() {
- const allTxs = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/latestTransactionDetails?limit=200`)
- return allTxs.latestTransactions
- }
-
- async function getAddressInfo(floAddress) {
- const addressInfo = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/floAddressInfo/${floAddress}`)
- return addressInfo.floAddressBalances
- }
-
- async function getAddressTxs(floAddress) {
- const transactions = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/floAddressTransactions/${floAddress}`)
- return transactions.transactions
- }
-
- async function getTxInfo(thisTx) {
- try {
- const transaction = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/transactionDetails/${thisTx}`)
- console.log(transaction)
- if (transaction.result === 'error')
- return [false, transaction.description]
- let {
- floData,
- tokenAmount,
- tokenIdentification,
- type,
- nftHash,
- blockheight,
- vin,
- vout,
- confirmations,
- transferType,
+ obj = {
+ hash: txid,
+ transactionTrigger: transactionTrigger || txid,
+ contractName,
+ contractAddress,
+ onChain: false,
+ type: 'offChainTransfer',
senderAddress,
receiverAddress,
- txid,
- onChain,
- subTransactions,
+ tokenAmount,
+ tokenIdentification: tokenIdentification || accepting_token || 'unknown',
time
- } = transaction;
- if (type == 'smartContractPays') {
- tokenAmount = '-'
- }
- return [
- true, {
- type: `${transferType || ''} ${type}`,
- name: tokenIdentification,
- blockHeight: blockheight,
- amount: tokenAmount,
- sender: senderAddress,
- receiver: receiverAddress,
- floData,
- hash: txid,
- confirmations,
- nftHash,
- onChain,
- subTransactions,
- time
- }
- ]
+ };
+ console.log("โ๏ธ Parsed offChainTransfer:", obj);
+ latestTxArray.push(obj);
+ } else {
+ console.warn("โ Ignored off-chain TX:", tx);
}
- catch (err) {
- throw err
+ return;
+ }
+
+ if (type === "trigger") {
+ console.log("โก Handling on-chain trigger");
+ obj = {
+ hash: transactionTrigger || txid,
+ contractName,
+ contractAddress,
+ sender: senderAddress || contractAddress,
+ receiver: receiverAddress || contractAddress,
+ amount: tokenAmount,
+ type: "trigger",
+ token: tokenIdentification || accepting_token || 'unknown',
+ triggerCondition,
+ onChain: true,
+ time
+ };
+ console.log("โก Parsed on-chain trigger:", obj);
+ latestTxArray.push(obj);
+ return;
+ }
+
+ if ((type === 'transfer' || type === 'participation') && transferType === 'smartContract') {
+ console.log("๐ Handling smartContract transfer/participation");
+ obj = {
+ hash: txid,
+ sender: senderAddress,
+ receiver: receiverAddress,
+ amount: tokenAmount,
+ contractName,
+ userChoice,
+ type: "contracttransfer",
+ token: tokenIdentification,
+ transactionTrigger,
+ time
+ };
+ console.log("๐ Parsed contracttransfer:", obj);
+ latestTxArray.push(obj);
+ return;
+ }
+
+ if (type === 'transfer') {
+ console.log("๐ธ Handling regular token/nft transfer");
+ if (transferType === "token" || transferType === 'nft') {
+ obj = {
+ hash: txid,
+ sender: senderAddress,
+ receiver: receiverAddress,
+ amount: tokenAmount,
+ type: transferType === "token" ? "tokentransfer" : "nfttransfer",
+ token: tokenIdentification,
+ time
+ };
+ console.log("๐ธ Parsed token/nft transfer:", obj);
+ latestTxArray.push(obj);
+ return;
+ } else {
+ console.warn("๐ซ Unrecognized transferType:", transferType, "in TX:", tx);
}
}
+ if (type === 'tokenIncorporation') {
+ console.log("๐ฆ Handling token incorporation");
+ obj = {
+ hash: txid,
+ incAddress: senderAddress,
+ supply: tokenAmount,
+ type: "tokenincorp",
+ token: tokenIdentification,
+ time
+ };
+ console.log("๐ฆ Parsed tokenIncorporation:", obj);
+ latestTxArray.push(obj);
+ return;
+ }
+
+ if (type === 'smartContractIncorporation' || type === 'incorporation') {
+ console.log("๐๏ธ Handling smart contract incorporation");
+ if (subtype === 'tokenswap') {
+ obj = {
+ hash: txid,
+ contractName,
+ incAddress: contractAddress,
+ contractType,
+ type: "contractincorp",
+ sellingToken: selling_token,
+ acceptingToken: accepting_token,
+ price,
+ time
+ };
+ } else {
+ obj = {
+ hash: txid,
+ contractName,
+ incAddress: contractAddress,
+ contractType,
+ expiration: expiryTime,
+ participationFees: contractAmount,
+ availableChoices: "",
+ type: "contractincorp",
+ minAmount: minimumsubscriptionamount,
+ maxAmount: maximumsubscriptionamount,
+ token: tokenIdentification,
+ time
+ };
+ }
+ console.log("โ
Parsed smartContractIncorporation:", obj);
+ latestTxArray.push(obj);
+ return;
+ }
+
+ if (type === 'nftIncorporation') {
+ console.log("๐ผ๏ธ Handling NFT incorporation");
+ obj = {
+ hash: txid,
+ incAddress: senderAddress,
+ supply: tokenAmount,
+ type: "nftincorp",
+ nftHash,
+ token: tokenIdentification,
+ time
+ };
+ console.log("๐ผ๏ธ Parsed nftIncorporation:", obj);
+ latestTxArray.push(obj);
+ return;
+ }
+
+ if (type === 'smartContractDeposit') {
+ console.log("๐ช Handling smart contract deposit");
+ obj = {
+ hash: txid,
+ contractName,
+ contractAddress,
+ contractType,
+ amount: typeof depositAmount !== "undefined" ? depositAmount : tokenAmount,
+ type: "contractdeposit",
+ sender: senderAddress,
+ receiver: receiverAddress,
+ token: tokenIdentification,
+ time
+ };
+ console.log("๐ช Parsed smartContractDeposit:", obj);
+ latestTxArray.push(obj);
+ return;
+ }
+
+ console.warn("โ Unhandled TX type:", type, tx);
+ });
+
+ console.log("๐งฉ Final Parsed Transactions (types):", latestTxArray.map(t => t.type));
+ console.log("๐งฉ Final Parsed Transactions (full):", latestTxArray);
+ return latestTxArray;
+}
+
+
+ async function getAllBlocks(number) {
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/latestBlockDetails${number ? `?limit=${number}` : ''}`;
+ console.log(`๐ก Fetching latest blocks from: ${url}`);
+ const allBlocks = await fetchJson(url);
+ console.log("๐ฆ Fetched blocks:", allBlocks);
+ const blocks = Object.values(allBlocks.latestBlocks || {}).sort((a, b) => b.height - a.height);
+ return blocks;
+ } catch (error) {
+ console.error("โ Error fetching latest blocks:", error);
+ return [];
+ }
+ }
+
+
+ async function getAllTxs() {
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/latestTransactionDetails?limit=200`;
+ console.log(`๐ก Fetching latest transactions from: ${url}`);
+ const allTxs = await fetchJson(url);
+ console.log("๐ฆ Fetched transactions:", allTxs);
+ return allTxs.latestTransactions || [];
+ } catch (error) {
+ console.error("โ Error fetching latest transactions:", error);
+ return [];
+ }
+ }
+
+
+ async function getAddressInfo(floAddress) {
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/floAddressInfo/${floAddress}`;
+ console.log(`๐ก Fetching address info from: ${url}`);
+ const addressInfo = await fetchJson(url);
+ console.log("๐ฆ Address info response:", addressInfo);
+ return addressInfo;
+ } catch (error) {
+ console.error("โ Error fetching address info:", error);
+ return null;
+ }
+ }
+
+
+
+ async function getAddressBalance(floAddress) {
+ try {
+ const url = `${floGlobals.floApiUrl}/api/v2/address/${floAddress}?details=basic`;
+ console.log(`๐ก Fetching address balance from: ${url}`);
+ const balance = await fetchJson(url);
+ console.log("๐ฆ Balance info:", balance);
+ return balance;
+ } catch (error) {
+ console.error("โ Error fetching address balance:", error);
+ return null;
+ }
+ }
+
+
+
+ async function getAddressTxs(floAddress) {
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/floAddressTransactions/${floAddress}`;
+ console.log(`๐ก Fetching address transactions from: ${url}`);
+ const transactions = await fetchJson(url);
+ console.log("๐ฆ Address transactions:", transactions);
+ return transactions.transactions || [];
+ } catch (error) {
+ console.error("โ Error fetching address transactions:", error);
+ return [];
+ }
+ }
+
+
+
+async function getTxInfo(thisTx) {
+ try {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/transactionDetails/${thisTx}`;
+ console.log(`๐ก Fetching transaction info from: ${url}`);
+
+ const transaction = await fetchJson(url);
+ console.log("๐ฆ Transaction response:", transaction);
+
+ if (transaction.result === 'error')
+ return [false, transaction.description];
+
+ // Extract all known fields safely
+ let {
+ floData,
+ tokenAmount,
+ tokenIdentification,
+ type,
+ nftHash,
+ blockheight,
+ vin,
+ vout,
+ confirmations,
+ transferType,
+ senderAddress,
+ receiverAddress,
+ txid,
+ onChain,
+ subTransactions,
+ time,
+ contractAddress,
+ contractName,
+ operation,
+ operationDetails
+ } = transaction;
+
+ // Normalize amount for smart contract pays
+ if (type === 'smartContractPays') {
+ tokenAmount = '-';
+ }
+
+ return [
+ true,
+ {
+ // Original and normalized values
+ txid,
+ hash: txid, // fallback for compatibility
+ type: transferType ? `${transferType.trim()} ${type.trim()}` : type.trim(),
+ name: tokenIdentification,
+ tokenAmount,
+ amount: tokenAmount,
+ blockheight,
+ blockHeight: blockheight,
+ confirmations,
+ sender: senderAddress,
+ senderAddress,
+ receiver: receiverAddress,
+ receiverAddress,
+ floData,
+ nftHash,
+ onChain,
+ subTransactions,
+ time,
+
+ // โ
Additional preserved fields
+ contractAddress,
+ contractName,
+ operation,
+ operationDetails
+ }
+ ];
+ } catch (err) {
+ console.error("โ Error fetching transaction info:", err);
+ return [false, err.message || "Unknown error"];
+ }
+}
+
+
+
function returnHexNumber(s) {
var regExp = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/;
@@ -2482,89 +3091,134 @@
}
function splitContractNameAddress(text) {
- const index = text.lastIndexOf('-');
+ const index = text.lastIndexOf('_');
return {
name: text.substring(0, index),
address: text.substring(index + 1)
};
}
- function categorizeText(text) {
- return new Promise((resolve, reject) => {
- // check if text have only numbers
- if (/^\d+$/.test(text)) {
- //console.log('this is a block number');
- location.href = `#/block/${text}`
- resolve('block')
- } else if (text.length == 34 && floCrypto.validateFloID(text)) {
- //console.log('data entered is a FLO address');
- location.href = `#/address/${text}`
- resolve('address')
- } else if (floGlobals.tokenList.includes(text.toLowerCase())) {
- //console.log('data entered is a token name');
- location.href = `#/token/${text}`
- resolve('token')
- } else if (floGlobals.smartContractList[text]) {
- location.href = `#/contract/${text}`
- resolve('contract')
- } else if (text.length == 64 && returnHexNumber(text)) {
- fetchJson(`${floGlobals.tokenApiUrl}/api/v2/categoriseString/` + text)
- .then(function (myJson) {
- console.log(`${floGlobals.tokenApiUrl}/api/v2/categoriseString/` + text)
- if (myJson['type'] == 'transaction') {
- //console.log('data entered is a transaction hash');
- location.href = `#/transaction/${text}`
- } else if (myJson['type'] == 'block') {
- // console.log('data entered is a block hash');
- location.href = `#/block/${text}`
- } else {
- renderElem(getRef("page_container"), html`${render.errorPage('The entered text is not a part of the token system')}`);
- }
- resolve()
- }).catch(err => {
- console.error(err)
- resolve()
- })
+function categorizeText(text) {
+ console.log("๐ Categorizing input:", text);
+ return new Promise((resolve, reject) => {
+ // check if text has only numbers
+ if (/^\d+$/.test(text)) {
+ console.log("๐ Input detected as block number");
+ location.href = `#/block/${text}`;
+ resolve('block');
+ } else if (text.length === 34 && floCrypto.validateFloID(text)) {
+ console.log("๐ Input detected as valid FLO address");
+ location.href = `#/address/${text}`;
+ resolve('address');
+ } else if (floGlobals.tokenList.includes(text.toLowerCase())) {
+ console.log("๐ Input detected as token name");
+ location.href = `#/token/${text}`;
+ resolve('token');
+ } else if (floGlobals.smartContractList[text]) {
+ console.log("๐ Input detected as smart contract key");
+ location.href = `#/contract/${text}`;
+ resolve('contract');
+ } else if (text.length === 64 && returnHexNumber(text)) {
+ const url = `${floGlobals.tokenApiUrl}/api/v2/categoriseString/${text}`;
+ console.log("๐ก Fetching categorization from:", url);
- } else {
- renderElem(getRef("page_container"), html`${render.errorPage('Invalid search query')}`);
- }
- })
+ fetchJson(url)
+ .then(function (myJson) {
+ console.log("โ
Categorization result:", myJson);
+ const type = myJson['type'];
+ if (type === 'transaction') {
+ location.href = `#/transaction/${text}`;
+ } else if (type === 'block') {
+ location.href = `#/block/${text}`;
+} else if (type === 'noise') {
+ console.warn("โ ๏ธ Categorized as noise โ no match found.");
+ renderElem(getRef("page_container"), html`
+
+
+
+
+
+
+ Nothing found
+
+ No transaction, block, token, or contract matched your input:
+ ${text}
+
+ router.routeTo('#/home')}>Go back home
+
+ `);
+}
+
+
+ else {
+ console.warn("โ ๏ธ Unknown categorization type:", type);
+ }
+ resolve(); // โ
Ensure spinner clears
+ })
+ .catch(err => {
+ console.error("โ Categorization fetch failed:", err);
+ resolve(); // โ
Still resolve to clear spinner
+ });
+ } else {
+ console.warn("โ ๏ธ Invalid search query format");
+ renderElem(getRef("page_container"), html`${render.errorPage('Invalid search query')}`);
+ resolve(); // โ
Added this resolve too
}
+ });
+}
+
+
+
+
+async function processNavbarSearch() {
+ const query = getRef('main_search_field').value.trim();
+ try {
+ if (query === '') return;
+ loading(); // Start spinner
+ await getAllSuggestions();
+ await categorizeText(query);
+ getRef('main_search_field').value = '';
+ renderElem(getRef('suggestions'), html``);
+ } catch (err) {
+ console.error(err);
+ } finally {
+ loading(false); // โ
Always stop spinner, even if error or "noise"
+ }
+}
+
+
+ async function getAllSuggestions() {
+ console.log(`๐ก Fetching token and smart contract list from ${floGlobals.tokenApiUrl}/api/v2/tokenSmartContractList`);
+ window.allSuggestions = [];
- async function processNavbarSearch() {
- const query = getRef('main_search_field').value.trim();
try {
- if (query === '') return
- loading()
- await getAllSuggestions();
- await categorizeText(query)
- getRef('main_search_field').value = ''
- renderElem(getRef('suggestions'), html``)
+ let { tokens, smartContracts } = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/tokenSmartContractList`);
+ console.log("โ
Token & Smart Contract list received:", { tokensCount: tokens.length, smartContractsCount: smartContracts.length });
+
+ floGlobals.tokenList = tokens;
+ floGlobals.smartContractList = {};
+ smartContracts.forEach(contract => {
+ floGlobals.smartContractList[`${contract.contractName}_${contract.contractAddress}`] = contract;
+ allSuggestions.push(`${contract.contractName}_${contract.contractAddress}`);
+ });
+ allSuggestions = allSuggestions.concat(tokens);
+
+ window.flexSearchIndex = new FlexSearch.Index({
+ tokenize: "reverse",
+ suggest: true
+ });
+
+ allSuggestions.forEach((suggestion, index) => {
+ flexSearchIndex.add(index, suggestion);
+ });
+
+ console.log("๐ Search suggestions initialized with", allSuggestions.length, "items");
} catch (err) {
- console.error(err)
+ console.error("โ Failed to fetch suggestions:", err);
+ throw err;
}
}
- async function getAllSuggestions() {
- window.allSuggestions = [];
- let { tokens, smartContracts } = await fetchJson(`${floGlobals.tokenApiUrl}/api/v2/tokenSmartContractList`);
- floGlobals.tokenList = tokens;
- floGlobals.smartContractList = {}
- smartContracts.forEach(contract => {
- floGlobals.smartContractList[`${contract.contractName}-${contract.contractAddress}`] = contract;
- allSuggestions.push(`${contract.contractName}-${contract.contractAddress}`);
- })
- allSuggestions = allSuggestions.concat(tokens);
- window.flexSearchIndex = new FlexSearch.Index({
- tokenize: "reverse",
- suggest: true
- });
-
- allSuggestions.forEach((suggestion, index) => {
- flexSearchIndex.add(index, suggestion);
- })
- }
function initSmartContractCreation() {
const [selectedSCTemplate, setSelectedSCTemplate] = $signal(null);
@@ -2961,7 +3615,7 @@
description = html`
`
break
@@ -2973,7 +3627,7 @@
description = html`
`
break
@@ -2988,7 +3642,7 @@
description = html`
`
break;
@@ -3094,7 +3748,7 @@
return notify(`You already have a smart contract with this address. Only one smart contract is allowed per address.`, 'error')
let floData
let confirmationMessage = ''
- if (floGlobals.smartContractList.hasOwnProperty(`${contractName}-${creatorAddress}`))
+ if (floGlobals.smartContractList.hasOwnProperty(`${contractName}_${creatorAddress}`))
return notify(`Contract with name: ${contractName} and address: ${creatorAddress} already exists`, 'error')
switch (type) {
case 'one-time-event':
@@ -3297,4 +3951,4 @@
-
\ No newline at end of file
+