import { CardanoUTXO } from "./utxos"; export type CardanoAsset = { policy: string; asset: string; quantity: number; }; export type Multiasset = { [policyId: string]: Multiasset | number; }; const areEqual = (asset1: CardanoAsset, asset2: CardanoAsset) => { return asset1?.asset === asset2.asset && asset1.policy === asset2.policy; }; const getAssetsFromUtxo = (utxo: CardanoUTXO): CardanoAsset[] => { const multiassets = utxo.utxo.amount.multiasset; const items = multiassets ? Object.keys(multiassets).reduce( (acc, policyKey) => [ ...acc, ...getAssetsFromPolicyMap( policyKey, multiassets[policyKey] as Multiasset ), ], [] as CardanoAsset[] ) : ([] as CardanoAsset[]); return items; }; const getExceedingAssetsFromLeft = ( a1?: CardanoAsset[], a2?: CardanoAsset[] ) => { const diff: CardanoAsset[] = []; const mergedA1 = mergeAssets(a1); const mergedA2 = mergeAssets(a2); mergedA1.forEach((a) => { const assetInSibling = mergedA2?.find((b) => areEqual(b, a)); if (assetInSibling) { // If it exists, check the diff in quantity const quantityDiff = Math.abs(assetInSibling.quantity - a.quantity); if (quantityDiff !== 0) { diff.push({ ...a, quantity: Math.abs(assetInSibling.quantity - a.quantity), }); } } else { diff.push(a); // asset does not exist on sibling, so it's a full diff } }); return diff; }; const isNumber = (value: unknown): value is number => { return typeof value === "number"; }; const getAssetsFromPolicyMap = ( policyKey: string, policyMap: Multiasset ): CardanoAsset[] => { return policyMap ? Object.keys(policyMap).reduce( (acc, assetName) => [ ...acc, { policy: policyKey, asset: assetName, quantity: (isNumber(policyMap[assetName]) ? policyMap[assetName] : 1) as number, }, ], [] as CardanoAsset[] ) : ([] as CardanoAsset[]); }; const fromAssetsToPolicyMap = (assets: CardanoAsset[]): Multiasset => { return assets.reduce((acc, { policy, asset, quantity }) => { if (acc[policy]) { (acc[policy] as Multiasset)[asset] = quantity || 1; } else { acc[policy] = { [asset]: quantity || 1, }; } return acc; }, {} as Multiasset); }; const toCardanoAssets = ( assets: { unit: string; quantity: string }[] ): CardanoAsset[] => { return assets.reduce((acc, { unit, quantity }) => { if (unit === "lovelace") return acc; const details = getAssetDetails(unit); return [ ...acc, { policy: details.assetPolicy, asset: details.assetName, quantity: parseInt(quantity), }, ]; }, [] as CardanoAsset[]); }; const toCardanoAsset = (asset: { unit: string; quantity: string; }): CardanoAsset => { const details = getAssetDetails(asset.unit); return { policy: details.assetPolicy, asset: details.assetName, quantity: parseInt(asset.quantity), }; }; const mergeAssets = (assets?: CardanoAsset[]) => { return assets ? assets.reduce((acc, a) => { const foundAsset = acc.find((v) => areEqual(a, v)); if (foundAsset) { foundAsset.quantity += a.quantity; } else { acc.push(a); } return acc; }, [] as CardanoAsset[]) : []; }; const toMultiasset = ( assets: { unit: string; quantity: string }[] ): Multiasset => { return fromAssetsToPolicyMap(toCardanoAssets(assets)); }; const getAssetDetails = (asset: string) => { const assetPolicy = asset.substring(0, 56); const assetName = asset.substring(56); return { assetPolicy, assetName, }; }; export const AssetUtils = { areEqual, getAssetsFromPolicyMap, getAssetsFromUtxo, fromAssetsToPolicyMap, getAssetDetails, toMultiasset, toCardanoAsset, toCardanoAssets, mergeAssets, getExceedingAssetsFromLeft, };