From d154b6996f232028e6617c3d6df3a8bc8e459f1c Mon Sep 17 00:00:00 2001 From: Hakim <59644786+haammar-ledger@users.noreply.github.com> Date: Thu, 10 Mar 2022 10:51:13 +0100 Subject: [PATCH] Cosmos-JS - Fix random balance changes (#1797) * Cleanup Cosmos.api and remove default values * forEach doesn't behave well with async/await * Restore some necessary API default values * Fix lint error --- src/families/cosmos/api/Cosmos.ts | 369 +++++++++++++----------------- 1 file changed, 154 insertions(+), 215 deletions(-) diff --git a/src/families/cosmos/api/Cosmos.ts b/src/families/cosmos/api/Cosmos.ts index 5d9e0127f3..c515b08ed7 100644 --- a/src/families/cosmos/api/Cosmos.ts +++ b/src/families/cosmos/api/Cosmos.ts @@ -9,37 +9,41 @@ const defaultEndpoint = getEnv( ).replace(/\/$/, ""); export const getAccountInfo = async (address: string): Promise => { - const [ - { accountNumber, sequence }, - balances, - blockHeight, - txs, - delegations, - redelegations, - unbondings, - withdrawAddress, - ] = await Promise.all([ - getAccount(address), - getAllBalances(address), - getHeight(), - getTransactions(address), - getDelegators(address), - getRedelegations(address), - getUnbondings(address), - getWithdrawAddress(address), - ]); - - return { - balances, - blockHeight, - txs, - delegations, - redelegations, - unbondings, - withdrawAddress, - accountNumber, - sequence, - }; + try { + const [ + { accountNumber, sequence }, + balances, + blockHeight, + txs, + delegations, + redelegations, + unbondings, + withdrawAddress, + ] = await Promise.all([ + getAccount(address), + getAllBalances(address), + getHeight(), + getTransactions(address), + getDelegations(address), + getRedelegations(address), + getUnbondings(address), + getWithdrawAddress(address), + ]); + + return { + balances, + blockHeight, + txs, + delegations, + redelegations, + unbondings, + withdrawAddress, + accountNumber, + sequence, + }; + } catch (e: any) { + throw new Error(`"Error during cosmos synchronization: "${e.message}`); + } }; export const getAccount = async (address: string): Promise => { @@ -66,77 +70,96 @@ export const getAccount = async (address: string): Promise => { if (data.account.sequence) { response.sequence = parseInt(data.account.sequence); } - // eslint-disable-next-line no-empty } catch (e) {} - return response; }; -export const getDelegators = async (address: string): Promise => { - const delegators: Array = []; +export const getChainId = async (): Promise => { + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/node_info`, + }); - try { - const { data } = await network({ - method: "GET", - url: `${defaultEndpoint}/cosmos/staking/v1beta1/delegations/${address}`, - }); + return data.node_info.network; +}; - let status = "unbonded"; - const statusMap = { - BOND_STATUS_UNBONDED: "unbonded", - BOND_STATUS_UNBONDING: "unbonding", - BOND_STATUS_BONDED: "bonded", - }; +const getHeight = async (): Promise => { + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/base/tendermint/v1beta1/blocks/latest`, + }); - data.delegation_responses.forEach(async (d) => { - try { - const { data } = await network({ - method: "GET", - url: `${defaultEndpoint}/cosmos/staking/v1beta1/validators/${d.delegation.validator_address}`, - }); + return data.block.header.height; +}; - status = statusMap[data.validator.status] || "unbonded"; +const getAllBalances = async (address: string): Promise => { + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/bank/v1beta1/balances/${address}`, + }); - // eslint-disable-next-line no-empty - } catch (e) {} + let amount = new BigNumber(0); - delegators.push({ - validatorAddress: d.delegation.validator_address, - amount: new BigNumber(d.balance.amount), - pendingRewards: new BigNumber(0), - status, - }); + for (const elem of data.balances) { + amount = amount.plus(elem.amount); + } + + return amount; +}; + +const getDelegations = async (address: string): Promise => { + const delegations: Array = []; + + const { data: data1 } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/staking/v1beta1/delegations/${address}`, + }); + + let status = "unbonded"; + const statusMap = { + BOND_STATUS_UNBONDED: "unbonded", + BOND_STATUS_UNBONDING: "unbonding", + BOND_STATUS_BONDED: "bonded", + }; + + for (const d of data1.delegation_responses) { + const { data: data2 } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/staking/v1beta1/validators/${d.delegation.validator_address}`, }); - try { - const { data } = await network({ - method: "GET", - url: `${defaultEndpoint}/cosmos/distribution/v1beta1/delegators/${address}/rewards`, - }); + status = statusMap[data2.validator.status] || "unbonded"; - data.rewards.forEach((r) => { - delegators.forEach(async (d) => { - if (r.validator_address === d.validatorAddress) { - r.reward.forEach(async (v) => { - d.pendingRewards = d.pendingRewards.plus( - new BigNumber(v.amount).integerValue(BigNumber.ROUND_CEIL) - ); - }); - } - }); - }); + delegations.push({ + validatorAddress: d.delegation.validator_address, + amount: new BigNumber(d.balance.amount), + pendingRewards: new BigNumber(0), + status, + }); + } - // eslint-disable-next-line no-empty - } catch (e) {} + const { data: data3 } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/distribution/v1beta1/delegators/${address}/rewards`, + }); - return delegators; - } catch (e) { - return []; + for (const r of data3.rewards) { + for (const d of delegations) { + if (r.validator_address === d.validatorAddress) { + for (const reward of r.reward) { + d.pendingRewards = d.pendingRewards.plus( + new BigNumber(reward.amount).integerValue(BigNumber.ROUND_CEIL) + ); + } + } + } } + + return delegations; }; -export const getRedelegations = async (address: string): Promise => { +const getRedelegations = async (address: string): Promise => { const redelegations: Array = []; const { data } = await network({ @@ -144,21 +167,21 @@ export const getRedelegations = async (address: string): Promise => { url: `${defaultEndpoint}/cosmos/staking/v1beta1/delegators/${address}/redelegations`, }); - data.redelegation_responses.forEach((elem) => { - elem.entries.forEach((entry) => { + for (const r of data.redelegation_responses) { + for (const entry of r.entries) { redelegations.push({ - validatorSrcAddress: elem.redelegation.validator_src_address, - validatorDstAddress: elem.redelegation.validator_dst_address, + validatorSrcAddress: r.redelegation.validator_src_address, + validatorDstAddress: r.redelegation.validator_dst_address, amount: new BigNumber(entry.redelegation_entry.initial_balance), completionDate: new Date(entry.redelegation_entry.completion_time), }); - }); - }); + } + } return redelegations; }; -export const getUnbondings = async (address: string): Promise => { +const getUnbondings = async (address: string): Promise => { const unbondings: Array = []; const { data } = await network({ @@ -166,46 +189,29 @@ export const getUnbondings = async (address: string): Promise => { url: `${defaultEndpoint}/cosmos/staking/v1beta1/delegators/${address}/unbonding_delegations`, }); - data.unbonding_responses.forEach((elem) => { - elem.entries.forEach((entries) => { + for (const u of data.unbonding_responses) { + for (const entry of u.entries) { unbondings.push({ - validatorAddress: elem.validator_address, - amount: new BigNumber(entries.initial_balance), - completionDate: new Date(entries.completion_time), + validatorAddress: u.validator_address, + amount: new BigNumber(entry.initial_balance), + completionDate: new Date(entry.completion_time), }); - }); - }); + } + } return unbondings; }; -export const isValidRecipent = async (address: string): Promise => { - try { - await network({ - method: "GET", - url: `${defaultEndpoint}/cosmos/bank/v1beta1/balances/${address}`, - }); - - return true; - } catch (e) { - return false; - } -}; - -export const getWithdrawAddress = async (address: string): Promise => { - try { - const { data } = await network({ - method: "GET", - url: `${defaultEndpoint}/cosmos/distribution/v1beta1/delegators/${address}/withdraw_address`, - }); +const getWithdrawAddress = async (address: string): Promise => { + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/distribution/v1beta1/delegators/${address}/withdraw_address`, + }); - return data.withdraw_address; - } catch (e) { - return ""; - } + return data.withdraw_address; }; -export const getTransactions = async (address: string): Promise => { +const getTransactions = async (address: string): Promise => { const receive = await network({ method: "GET", url: @@ -223,45 +229,16 @@ export const getTransactions = async (address: string): Promise => { return [...receive.data.tx_responses, ...send.data.tx_responses]; }; -export const broadcast = async ({ - signedOperation: { operation, signature }, -}): Promise => { - const { data } = await network({ - method: "POST", - url: `${defaultEndpoint}/cosmos/tx/v1beta1/txs`, - data: { - tx_bytes: Array.from(Uint8Array.from(Buffer.from(signature, "hex"))), - mode: "BROADCAST_MODE_SYNC", - }, - }); - - if (data.tx_response.code != 0) { - // error codes: https://github.com/cosmos/cosmos-sdk/blob/master/types/errors/errors.go - throw new Error( - "invalid broadcast return (code: " + - (data.tx_response.code || "?") + - ", message: '" + - (data.tx_response.raw_log || "") + - "')" - ); - } - - return patchOperationWithHash( - { ...operation, blockHeight: data.tx_response.height }, - data.tx_response.txhash - ); -}; - -export const getBlock = async (height: number): Promise => { +export const isValidRecipent = async (address: string): Promise => { try { - const { data } = await network({ + await network({ method: "GET", - url: `${defaultEndpoint}/cosmos/base/tendermint/v1beta1/blocks/${height}`, + url: `${defaultEndpoint}/cosmos/bank/v1beta1/balances/${address}`, }); - return data; + return true; } catch (e) { - return {}; + return false; } }; @@ -281,69 +258,31 @@ export const simulate = async (tx_bytes: Array): Promise => { } }; -export const getHeight = async (): Promise => { - try { - const { data } = await network({ - method: "GET", - url: `${defaultEndpoint}/cosmos/base/tendermint/v1beta1/blocks/latest`, - }); - - return data.block.header.height; - } catch (e) { - return 0; - } -}; - -export const getAllBalances = async (address: string): Promise => { +export const broadcast = async ({ + signedOperation: { operation, signature }, +}): Promise => { const { data } = await network({ - method: "GET", - url: `${defaultEndpoint}/cosmos/bank/v1beta1/balances/${address}`, - }); - - let amount = new BigNumber(0); - - data.balances.forEach((elem) => { - amount = amount.plus(elem.amount); + method: "POST", + url: `${defaultEndpoint}/cosmos/tx/v1beta1/txs`, + data: { + tx_bytes: Array.from(Uint8Array.from(Buffer.from(signature, "hex"))), + mode: "BROADCAST_MODE_SYNC", + }, }); - return amount; -}; - -export const getChainId = async (): Promise => { - try { - const { data } = await network({ - method: "GET", - url: `${defaultEndpoint}/node_info`, - }); - - return data.node_info.network; - } catch (e) { - return ""; - } -}; - -export const getSequence = async (address: string): Promise => { - try { - const { data } = await network({ - method: "GET", - url: `${defaultEndpoint}/cosmos/auth/v1beta1/accounts/${address}`, - }); - - return data.account.sequence; - } catch (e) { - return 0; + if (data.tx_response.code != 0) { + // error codes: https://github.com/cosmos/cosmos-sdk/blob/master/types/errors/errors.go + throw new Error( + "invalid broadcast return (code: " + + (data.tx_response.code || "?") + + ", message: '" + + (data.tx_response.raw_log || "") + + "')" + ); } -}; - -export const getAccountNumber = async (address: string): Promise => { - try { - const { data } = await network({ - method: "GET", - url: `${defaultEndpoint}/cosmos/auth/v1beta1/accounts/${address}`, - }); - return data.account.account_number; - } catch (e) { - return 0; - } + return patchOperationWithHash( + { ...operation, blockHeight: data.tx_response.height }, + data.tx_response.txhash + ); };