diff --git a/cardano-rosetta-server/src/server/models.ts b/cardano-rosetta-server/src/server/models.ts index 5eb415159..1ffe29cfc 100644 --- a/cardano-rosetta-server/src/server/models.ts +++ b/cardano-rosetta-server/src/server/models.ts @@ -44,9 +44,9 @@ export interface Utxo { value: string; transactionHash: string; index: number; - name: string; - policy: string; - quantity: string; + name?: string; + policy?: string; + quantity?: string; } export interface TransactionInOut { diff --git a/cardano-rosetta-server/src/server/openApi.json b/cardano-rosetta-server/src/server/openApi.json index e047e2ee6..225b5b047 100644 --- a/cardano-rosetta-server/src/server/openApi.json +++ b/cardano-rosetta-server/src/server/openApi.json @@ -967,9 +967,6 @@ }, "currency": { "$ref": "#/components/schemas/Currency" - }, - "metadata": { - "type": "object" } } }, diff --git a/cardano-rosetta-server/src/server/utils/data-mapper.ts b/cardano-rosetta-server/src/server/utils/data-mapper.ts index fd7dbe32a..171454539 100644 --- a/cardano-rosetta-server/src/server/utils/data-mapper.ts +++ b/cardano-rosetta-server/src/server/utils/data-mapper.ts @@ -19,18 +19,24 @@ import { Block, BlockUtxos, BalanceAtBlock, Network, PopulatedTransaction, Utxo, const COIN_SPENT_ACTION = 'coin_spent'; const COIN_CREATED_ACTION = 'coin_created'; -export const mapAmount = (lovelace: string): Components.Schemas.Amount => ({ - value: lovelace, +export const mapAmount = ( + value: string, + symbol = ADA, + decimals = ADA_DECIMALS, + metadata?: any +): Components.Schemas.Amount => ({ + value, currency: { - symbol: ADA, - decimals: ADA_DECIMALS + symbol, + decimals, + metadata } }); export const mapMaAmount = (maUtxo: Utxo): Components.Schemas.Amount => ({ - value: maUtxo.quantity, + value: maUtxo.quantity || '', currency: { - symbol: maUtxo.name, + symbol: maUtxo.name || '', decimals: MULTI_ASSET_DECIMALS, metadata: { policy: maUtxo.policy @@ -253,61 +259,6 @@ export const mapToRosettaBlock = (block: Block, transactions: PopulatedTransacti transactions: transactions.map(mapToRosettaTransaction) }); -/** - * Processes AccountBalance response utxo section - * @param utxoDetails - */ -const parseUtxoDetails = (utxoDetails: Utxo[]): Components.Schemas.Coin[] => { - const coinList: Components.Schemas.Coin[] = []; - utxoDetails.forEach((utxoDetail, index) => { - const coinId = { identifier: `${utxoDetail.transactionHash}:${utxoDetail.index}` }; - if (index === 0 || utxoDetails[index - 1].transactionHash !== utxoDetail.transactionHash) - coinList.push({ - amount: mapAmount(utxoDetail.value), - coin_identifier: coinId - }); - if (utxoDetail.name && utxoDetail.policy) { - coinList.push({ - amount: mapMaAmount(utxoDetail), - coin_identifier: coinId - }); - } - }); - return coinList; -}; - -const calculateTotalMaAmount = (multiAssetsUtxo: Utxo[], maUtxo: Utxo): string => - multiAssetsUtxo - .reduce( - (accum, current) => - current.policy === maUtxo.policy && - current.name === maUtxo.name && - current.transactionHash !== maUtxo.transactionHash - ? accum + BigInt(current.quantity) - : accum, - BigInt(maUtxo.quantity) - ) - .toString(); - -/** - * Generates an Amount list for multi assets utxos - * @param multiAssetsUtxo multi assets utxos - */ -const convertToMultiAssetBalances = (multiAssetsUtxo: Utxo[]): Components.Schemas.Amount[] => { - const multiAssetsAmounts: Utxo[] = []; - multiAssetsUtxo.forEach(maUtxo => { - if (!multiAssetsAmounts.some(maAmount => maAmount.policy === maUtxo.policy && maAmount.name === maUtxo.name)) { - const totalMaAmount = calculateTotalMaAmount(multiAssetsUtxo, maUtxo); - - multiAssetsAmounts.push({ - ...maUtxo, - quantity: totalMaAmount - }); - } - }); - return multiAssetsAmounts.map(mapMaAmount); -}; - /** * Generates an AccountBalance response object * @param blockBalanceData @@ -318,27 +269,56 @@ export const mapToAccountBalanceResponse = ( ): Components.Schemas.AccountBalanceResponse => { // FIXME: handle this in a better way if (isBlockUtxos(blockBalanceData)) { - const balanceForAddress = blockBalanceData.utxos - .reduce((acum, current, index) => { + const balanceForAddress = blockBalanceData.utxos.reduce( + ({ balances, adaCoins }, current, index) => { const previousValue = blockBalanceData.utxos[index - 1]; - if (index === 0) return acum + BigInt(current.value); // This function accumulates ADA value. As there might be several, one for each multi-asset, we need to // avoid counting them twice - const isTheSameUnspent = - current.transactionHash === previousValue.transactionHash && current.index === previousValue.index; - if (isTheSameUnspent) return acum; - return acum + BigInt(current.value); - }, BigInt(0)) - .toString(); - const multiAssetUtxo = blockBalanceData.utxos.filter(utxo => utxo.policy && utxo.name); - const multiAssetsBalance = convertToMultiAssetBalances(multiAssetUtxo); + if ( + !previousValue || + current.transactionHash !== previousValue.transactionHash || + current.index !== previousValue.index + ) { + const key = ADA; + const entry = balances.get(key) ?? mapAmount('0'); + balances.set(key, { + ...entry, + value: (BigInt(entry.value) + BigInt(current.value)).toString() + }); + const coinId = `${current.transactionHash}:${current.index}`; + adaCoins.push({ + coin_identifier: { + identifier: coinId + }, + amount: mapAmount(current.value) + }); + } + if (current.policy && current.name && current.quantity) { + // MultiAsset + const key = current.policy + current.name; + const entry = + balances.get(key) ?? mapAmount('0', current.name, MULTI_ASSET_DECIMALS, { policyId: current.policy }); + balances.set(key, { + ...entry, + value: (BigInt(entry.value) + BigInt(current.quantity)).toString() + }); + // coins.set() + } + return { balances, adaCoins }; + }, + { + balances: new Map(), + adaCoins: new Array() + } + ); + const { balances, adaCoins } = balanceForAddress; return { block_identifier: { index: blockBalanceData.block.number, hash: blockBalanceData.block.hash }, - balances: [mapAmount(balanceForAddress), ...multiAssetsBalance], - coins: parseUtxoDetails(blockBalanceData.utxos) + balances: balances.size === 0 ? [mapAmount('0')] : [...balances.values()], + coins: [...adaCoins.values()] }; } return { diff --git a/cardano-rosetta-server/src/types/rosetta-types.d.ts b/cardano-rosetta-server/src/types/rosetta-types.d.ts index 0a37a2f7a..9db824bee 100644 --- a/cardano-rosetta-server/src/types/rosetta-types.d.ts +++ b/cardano-rosetta-server/src/types/rosetta-types.d.ts @@ -90,7 +90,6 @@ declare namespace Components { */ value: string; currency: /* Currency is composed of a canonical Symbol and Decimals. This Decimals value is used to convert an Amount.Value from atomic units (Satoshis) to standard units (Bitcoins). */ Currency; - metadata?: {}; } /** * Blocks contain an array of Transactions that occurred at a particular BlockIdentifier. A hard requirement for blocks returned by Rosetta implementations is that they MUST be _inalterable_: once a client has requested and received a block identified by a specific BlockIndentifier, all future calls for that same BlockIdentifier must return the same block contents. @@ -571,9 +570,9 @@ declare namespace Components { staking_credential?: /* PublicKey contains a public key byte array for a particular CurveType encoded in hex. Note that there is no PrivateKey struct as this is NEVER the concern of an implementation. */ PublicKey; pool_key_hash?: string; /** - * All the multiassets for the given unspent + * A token bundle is a heterogeneous (‘mixed’) collection of tokens. Any tokens can be bundled together. Token bundles are the standard - and only - way to represent and store assets on the Cardano blockchain. */ - tokenBundle?: /* A token bundle is a heterogeneous (‘mixed’) collection of tokens. Any tokens can be bundled together. Token bundles are the standard - and only - way to represent and store assets on the Cardano blockchain. */ TokenBundleItem[]; + tokenBundle?: TokenBundleItem[]; } /** * OperationStatus is utilized to indicate which Operation status are considered successful. @@ -715,9 +714,6 @@ declare namespace Components { * 1582833600000 */ export type Timestamp = number; // int64 - /** - * A token bundle is a heterogeneous (‘mixed’) collection of tokens. Any tokens can be bundled together. Token bundles are the standard - and only - way to represent and store assets on the Cardano blockchain. - */ export interface TokenBundleItem { /** * Policy Id hex string diff --git a/cardano-rosetta-server/test/e2e/fixture-data.ts b/cardano-rosetta-server/test/e2e/fixture-data.ts index 9e549866a..8da72ceea 100644 --- a/cardano-rosetta-server/test/e2e/fixture-data.ts +++ b/cardano-rosetta-server/test/e2e/fixture-data.ts @@ -2544,7 +2544,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '424e42', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2554,7 +2554,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '425443', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2564,7 +2564,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '444f54', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2574,7 +2574,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '455448', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2584,7 +2584,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '4c494e4b', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2594,7 +2594,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '4c5443', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2604,7 +2604,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '585250', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2614,7 +2614,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '4154414441636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2624,7 +2624,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '6e7574636f696e', metadata: { - policy: 'b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a7' + policyId: 'b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a7' } } }, @@ -2634,7 +2634,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '5045414345746f6b656e', metadata: { - policy: 'dabee8ee0f3f0906ba0542b06924c3e9c477a013cfe7d326a8ea9beb' + policyId: 'dabee8ee0f3f0906ba0542b06924c3e9c477a013cfe7d326a8ea9beb' } } }, @@ -2644,7 +2644,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '6d616368746c32636f696e', metadata: { - policy: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' + policyId: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' } } }, @@ -2654,7 +2654,7 @@ export const address1vpfAccountBalances = [ decimals: 0, symbol: '61646f736961', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2680,7 +2680,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '424e42', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2693,7 +2693,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '425443', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2706,7 +2706,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '444f54', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2719,7 +2719,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '455448', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2732,7 +2732,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '4c494e4b', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2745,7 +2745,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '4c5443', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2758,7 +2758,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '585250', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2780,7 +2780,7 @@ export const address1vpfCoins = [ currency: { decimals: 0, metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' }, symbol: '4154414441636f696e' } @@ -2804,7 +2804,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '6e7574636f696e', metadata: { - policy: 'b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a7' + policyId: 'b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a7' } } } @@ -2827,7 +2827,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '5045414345746f6b656e', metadata: { - policy: 'dabee8ee0f3f0906ba0542b06924c3e9c477a013cfe7d326a8ea9beb' + policyId: 'dabee8ee0f3f0906ba0542b06924c3e9c477a013cfe7d326a8ea9beb' } } } @@ -2850,7 +2850,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '6d616368746c32636f696e', metadata: { - policy: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' + policyId: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' } } } @@ -2873,7 +2873,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '61646f736961', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2896,7 +2896,7 @@ export const address1vpfCoins = [ decimals: 0, symbol: '4154414441636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -2947,7 +2947,7 @@ export const balancesAtBlock213891 = [ decimals: 0, symbol: '74657374746f6b656e', metadata: { - policy: '06e5f0ade7121aaefa0e7ec53cac61820d774de0c12c83e8597627ff' + policyId: '06e5f0ade7121aaefa0e7ec53cac61820d774de0c12c83e8597627ff' } } }, @@ -2957,7 +2957,7 @@ export const balancesAtBlock213891 = [ decimals: 0, symbol: '4154414441636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2967,7 +2967,7 @@ export const balancesAtBlock213891 = [ decimals: 0, symbol: '6d616368746c636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -2977,7 +2977,7 @@ export const balancesAtBlock213891 = [ decimals: 0, symbol: '6d616368746c32636f696e', metadata: { - policy: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' + policyId: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' } } } @@ -3003,7 +3003,7 @@ export const coinsAtBlock213891 = [ decimals: 0, symbol: '74657374746f6b656e', metadata: { - policy: '06e5f0ade7121aaefa0e7ec53cac61820d774de0c12c83e8597627ff' + policyId: '06e5f0ade7121aaefa0e7ec53cac61820d774de0c12c83e8597627ff' } } } @@ -3016,7 +3016,7 @@ export const coinsAtBlock213891 = [ decimals: 0, symbol: '4154414441636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -3029,7 +3029,7 @@ export const coinsAtBlock213891 = [ decimals: 0, symbol: '6d616368746c636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -3042,7 +3042,7 @@ export const coinsAtBlock213891 = [ decimals: 0, symbol: '6d616368746c32636f696e', metadata: { - policy: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' + policyId: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' } } } @@ -3062,7 +3062,7 @@ export const balancesAtBlock213892 = [ decimals: 0, symbol: '4154414441636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -3072,7 +3072,7 @@ export const balancesAtBlock213892 = [ decimals: 0, symbol: '6d616368746c636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }, @@ -3082,7 +3082,7 @@ export const balancesAtBlock213892 = [ decimals: 0, symbol: '6d616368746c32636f696e', metadata: { - policy: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' + policyId: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' } } } @@ -3108,7 +3108,7 @@ export const coinsAtBlock213892 = [ decimals: 0, symbol: '6d616368746c32636f696e', metadata: { - policy: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' + policyId: 'ecd07b4ef62f37a68d145de8efd60c53d288dd5ffc641215120cc3db' } } } @@ -3121,7 +3121,7 @@ export const coinsAtBlock213892 = [ decimals: 0, symbol: '6d616368746c636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } } @@ -3134,7 +3134,7 @@ export const coinsAtBlock213892 = [ decimals: 0, symbol: '4154414441636f696e', metadata: { - policy: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' + policyId: '34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518' } } }