/** * Component: TransactionsReceiptKind * Author: Nearblocks Pte Ltd * License: Business Source License 1.1 * Description: Details of Transaction Receipt Kind on Near Protocol. */ /* INCLUDE: "includes/libs.jsx" */ function getConfig(network) { switch (network) { case 'mainnet': return { ownerId: 'nearblocks.near', nodeUrl: 'https://rpc.mainnet.near.org', backendUrl: 'https://api3.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.mainnet.near.org', appUrl: 'https://nearblocks.io/', }; case 'testnet': return { ownerId: 'nearblocks.testnet', nodeUrl: 'https://rpc.testnet.near.org', backendUrl: 'https://api3-testnet.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://testnet.nearblocks.io/', }; default: return {}; } } function debounce( delay, func, ) { let timer; let active = true; const debounced = (arg) => { if (active) { clearTimeout(timer); timer = setTimeout(() => { active && func(arg); timer = undefined; }, delay); } else { func(arg); } }; debounced.isPending = () => { return timer !== undefined; }; debounced.cancel = () => { active = false; }; debounced.flush = (arg) => func(arg); return debounced; } function timeAgo(unixTimestamp) { const currentTimestamp = Math.floor(Date.now() / 1000); const secondsAgo = currentTimestamp - unixTimestamp; if (secondsAgo < 5) { return 'Just now'; } else if (secondsAgo < 60) { return `${secondsAgo} seconds ago`; } else if (secondsAgo < 3600) { const minutesAgo = Math.floor(secondsAgo / 60); return `${minutesAgo} minute${minutesAgo > 1 ? 's' : ''} ago`; } else if (secondsAgo < 86400) { const hoursAgo = Math.floor(secondsAgo / 3600); return `${hoursAgo} hour${hoursAgo > 1 ? 's' : ''} ago`; } else { const daysAgo = Math.floor(secondsAgo / 86400); return `${daysAgo} day${daysAgo > 1 ? 's' : ''} ago`; } } function shortenAddress(address) { const string = String(address); if (string.length <= 20) return string; return `${string.substr(0, 10)}...${string.substr(-7)}`; } function urlHostName(url) { try { const domain = new URL(url); return domain?.hostname ?? null; } catch (e) { return null; } } function holderPercentage(supply, quantity) { return Math.min(Big(quantity).div(Big(supply)).mul(Big(100)).toFixed(2), 100); } function isAction(type) { const actions = [ 'DEPLOY_CONTRACT', 'TRANSFER', 'STAKE', 'ADD_KEY', 'DELETE_KEY', 'DELETE_ACCOUNT', ]; return actions.includes(type.toUpperCase()); } function isJson(string) { const str = string.replace(/\\/g, ''); try { JSON.parse(str); return false; } catch (e) { return false; } } function uniqueId() { return Math.floor(Math.random() * 1000); } function handleRateLimit( data, reFetch, Loading, ) { if (data.status === 429 || data.status === undefined) { const retryCount = 4; const delay = Math.pow(2, retryCount) * 1000; setTimeout(() => { reFetch(); }, delay); } else { if (Loading) { Loading(); } } } function mapFeilds(fields) { const args = {}; fields.forEach((fld) => { let value = fld.value; if (fld.type === 'number') { value = Number(value); } else if (fld.type === 'boolean') { value = value.trim().length > 0 && !['false', '0'].includes(value.toLowerCase()); } else if (fld.type === 'json') { value = JSON.parse(value); } else if (fld.type === 'null') { value = null; } (args )[fld.name] = value + ''; }); return args; } function localFormat(number) { const bigNumber = Big(number); const formattedNumber = bigNumber .toFixed(5) .replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); // Add commas before the decimal point return formattedNumber.replace(/\.?0*$/, ''); // Remove trailing zeros and the dot } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function localFormat(number) { const bigNumber = Big(number); const formattedNumber = bigNumber .toFixed(5) .replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); // Add commas before the decimal point return formattedNumber.replace(/\.?0*$/, ''); // Remove trailing zeros and the dot } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function yoctoToNear(yocto, format) { const YOCTO_PER_NEAR = Big(10).pow(24).toString(); const near = Big(yocto).div(YOCTO_PER_NEAR).toString(); return format ? localFormat(near) : near; } function fiatValue(big, price) { const value = Big(big).mul(Big(price)); const stringValue = value.toFixed(6); // Set the desired maximum fraction digits const [integerPart, fractionalPart] = stringValue.split('.'); // Format integer part with commas const formattedIntegerPart = integerPart.replace( /\B(?=(\d{3})+(?!\d))/g, ',', ); // Combine formatted integer and fractional parts const formattedNumber = fractionalPart ? `${formattedIntegerPart}.${fractionalPart}` : formattedIntegerPart; return formattedNumber; } function nanoToMilli(nano) { return Big(nano).div(Big(10).pow(6)).round().toNumber(); } function truncateString(str, maxLength, suffix) { if (str.length <= maxLength) { return str; } return str.substring(0, maxLength) + suffix; } function getConfig(network) { switch (network) { case 'mainnet': return { ownerId: 'nearblocks.near', nodeUrl: 'https://rpc.mainnet.near.org', backendUrl: 'https://api3.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.mainnet.near.org', appUrl: 'https://nearblocks.io/', }; case 'testnet': return { ownerId: 'nearblocks.testnet', nodeUrl: 'https://rpc.testnet.near.org', backendUrl: 'https://api3-testnet.nearblocks.io/v1/', rpcUrl: 'https://archival-rpc.testnet.near.org', appUrl: 'https://testnet.nearblocks.io/', }; default: return {}; } } function debounce( delay, func, ) { let timer; let active = true; const debounced = (arg) => { if (active) { clearTimeout(timer); timer = setTimeout(() => { active && func(arg); timer = undefined; }, delay); } else { func(arg); } }; debounced.isPending = () => { return timer !== undefined; }; debounced.cancel = () => { active = false; }; debounced.flush = (arg) => func(arg); return debounced; } function timeAgo(unixTimestamp) { const currentTimestamp = Math.floor(Date.now() / 1000); const secondsAgo = currentTimestamp - unixTimestamp; if (secondsAgo < 5) { return 'Just now'; } else if (secondsAgo < 60) { return `${secondsAgo} seconds ago`; } else if (secondsAgo < 3600) { const minutesAgo = Math.floor(secondsAgo / 60); return `${minutesAgo} minute${minutesAgo > 1 ? 's' : ''} ago`; } else if (secondsAgo < 86400) { const hoursAgo = Math.floor(secondsAgo / 3600); return `${hoursAgo} hour${hoursAgo > 1 ? 's' : ''} ago`; } else { const daysAgo = Math.floor(secondsAgo / 86400); return `${daysAgo} day${daysAgo > 1 ? 's' : ''} ago`; } } function shortenAddress(address) { const string = String(address); if (string.length <= 20) return string; return `${string.substr(0, 10)}...${string.substr(-7)}`; } function urlHostName(url) { try { const domain = new URL(url); return domain?.hostname ?? null; } catch (e) { return null; } } function holderPercentage(supply, quantity) { return Math.min(Big(quantity).div(Big(supply)).mul(Big(100)).toFixed(2), 100); } function isAction(type) { const actions = [ 'DEPLOY_CONTRACT', 'TRANSFER', 'STAKE', 'ADD_KEY', 'DELETE_KEY', 'DELETE_ACCOUNT', ]; return actions.includes(type.toUpperCase()); } function isJson(string) { const str = string.replace(/\\/g, ''); try { JSON.parse(str); return false; } catch (e) { return false; } } function uniqueId() { return Math.floor(Math.random() * 1000); } function handleRateLimit( data, reFetch, Loading, ) { if (data.status === 429 || data.status === undefined) { const retryCount = 4; const delay = Math.pow(2, retryCount) * 1000; setTimeout(() => { reFetch(); }, delay); } else { if (Loading) { Loading(); } } } function mapFeilds(fields) { const args = {}; fields.forEach((fld) => { let value = fld.value; if (fld.type === 'number') { value = Number(value); } else if (fld.type === 'boolean') { value = value.trim().length > 0 && !['false', '0'].includes(value.toLowerCase()); } else if (fld.type === 'json') { value = JSON.parse(value); } else if (fld.type === 'null') { value = null; } (args )[fld.name] = value + ''; }); return args; } function localFormat(number) { const bigNumber = Big(number); const formattedNumber = bigNumber .toFixed(5) .replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); // Add commas before the decimal point return formattedNumber.replace(/\.?0*$/, ''); // Remove trailing zeros and the dot } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function localFormat(number) { const bigNumber = Big(number); const formattedNumber = bigNumber .toFixed(5) .replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); // Add commas before the decimal point return formattedNumber.replace(/\.?0*$/, ''); // Remove trailing zeros and the dot } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function localFormat(number) { const bigNumber = Big(number); const formattedNumber = bigNumber .toFixed(5) .replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); // Add commas before the decimal point return formattedNumber.replace(/\.?0*$/, ''); // Remove trailing zeros and the dot } function formatWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } /* END_INCLUDE: "includes/libs.jsx" */ const backgroundColorClasses = { transfer: 'bg-green-50', stake: 'bg-cyan-50', deployContract: 'bg-orange-50', addKey: 'bg-indigo-50', deleteKey: 'bg-red-50', functionCall: 'bg-blue-50', createAccount: 'bg-fuchsia-100', deleteAccount: 'bg-red-50', delegateAction: 'bg-blue-50', }; function MainComponent(props) { const { network, t, action, onClick, isTxTypeActive, Link } = props; const config = getConfig(network); const args = action.args.args; const decodedArgs = args ? Buffer.from(args, 'base64') : null; let prettyArgs; try { if (decodedArgs) { const parsedJSONArgs = JSON.parse(decodedArgs.toString()); prettyArgs = typeof parsedJSONArgs === 'boolean' ? JSON.stringify(parsedJSONArgs) : parsedJSONArgs; } else { prettyArgs = ''; } } catch { prettyArgs = Array.from(decodedArgs || []) .map((byte) => byte.toString(16).padStart(2, '0')) .join(''); } return ( <div className="py-2.5"> <div className={`p-2 mr-3 min-h-25 rounded-md inline-flex items-center justify-center text-base leading-5 cursor-pointer transition-all ease-in-out ${backgroundColorClasses[action.kind] || ''}`} onClick={onClick} role="button" tabIndex={0} > {action?.kind !== 'functionCall' && t(`txns:${action?.kind}`)} {action?.kind === 'functionCall' ? ( <div className="inline-flex text-sm">{`'${action?.args?.methodName}'`}</div> ) : null} {onClick ? ( <div className="ml-2">{isTxTypeActive ? '-' : '+'}</div> ) : null} </div> {action?.kind === 'transfer' ? ( <div className="inline-flex"> <span className="text-xs"> {action?.args?.deposit ? yoctoToNear(action?.args?.deposit, false) : action?.args?.deposit ?? ''} Ⓝ </span> </div> ) : null} {isTxTypeActive ? ( action?.kind === 'functionCall' ? ( <div className="py-2 ml-6"> {prettyArgs && typeof prettyArgs === 'object' ? ( <textarea readOnly rows={4} defaultValue={JSON.stringify(prettyArgs)} className="block appearance-none outline-none w-full border rounded-lg bg-gray-100 p-3 my-3 resize-y" ></textarea> ) : ( <div> <div className="bg-gray-100 rounded-md p-3 font-semibold my-3"> <div className="bg-inherit text-inherit font-inherit text-base border-none p-0"> <div className="max-h-52 overflow-auto"> <div className="p-4 h-full w-full">{prettyArgs}</div> </div> </div> </div> </div> )} </div> ) : action?.kind === 'delegateAction' ? ( <div className="py-2 ml-6"> {action?.args?.senderId} {[...action.args.actions] .sort( (actionA, actionB) => actionA.delegateIndex - actionB.delegateIndex, ) .map((subaction) => ( <Widget key={subaction.delegateIndex} src={`${config.ownerId}/widget/bos-components.components.Transactions.ReceiptKind`} props={{ network: network, t: t, action: action, isTxTypeActive: true, Link, }} /> ))} </div> ) : null ) : null} </div> ); } return MainComponent(props, context);