Skip to content

Commit

Permalink
refactor: improved account balance mapping genration code
Browse files Browse the repository at this point in the history
  • Loading branch information
AlanVerbner committed Jan 20, 2021
1 parent 6e1c1de commit e20dc24
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 125 deletions.
6 changes: 3 additions & 3 deletions cardano-rosetta-server/src/server/models.ts
Expand Up @@ -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 {
Expand Down
3 changes: 0 additions & 3 deletions cardano-rosetta-server/src/server/openApi.json
Expand Up @@ -967,9 +967,6 @@
},
"currency": {
"$ref": "#/components/schemas/Currency"
},
"metadata": {
"type": "object"
}
}
},
Expand Down
128 changes: 54 additions & 74 deletions cardano-rosetta-server/src/server/utils/data-mapper.ts
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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<string, Components.Schemas.Amount>(),
adaCoins: new Array<Components.Schemas.Coin>()
}
);
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 {
Expand Down
8 changes: 2 additions & 6 deletions cardano-rosetta-server/src/types/rosetta-types.d.ts
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit e20dc24

Please sign in to comment.