From 122fc1701cf7d7fa9c4e8d152acba552c61e2120 Mon Sep 17 00:00:00 2001 From: Hakim <59644786+haammar-ledger@users.noreply.github.com> Date: Tue, 15 Mar 2022 08:32:08 +0100 Subject: [PATCH] Cosmos JS implementation (experimental) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * Cosmos js (#1701) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot Co-authored-by: Alexandre Alouit * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * Cosmos js (#1711) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature Co-authored-by: Alexandre Alouit * Increase timeout for Cosmos bot spec * Increase timeout for Cosmos bot spec * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * Cosmos JS (#1713) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix Co-authored-by: Alexandre Alouit * fix regression * Cosmos JS (#1715) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression Co-authored-by: Alexandre Alouit * update optimistic operation fix regression add operation type more consistent fee * Cosmos JS (#1716) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee Co-authored-by: Alexandre Alouit * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping * Cosmos JS (#1721) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping Co-authored-by: Alexandre Alouit * fix mixed styles * fix array cast type * fix more determinist operation data * Cosmos JS (#1725) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping * fix mixed styles * fix array cast type * fix more determinist operation data Co-authored-by: Alexandre Alouit * adjust constants of the cosmos spec to make it more balanced * bot run * bot run * simplify speculos search logic * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * fix could not find optimisticOperation in redelegate transaction * trigger bot * trigger bot * Cosmos JS (#1730) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping * fix mixed styles * fix array cast type * fix more determinist operation data * fix could not find optimisticOperation in redelegate transaction Co-authored-by: Alexandre Alouit * restore getStargateRewardsState methode * Cosmos JS (#1731) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping * fix mixed styles * fix array cast type * fix more determinist operation data * fix could not find optimisticOperation in redelegate transaction * restore getStargateRewardsState methode Co-authored-by: Alexandre Alouit * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * try to limit even more the cost of delegation * lint * Add files via upload * amount of the operation more close * return transaction in prepareTransaction * prevent cosmos-js bot to do invalid scenarios of amount=0 * keep immutable paradigm for prepareTransaction * bot: do not limit a maxRun for 'send some' * Cosmos JS (#1743) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping * fix mixed styles * fix array cast type * fix more determinist operation data * fix could not find optimisticOperation in redelegate transaction * restore getStargateRewardsState methode * amount of the operation more close * return transaction in prepareTransaction * keep immutable paradigm for prepareTransaction Co-authored-by: Alexandre Alouit * add .github/workflows/bot7-meredenis.yml * fix amount update in prepareTransaction * fix amount control to be more specific * fix BigNumber compare * Cosmos JS (#1746) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping * fix mixed styles * fix array cast type * fix more determinist operation data * fix could not find optimisticOperation in redelegate transaction * restore getStargateRewardsState methode * amount of the operation more close * return transaction in prepareTransaction * keep immutable paradigm for prepareTransaction * fix amount update in prepareTransaction * fix amount control to be more specific * fix BigNumber compare Co-authored-by: Alexandre Alouit * fix spendableBalance * Cosmos JS (#1749) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping * fix mixed styles * fix array cast type * fix more determinist operation data * fix could not find optimisticOperation in redelegate transaction * restore getStargateRewardsState methode * amount of the operation more close * return transaction in prepareTransaction * keep immutable paradigm for prepareTransaction * fix amount update in prepareTransaction * fix amount control to be more specific * fix BigNumber compare * fix spendableBalance Co-authored-by: Alexandre Alouit * separate postBuildTransaction logic + fix use useAllAmount * Cosmos JS (#1751) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping * fix mixed styles * fix array cast type * fix more determinist operation data * fix could not find optimisticOperation in redelegate transaction * restore getStargateRewardsState methode * amount of the operation more close * return transaction in prepareTransaction * keep immutable paradigm for prepareTransaction * fix amount update in prepareTransaction * fix amount control to be more specific * fix BigNumber compare * fix spendableBalance * separate postBuildTransaction logic + fix use useAllAmount Co-authored-by: Alexandre Alouit * bot: lower claim reward at 1000 * bot: lower the # of // delegations for cosmos * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * bot: fixes 0 atom undelegate * trigger bot * v21.33.1-cosmos.0 * trigger bot * trigger bot * update cosmos specs for claim rewards * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * fix rewards * trigger bot * Cosmos JS (#1761) * increase gas amplifier * fix payload construction handle payload atomic construction * More accurate gas amplifier * increase gas amplifier * use same node for calculation and broadcast * fix amount payload * fix fees/gas calculation * fix signature fix public key when account is derivate * fix fees regression * More accurate pubkey selection * don't use extra.tx_bytes * fix pubkey selection * simplify hex serialization * update transaction: more strict types * many things restruct operation builder simulate now return int prepareTransaction use patch format * accuracy more accuracy int value small refactor * remove useless isPreValidation * fix strange edge effect of ledger live desktop * Update xpub during sync * temporary enable log for bot * LL-9159 cosmos node * Update js-signOperation.ts revert back test trace for bot * fix signature * fix redelegate payload * fix payload send transaction when sendmax * fix optimistic operation type * fix typo * bugfix * fix regression * update optimistic operation fix regression add operation type more consistent fee * fix prettier * more deterministic transaction parsing * clarify code readable * adjust sender and recipient * fix fees when is ibc transaction * fix redelegations data mapping * fix mixed styles * fix array cast type * fix more determinist operation data * fix could not find optimisticOperation in redelegate transaction * restore getStargateRewardsState methode * amount of the operation more close * return transaction in prepareTransaction * keep immutable paradigm for prepareTransaction * fix amount update in prepareTransaction * fix amount control to be more specific * fix BigNumber compare * fix spendableBalance * separate postBuildTransaction logic + fix use useAllAmount * fix rewards Co-authored-by: Alexandre Alouit * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * fix unbondings * fix fees calculation and add cache usage * trigger bot * v21.33.1-cosmos.1 * v21.33.1-cosmos.2 * trigger bot * trigger bot * trigger bot * trigger bot * cosmos bot: lower the amount for general tx & lower part alloc in delegate * ensure for all cosmos mutation that only one op results of a tx * be more conform between optimistic and final operation (#1786) Co-authored-by: Alexandre Alouit * Filecoin fixes (#1740) * make use of useAllAmount flag * make use of useAllAmount on signing tx process * refactor signOperation process to use extra field better * add deviceTransactionConfig for filecoin * re org fields for confirm tx * change fields order * add extra field parsers * move extra field parsers to the correct file * fix lint issue * Create dependencies.md * Add env var to enable learn staging URL * lint * Bump urijs from 1.19.8 to 1.19.9 (#1774) Bumps [urijs](https://github.com/medialize/URI.js) from 1.19.8 to 1.19.9. - [Release notes](https://github.com/medialize/URI.js/releases) - [Changelog](https://github.com/medialize/URI.js/blob/gh-pages/CHANGELOG.md) - [Commits](https://github.com/medialize/URI.js/compare/v1.19.8...v1.19.9) --- updated-dependencies: - dependency-name: urijs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump urijs from 1.19.8 to 1.19.9 in /tools (#1776) Bumps [urijs](https://github.com/medialize/URI.js) from 1.19.8 to 1.19.9. - [Release notes](https://github.com/medialize/URI.js/releases) - [Changelog](https://github.com/medialize/URI.js/blob/gh-pages/CHANGELOG.md) - [Commits](https://github.com/medialize/URI.js/compare/v1.19.8...v1.19.9) --- updated-dependencies: - dependency-name: urijs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump urijs from 1.19.8 to 1.19.9 in /cli (#1775) Bumps [urijs](https://github.com/medialize/URI.js) from 1.19.8 to 1.19.9. - [Release notes](https://github.com/medialize/URI.js/releases) - [Changelog](https://github.com/medialize/URI.js/blob/gh-pages/CHANGELOG.md) - [Commits](https://github.com/medialize/URI.js/compare/v1.19.8...v1.19.9) --- updated-dependencies: - dependency-name: urijs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump urijs from 1.19.8 to 1.19.9 in /mobile-test-app (#1773) Bumps [urijs](https://github.com/medialize/URI.js) from 1.19.8 to 1.19.9. - [Release notes](https://github.com/medialize/URI.js/releases) - [Changelog](https://github.com/medialize/URI.js/blob/gh-pages/CHANGELOG.md) - [Commits](https://github.com/medialize/URI.js/compare/v1.19.8...v1.19.9) --- updated-dependencies: - dependency-name: urijs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump plist from 3.0.1 to 3.0.4 in /mobile-test-app (#1767) Bumps [plist](https://github.com/TooTallNate/node-plist) from 3.0.1 to 3.0.4. - [Release notes](https://github.com/TooTallNate/node-plist/releases) - [Changelog](https://github.com/TooTallNate/plist.js/blob/master/History.md) - [Commits](https://github.com/TooTallNate/node-plist/commits) --- updated-dependencies: - dependency-name: plist dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * disable a failing test of countervalues (#1778) temporarily disable a countervalues api test. team informed. * re-enable countervalues test (#1779) * fix device actions used by bot and add new test case (#1784) * Fixes for XTZ JS (still under experimental, no impact) * Update Ethereum related libraries (#1780) * Update Ethereum related libraries * axios update * bitcoin bot to slack on ll-bitcoin-ci * v21.34.0-cosmos.0 * Cosmos JS (#1791) * be more conform between optimistic and final operation * use old code logic Co-authored-by: Alexandre Alouit * Cosmos JS (#1793) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression Co-authored-by: Alexandre Alouit * Cosmos JS (#1794) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression * fix duplicate data Co-authored-by: Alexandre Alouit * Cosmos JS (#1795) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression * fix duplicate data * fix duplicate data (again) Co-authored-by: Alexandre Alouit * Cosmos JS (#1796) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression * fix duplicate data * fix duplicate data (again) * append block height Co-authored-by: Alexandre Alouit * trigger bot * 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 * trigger bot * Fix setting of experimental JS currencies * Cosmos JS (#1799) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression * fix duplicate data * fix duplicate data (again) * append block height * fix specific empty amount case in reward transaction Co-authored-by: Alexandre Alouit * Cosmos JS (#1800) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression * fix duplicate data * fix duplicate data (again) * append block height * fix specific empty amount case in reward transaction * temporary debug operation broadcasted * use toOperationRaw method * return patchedOperation Co-authored-by: Alexandre Alouit * Cosmos JS (#1801) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression * fix duplicate data * fix duplicate data (again) * append block height * fix specific empty amount case in reward transaction * temporary debug operation broadcasted * use toOperationRaw method * return patchedOperation * restore broadcast operation * Update js-synchronisation.ts * remove block height support Co-authored-by: Alexandre Alouit * v21.34.1-cosmos.3 * log allOperationsMatchingId * lint * augment the nb of cosmos accounts * trigger bot * trigger bot * trigger bot * trigger bot * enable crypto_org to run tests * Cosmos JS (#1803) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression * fix duplicate data * fix duplicate data (again) * append block height * fix specific empty amount case in reward transaction * temporary debug operation broadcasted * use toOperationRaw method * return patchedOperation * restore broadcast operation * Update js-synchronisation.ts * remove block height support * Revert "remove block height support" This reverts commit eb7198f9ab1edef8a82a1edcccaefc9907e99868. * More strict type * reverse order of validators Co-authored-by: Alexandre Alouit * Cosmos JS (#1804) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression * fix duplicate data * fix duplicate data (again) * append block height * fix specific empty amount case in reward transaction * temporary debug operation broadcasted * use toOperationRaw method * return patchedOperation * restore broadcast operation * Update js-synchronisation.ts * remove block height support * Revert "remove block height support" This reverts commit eb7198f9ab1edef8a82a1edcccaefc9907e99868. * More strict type * reverse order of validators * fix validators type in createTransmission * add sequence to ops * strict output getAccount type * fix operations sequence Co-authored-by: Alexandre Alouit * Add a workaround that prevent 2 last operations to be same id (#1807) * Fixes deduplication logic of mergeOps * Revert "Add a workaround that prevent 2 last operations to be same id (#1807)" This reverts commit b0e55cd909829febbe43941c13ea5029b8a675fa. * trigger bot * Cosmos JS (#1809) * be more conform between optimistic and final operation * use old code logic * fix senders/recipients regression * fix duplicate data * fix duplicate data (again) * append block height * fix specific empty amount case in reward transaction * temporary debug operation broadcasted * use toOperationRaw method * return patchedOperation * restore broadcast operation * Update js-synchronisation.ts * remove block height support * Revert "remove block height support" This reverts commit eb7198f9ab1edef8a82a1edcccaefc9907e99868. * More strict type * reverse order of validators * fix validators type in createTransmission * add sequence to ops * strict output getAccount type * fix operations sequence * fix multiple tx messages Co-authored-by: Alexandre Alouit * snapshot * v21.34.1-cosmos.4 * cosmos bot: better approximation of delegating amounts * trigger bot * Cosmos bot: redelegation effects immediately when already delegate * trigger bot * trigger bot * trigger bot * trigger bot * increase maxAccount to have more coverage * simplify spec test * trigger bot * trigger bot * lint * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot * trigger bot Co-authored-by: Alexandre Alouit Co-authored-by: wa-aal <93649819+wa-aal@users.noreply.github.com> Co-authored-by: Gaëtan Renaudeau Co-authored-by: Emmanuel Co-authored-by: Olivier Freyssinet Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .ci.env | 4 +- .github/workflows/bot7-meredenis.yml | 81 ++ .github/workflows/bot7.yml | 82 ++ cli/scripts/tests.sh | 29 - cli/tests/encryption/test.sh | 42 - cli/tests/libcoreReset/test.sh | 10 - cli/tests/version/test.sh | 5 - package.json | 6 +- .../__snapshots__/all.libcore.ts.snap | 1057 +++++++++++++++++ src/account/support.ts | 2 +- src/env.ts | 2 +- src/families/bitcoin/bridge/js.ts | 1 + src/families/cosmos/api/Cosmos.ts | 287 +++++ src/families/cosmos/bridge/js.ts | 68 ++ src/families/cosmos/js-buildTransaction.ts | 233 ++++ src/families/cosmos/js-createTransaction.ts | 23 + .../cosmos/js-estimateMaxSpendable.ts | 33 + .../cosmos/js-getTransactionStatus.ts | 292 +++++ src/families/cosmos/js-prepareTransaction.ts | 131 ++ src/families/cosmos/js-signOperation.ts | 181 +++ src/families/cosmos/js-synchronisation.ts | 238 ++++ src/families/cosmos/js-updateTransaction.ts | 21 + .../cosmos/libcore-buildTransaction.ts | 5 +- src/families/cosmos/specs.ts | 204 ++-- src/families/cosmos/test-dataset.ts | 4 +- src/families/cosmos/validators.ts | 175 +-- src/families/crypto_org/specs.ts | 28 +- src/generated/bridge/js.ts | 3 + yarn.lock | 156 ++- 29 files changed, 3129 insertions(+), 274 deletions(-) create mode 100644 .github/workflows/bot7-meredenis.yml create mode 100644 .github/workflows/bot7.yml delete mode 100644 cli/tests/encryption/test.sh delete mode 100644 cli/tests/libcoreReset/test.sh delete mode 100644 cli/tests/version/test.sh create mode 100644 src/families/cosmos/api/Cosmos.ts create mode 100644 src/families/cosmos/bridge/js.ts create mode 100644 src/families/cosmos/js-buildTransaction.ts create mode 100644 src/families/cosmos/js-createTransaction.ts create mode 100644 src/families/cosmos/js-estimateMaxSpendable.ts create mode 100644 src/families/cosmos/js-getTransactionStatus.ts create mode 100644 src/families/cosmos/js-prepareTransaction.ts create mode 100644 src/families/cosmos/js-signOperation.ts create mode 100644 src/families/cosmos/js-synchronisation.ts create mode 100644 src/families/cosmos/js-updateTransaction.ts diff --git a/.ci.env b/.ci.env index 5dd7ddbb31..9ef5f24c3e 100644 --- a/.ci.env +++ b/.ci.env @@ -1,4 +1,2 @@ DISABLE_MOCKED_WARNING=true -# always test on latest conditions -EXPERIMENTAL_EXPLORERS=1 -EXPERIMENTAL_CURRENCIES_JS_BRIDGE=algorand,bitcoin,bitcoin_cash,bsc,litecoin,dash,qtum,zcash,bitcoin_gold,stratis,dogecoin,digibyte,komodo,pivx,zencash,vertcoin,peercoin,viacoin,stakenet,stealthcoin,decred,bitcoin_testnet,tezos +EXPERIMENTAL_CURRENCIES_JS_BRIDGE=algorand,bitcoin,bitcoin_cash,bsc,litecoin,dash,qtum,zcash,bitcoin_gold,stratis,dogecoin,digibyte,komodo,pivx,zencash,vertcoin,peercoin,viacoin,stakenet,stealthcoin,decred,bitcoin_testnet,tezos,cosmos diff --git a/.github/workflows/bot7-meredenis.yml b/.github/workflows/bot7-meredenis.yml new file mode 100644 index 0000000000..4c55290969 --- /dev/null +++ b/.github/workflows/bot7-meredenis.yml @@ -0,0 +1,81 @@ +name: Bot 'Cosmos JS via Mère Denis' +on: + push: + branches: + - cosmos-js + +jobs: + start-runner: + name: "start ec2 instance (Linux)" + if: ${{ always() }} + uses: ledgerhq/actions/.github/workflows/start-linux-runner.yml@main + secrets: + CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }} + + stop-runner: + name: "stop ec2 instance (Linux)" + needs: [start-runner, run-bot] + uses: ledgerhq/actions/.github/workflows/stop-linux-runner.yml@main + if: ${{ always() }} + with: + label: ${{ needs.start-runner.outputs.label }} + ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }} + secrets: + CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }} + + run-bot: + needs: [start-runner] + runs-on: ${{ needs.start-runner.outputs.label }} + steps: + - name: prepare runner + run: | + sudo growpart /dev/nvme0n1 1 + sudo resize2fs /dev/nvme0n1p1 + - uses: actions/checkout@v2 + - name: Retrieving coin apps + uses: actions/checkout@v2 + with: + repository: LedgerHQ/coin-apps + token: ${{ secrets.PAT }} + path: coin-apps + - uses: actions/setup-node@master + with: + node-version: 14.x + - name: install yarn + run: npm i -g yarn + - name: pull docker image + run: docker pull ghcr.io/ledgerhq/speculos + - name: kill apt-get + run: sudo killall -w apt-get apt || echo OK + - name: Install linux deps + run: sudo apt-get install -y libusb-1.0-0-dev jq + - name: Install dependencies + run: | + yarn global add yalc + yarn --frozen-lockfile + yarn ci-setup-cli + - name: BOT + env: + SHOW_LEGACY_NEW_ACCOUNT: "1" + SEED: ${{ secrets.SEED1 }} + VERBOSE_FILE: bot-tests.txt + GITHUB_SHA: ${GITHUB_SHA} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_RUN_ID: ${{ github.run_id }} + GITHUB_WORKFLOW: ${{ github.workflow }} + SLACK_API_TOKEN: ${{ secrets.SLACK_API_TOKEN }} + SLACK_CHANNEL: live-ft-atom-js + BOT_FILTER_FAMILY: cosmos + EXPERIMENTAL_CURRENCIES_JS_BRIDGE: cosmos + run: COINAPPS=$PWD/coin-apps yarn ci-test-bot + timeout-minutes: 120 + - name: Run coverage + if: failure() || success() + run: CODECOV_TOKEN=${{ secrets.CODECOV_TOKEN }} npx codecov + - name: upload logs + if: failure() || success() + uses: actions/upload-artifact@v1 + with: + name: bot-tests.txt + path: bot-tests.txt + diff --git a/.github/workflows/bot7.yml b/.github/workflows/bot7.yml new file mode 100644 index 0000000000..6889ab37e3 --- /dev/null +++ b/.github/workflows/bot7.yml @@ -0,0 +1,82 @@ +name: Bot 'Cosmos JS' +on: + push: + branches: + - cosmos-js + +jobs: + start-runner: + name: "start ec2 instance (Linux)" + if: ${{ always() }} + uses: ledgerhq/actions/.github/workflows/start-linux-runner.yml@main + secrets: + CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }} + + stop-runner: + name: "stop ec2 instance (Linux)" + needs: [start-runner, run-bot] + uses: ledgerhq/actions/.github/workflows/stop-linux-runner.yml@main + if: ${{ always() }} + with: + label: ${{ needs.start-runner.outputs.label }} + ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }} + secrets: + CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }} + + run-bot: + needs: [start-runner] + runs-on: ${{ needs.start-runner.outputs.label }} + steps: + - name: prepare runner + run: | + sudo growpart /dev/nvme0n1 1 + sudo resize2fs /dev/nvme0n1p1 + - uses: actions/checkout@v2 + - name: Retrieving coin apps + uses: actions/checkout@v2 + with: + repository: LedgerHQ/coin-apps + token: ${{ secrets.PAT }} + path: coin-apps + - uses: actions/setup-node@master + with: + node-version: 14.x + - name: install yarn + run: npm i -g yarn + - name: pull docker image + run: docker pull ghcr.io/ledgerhq/speculos + - name: kill apt-get + run: sudo killall -w apt-get apt || echo OK + - name: Install linux deps + run: sudo apt-get install -y libusb-1.0-0-dev jq + - name: Install dependencies + run: | + yarn global add yalc + yarn --frozen-lockfile + yarn ci-setup-cli + - name: BOT + env: + SHOW_LEGACY_NEW_ACCOUNT: "1" + DEBUG_HTTP_RESPONSE: "1" + SEED: ${{ secrets.SEED4 }} + VERBOSE_FILE: bot-tests.txt + GITHUB_SHA: ${GITHUB_SHA} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_RUN_ID: ${{ github.run_id }} + GITHUB_WORKFLOW: ${{ github.workflow }} + SLACK_API_TOKEN: ${{ secrets.SLACK_API_TOKEN }} + SLACK_CHANNEL: live-ft-atom-js + BOT_FILTER_FAMILY: cosmos + EXPERIMENTAL_CURRENCIES_JS_BRIDGE: cosmos + run: COINAPPS=$PWD/coin-apps yarn ci-test-bot + timeout-minutes: 120 + - name: Run coverage + if: failure() || success() + run: CODECOV_TOKEN=${{ secrets.CODECOV_TOKEN }} npx codecov + - name: upload logs + if: failure() || success() + uses: actions/upload-artifact@v1 + with: + name: bot-tests.txt + path: bot-tests.txt + diff --git a/cli/scripts/tests.sh b/cli/scripts/tests.sh index d4b42aae0d..d9628ae71d 100755 --- a/cli/scripts/tests.sh +++ b/cli/scripts/tests.sh @@ -1,32 +1,3 @@ #!/bin/bash set -e -cd $(dirname $0)/../tests - -MINIMAL_MODE=$MINIMAL # can be overriden -if [ "master" != "$CIRCLE_BRANCH" ]; then - MINIMAL_MODE=1 -fi - -MANDATORY_TESTS= -OPTIONAL_TESTS= -for td in *; do - if [ -f ./$td/test.sh ]; then - if [ -f ./$td/mandatory ]; then - MANDATORY_TESTS="$MANDATORY_TESTS $td" - else - OPTIONAL_TESTS="$OPTIONAL_TESTS $td" - fi - fi -done - -for td in $MANDATORY_TESTS; do - bash $PWD/../scripts/testOne.sh $td $1 -done - -if [ ! $MINIMAL_MODE ]; then - for td in $OPTIONAL_TESTS; do - bash $PWD/../scripts/testOne.sh $td $1 - done -fi - diff --git a/cli/tests/encryption/test.sh b/cli/tests/encryption/test.sh deleted file mode 100644 index c00ce0d3d1..0000000000 --- a/cli/tests/encryption/test.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -set -e - -ledger-live sync -c cosmos --id cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4 -f summary - -echo set password for first time -ledger-live libcoreSetPassword --password foobar -export LIBCORE_PASSWORD=foobar - -echo try a sync with the new password -ledger-live sync -c cosmos --id cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4 -f summary - -echo change password -ledger-live libcoreSetPassword --password foo -export LIBCORE_PASSWORD=foo - -echo try a sync with the changed password -ledger-live sync -c cosmos --id cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4 -f summary - -echo decrypt the libcore -ledger-live libcoreSetPassword --password "" -export LIBCORE_PASSWORD= - -echo try a sync after removing the password -ledger-live sync -c cosmos --id cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4 -f summary - -echo set again password and try to not use encryption -ledger-live libcoreSetPassword --password foo - -echo try a sync with a wrong password -set +e -LIBCORE_PASSWORD=mistake ledger-live sync -c cosmos --id cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4 -f summary -RES=$? -set -e -if [ $RES -eq 0 ]; then - echo expected failure with wrong password - exit 1 -fi - -echo check encrypted data can be descripted later -LIBCORE_PASSWORD=foo ledger-live sync -c cosmos --id cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4 -f summary \ No newline at end of file diff --git a/cli/tests/libcoreReset/test.sh b/cli/tests/libcoreReset/test.sh deleted file mode 100644 index 67d01b9ff3..0000000000 --- a/cli/tests/libcoreReset/test.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -ledger-live libcoreReset - -ledger-live sync -c cosmos --id cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4 -f summary - -ledger-live libcoreReset - -# TODO for now just checking it doesn't break. later we'll check it effectively clean things. \ No newline at end of file diff --git a/cli/tests/version/test.sh b/cli/tests/version/test.sh deleted file mode 100644 index 39024bf007..0000000000 --- a/cli/tests/version/test.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -set -e - -ledger-live version \ No newline at end of file diff --git a/package.json b/package.json index a8c78899d1..80e348392d 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "type": "git", "url": "https://github.com/LedgerHQ/ledger-live-common" }, - "version": "21.34.0", + "version": "21.34.1-cosmos.4", "main": "lib/index.js", "types": "lib/index.d.ts", "license": "Apache-2.0", @@ -45,6 +45,10 @@ "@celo/contractkit": "^1.5.2", "@celo/wallet-base": "^1.5.2", "@celo/wallet-ledger": "^1.5.2", + "@cosmjs/crypto": "^0.26.5", + "@cosmjs/ledger-amino": "^0.26.5", + "@cosmjs/proto-signing": "^0.26.5", + "@cosmjs/stargate": "^0.26.5", "@crypto-com/chain-jslib": "0.0.19", "@ethereumjs/common": "^2.6.2", "@ethereumjs/tx": "^3.5.0", diff --git a/src/__tests__/__snapshots__/all.libcore.ts.snap b/src/__tests__/__snapshots__/all.libcore.ts.snap index a676be54a9..1df4289a47 100644 --- a/src/__tests__/__snapshots__/all.libcore.ts.snap +++ b/src/__tests__/__snapshots__/all.libcore.ts.snap @@ -11368,6 +11368,1063 @@ Array [ ] `; +exports[`cosmos currency bridge scanAccounts cosmos seed 1 1`] = ` +Array [ + Object { + "balance": "2578910", + "cosmosResources": Object { + "delegatedBalance": "2149064", + "withdrawAddress": "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + }, + "currencyId": "cosmos", + "derivationMode": "", + "freshAddress": "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + "freshAddressPath": "44'/118'/0'/0/0", + "freshAddresses": Array [ + Object { + "address": "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + "derivationPath": "44'/118'/0'/0/0", + }, + ], + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "index": 0, + "name": "Cosmos 1", + "nfts": undefined, + "operationsCount": 20, + "pendingOperations": Array [], + "seedIdentifier": "0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a", + "starred": false, + "swapHistory": Array [], + "syncHash": undefined, + "unitMagnitude": 6, + "used": true, + "xpub": "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + }, + Object { + "balance": "0", + "cosmosResources": Object { + "delegatedBalance": "0", + "withdrawAddress": "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + }, + "currencyId": "cosmos", + "derivationMode": "", + "freshAddress": "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + "freshAddressPath": "44'/118'/1'/0/0", + "freshAddresses": Array [ + Object { + "address": "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + "derivationPath": "44'/118'/1'/0/0", + }, + ], + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "index": 1, + "name": "Cosmos 2", + "nfts": undefined, + "operationsCount": 12, + "pendingOperations": Array [], + "seedIdentifier": "0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a", + "starred": false, + "swapHistory": Array [], + "syncHash": undefined, + "unitMagnitude": 6, + "used": true, + "xpub": "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + }, + Object { + "balance": "327600", + "cosmosResources": Object { + "delegatedBalance": "326000", + "withdrawAddress": "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + }, + "currencyId": "cosmos", + "derivationMode": "", + "freshAddress": "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + "freshAddressPath": "44'/118'/2'/0/0", + "freshAddresses": Array [ + Object { + "address": "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + "derivationPath": "44'/118'/2'/0/0", + }, + ], + "id": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:", + "index": 2, + "name": "Cosmos 3", + "nfts": undefined, + "operationsCount": 6, + "pendingOperations": Array [], + "seedIdentifier": "0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a", + "starred": false, + "swapHistory": Array [], + "syncHash": undefined, + "unitMagnitude": 6, + "used": true, + "xpub": "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + }, + Object { + "balance": "0", + "cosmosResources": Object { + "delegatedBalance": "0", + "withdrawAddress": "cosmos14tg4vsd0q745fxhzn329pkx0krqtszcxyzl5ku", + }, + "currencyId": "cosmos", + "derivationMode": "", + "freshAddress": "cosmos14tg4vsd0q745fxhzn329pkx0krqtszcxyzl5ku", + "freshAddressPath": "44'/118'/3'/0/0", + "freshAddresses": Array [ + Object { + "address": "cosmos14tg4vsd0q745fxhzn329pkx0krqtszcxyzl5ku", + "derivationPath": "44'/118'/3'/0/0", + }, + ], + "id": "js:2:cosmos:cosmos14tg4vsd0q745fxhzn329pkx0krqtszcxyzl5ku:", + "index": 3, + "name": "Cosmos 4", + "nfts": undefined, + "operationsCount": 0, + "pendingOperations": Array [], + "seedIdentifier": "0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a", + "starred": false, + "swapHistory": Array [], + "syncHash": undefined, + "unitMagnitude": 6, + "used": false, + "xpub": "cosmos14tg4vsd0q745fxhzn329pkx0krqtszcxyzl5ku", + }, +] +`; + +exports[`cosmos currency bridge scanAccounts cosmos seed 1 2`] = ` +Array [ + Array [ + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "9309182", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "2144", + "hash": "1D8C46B1F1A8F17B30DECF2D8455DDE1EE2CBFBD884E708A38897A748C42F75F", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-1D8C46B1F1A8F17B30DECF2D8455DDE1EE2CBFBD884E708A38897A748C42F75F-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 70, + "type": "OUT", + "value": "3144", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "9449581", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "4286", + "hash": "1ECF17AE5234685DB2A4CE97DFEFE1B8D518B332B3E93035B2AE140FD0ECC1A2", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-1ECF17AE5234685DB2A4CE97DFEFE1B8D518B332B3E93035B2AE140FD0ECC1A2-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 73, + "type": "OUT", + "value": "5286", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "5421691", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5203", + "hash": "2473F171917C446D0B1B378BF6C059AA2A12B5D69B8346BA473F4E3CE749D7CC", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-2473F171917C446D0B1B378BF6C059AA2A12B5D69B8346BA473F4E3CE749D7CC-IN", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "5421734", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5203", + "hash": "296EBDCC53F569CA35F50782181DDD536610206781F6042171DFAC8D47FA368A", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-296EBDCC53F569CA35F50782181DDD536610206781F6042171DFAC8D47FA368A-IN", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "9439493", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "4286", + "hash": "3C30A3262525BE42B55DCEAF56DA332CA9E8B90497AEB7FBD56431FB6D41BAB9", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-3C30A3262525BE42B55DCEAF56DA332CA9E8B90497AEB7FBD56431FB6D41BAB9-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 71, + "type": "OUT", + "value": "5286", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "9610331", + "contract": undefined, + "extra": Object { + "validators": Array [ + Object { + "address": "cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys", + "amount": "239", + }, + ], + }, + "fee": "7392", + "hash": "5832ECAD80A96016717F7A1B6EC72D08C876D9C05AA2597698E0CDF5545D7D9C", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-5832ECAD80A96016717F7A1B6EC72D08C876D9C05AA2597698E0CDF5545D7D9C-REWARD", + "operator": undefined, + "recipients": Array [], + "senders": Array [], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 75, + "type": "REWARD", + "value": "7392", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "5610846", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5193", + "hash": "689F1D9FB0064A27A0B94C14F8106BF12292889103EAA8B425E04D6E54893710", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-689F1D9FB0064A27A0B94C14F8106BF12292889103EAA8B425E04D6E54893710-IN", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "IN", + "value": "478768", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "8778816", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5178", + "hash": "79A2B43CF788D3D7C917A1453F650CD587646D5BC71DAED39930288CD08423FC", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-79A2B43CF788D3D7C917A1453F650CD587646D5BC71DAED39930288CD08423FC-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 66, + "type": "OUT", + "value": "6178", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "9692513", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "3077", + "hash": "82C002E20CD52761C43126FCED13C6934E390508DB89605784CEE7CF5E625297", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-82C002E20CD52761C43126FCED13C6934E390508DB89605784CEE7CF5E625297-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 76, + "type": "OUT", + "value": "13077", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "9449621", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "4286", + "hash": "84648F58558E45F745576F1523EAE4605916D4EE146F3B5C5282FFA6CF84FD32", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-84648F58558E45F745576F1523EAE4605916D4EE146F3B5C5282FFA6CF84FD32-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 74, + "type": "OUT", + "value": "5286", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "5297866", + "contract": undefined, + "extra": Object { + "validators": Array [ + Object { + "address": "cosmosvaloper1crqm3598z6qmyn2kkcl9dz7uqs4qdqnr6s8jdn", + "amount": "15935", + }, + ], + }, + "fee": "9838", + "hash": "88AF1F6010CE8C8BBBF4247B8AB723C469022F7329CAB906CB783C3377ECB005", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-88AF1F6010CE8C8BBBF4247B8AB723C469022F7329CAB906CB783C3377ECB005-REWARD", + "operator": undefined, + "recipients": Array [], + "senders": Array [], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "REWARD", + "value": "9838", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "8778735", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5178", + "hash": "901F2DC426B53945E3AF4C75FEE78745DD094297AD60BA3E6D5CB50AE4D09DAE", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-901F2DC426B53945E3AF4C75FEE78745DD094297AD60BA3E6D5CB50AE4D09DAE-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 65, + "type": "OUT", + "value": "6178", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "5421368", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5174", + "hash": "913BFAF078A8633A52CC18B854FCDFC5569AC122C5F4E523981C307A68200D99", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-913BFAF078A8633A52CC18B854FCDFC5569AC122C5F4E523981C307A68200D99-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "OUT", + "value": "10174", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "8861730", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5178", + "hash": "9840D58AE49A79D1154013B7C547D91292390577A5C8A767C470733AA14AF660", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-9840D58AE49A79D1154013B7C547D91292390577A5C8A767C470733AA14AF660-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 68, + "type": "OUT", + "value": "6178", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "9447888", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "3062", + "hash": "AACE7EF7486BB84FD9CE77FF8ADAEBEFBD6C632FBE8B026652E4D71E48C68B2F", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-AACE7EF7486BB84FD9CE77FF8ADAEBEFBD6C632FBE8B026652E4D71E48C68B2F-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 72, + "type": "OUT", + "value": "4062", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "8765958", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5178", + "hash": "B97F8ED1CB8F7E7FFAA150A5D12A7DA9D115C8112D37A434D4EF12E41CDCB793", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-B97F8ED1CB8F7E7FFAA150A5D12A7DA9D115C8112D37A434D4EF12E41CDCB793-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 64, + "type": "OUT", + "value": "6178", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "5421671", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5565", + "hash": "C1B14B063266C3F735A34EEF5687E617A0F5DB5988EAFC33264C6C38F65AE20D", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-C1B14B063266C3F735A34EEF5687E617A0F5DB5988EAFC33264C6C38F65AE20D-IN", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "8946424", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5181", + "hash": "C597CEC7FA4F41EFB55648134B65488A502C7C9B538757E4DE104E5BC14BA505", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-C597CEC7FA4F41EFB55648134B65488A502C7C9B538757E4DE104E5BC14BA505-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 69, + "type": "OUT", + "value": "6181", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "8779134", + "contract": undefined, + "extra": Object { + "validators": Array [ + Object { + "address": "cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys", + "amount": "10000", + }, + ], + }, + "fee": "11254", + "hash": "CEE5FA6A5B678E9E0756547A2EDA491A05F6528CBEBB01BAC7CE8CC131E1184C", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-CEE5FA6A5B678E9E0756547A2EDA491A05F6528CBEBB01BAC7CE8CC131E1184C-DELEGATE", + "operator": undefined, + "recipients": Array [], + "senders": Array [], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 67, + "type": "DELEGATE", + "value": "11254", + }, + Object { + "accountId": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:", + "blockHash": null, + "blockHeight": "9666237", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "6250", + "hash": "E01DA1E9FAD08AA2F7B27D56D2AD894305E5C308421A9C52B9B436574B00A1FB", + "id": "js:2:cosmos:cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl:-E01DA1E9FAD08AA2F7B27D56D2AD894305E5C308421A9C52B9B436574B00A1FB-IN", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 1, + "type": "IN", + "value": "8800", + }, + ], + Array [ + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "9309182", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "2144", + "hash": "1D8C46B1F1A8F17B30DECF2D8455DDE1EE2CBFBD884E708A38897A748C42F75F", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-1D8C46B1F1A8F17B30DECF2D8455DDE1EE2CBFBD884E708A38897A748C42F75F-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 70, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "9449581", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "4286", + "hash": "1ECF17AE5234685DB2A4CE97DFEFE1B8D518B332B3E93035B2AE140FD0ECC1A2", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-1ECF17AE5234685DB2A4CE97DFEFE1B8D518B332B3E93035B2AE140FD0ECC1A2-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 73, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "9439493", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "4286", + "hash": "3C30A3262525BE42B55DCEAF56DA332CA9E8B90497AEB7FBD56431FB6D41BAB9", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-3C30A3262525BE42B55DCEAF56DA332CA9E8B90497AEB7FBD56431FB6D41BAB9-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 71, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "8778816", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5178", + "hash": "79A2B43CF788D3D7C917A1453F650CD587646D5BC71DAED39930288CD08423FC", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-79A2B43CF788D3D7C917A1453F650CD587646D5BC71DAED39930288CD08423FC-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 66, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "9449621", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "4286", + "hash": "84648F58558E45F745576F1523EAE4605916D4EE146F3B5C5282FFA6CF84FD32", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-84648F58558E45F745576F1523EAE4605916D4EE146F3B5C5282FFA6CF84FD32-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 74, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "8778735", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5178", + "hash": "901F2DC426B53945E3AF4C75FEE78745DD094297AD60BA3E6D5CB50AE4D09DAE", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-901F2DC426B53945E3AF4C75FEE78745DD094297AD60BA3E6D5CB50AE4D09DAE-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 65, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "5421368", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5174", + "hash": "913BFAF078A8633A52CC18B854FCDFC5569AC122C5F4E523981C307A68200D99", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-913BFAF078A8633A52CC18B854FCDFC5569AC122C5F4E523981C307A68200D99-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "IN", + "value": "5000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "8861730", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5178", + "hash": "9840D58AE49A79D1154013B7C547D91292390577A5C8A767C470733AA14AF660", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-9840D58AE49A79D1154013B7C547D91292390577A5C8A767C470733AA14AF660-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 68, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "9447888", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "3062", + "hash": "AACE7EF7486BB84FD9CE77FF8ADAEBEFBD6C632FBE8B026652E4D71E48C68B2F", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-AACE7EF7486BB84FD9CE77FF8ADAEBEFBD6C632FBE8B026652E4D71E48C68B2F-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 72, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "8765958", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5178", + "hash": "B97F8ED1CB8F7E7FFAA150A5D12A7DA9D115C8112D37A434D4EF12E41CDCB793", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-B97F8ED1CB8F7E7FFAA150A5D12A7DA9D115C8112D37A434D4EF12E41CDCB793-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 64, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "8946424", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5181", + "hash": "C597CEC7FA4F41EFB55648134B65488A502C7C9B538757E4DE104E5BC14BA505", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-C597CEC7FA4F41EFB55648134B65488A502C7C9B538757E4DE104E5BC14BA505-IN", + "operator": undefined, + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 69, + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:", + "blockHash": null, + "blockHeight": "9666237", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "6250", + "hash": "E01DA1E9FAD08AA2F7B27D56D2AD894305E5C308421A9C52B9B436574B00A1FB", + "id": "js:2:cosmos:cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5:-E01DA1E9FAD08AA2F7B27D56D2AD894305E5C308421A9C52B9B436574B00A1FB-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 1, + "type": "OUT", + "value": "15050", + }, + ], + Array [ + Object { + "accountId": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:", + "blockHash": null, + "blockHeight": "5421691", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5203", + "hash": "2473F171917C446D0B1B378BF6C059AA2A12B5D69B8346BA473F4E3CE749D7CC", + "id": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:-2473F171917C446D0B1B378BF6C059AA2A12B5D69B8346BA473F4E3CE749D7CC-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "OUT", + "value": "6203", + }, + Object { + "accountId": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:", + "blockHash": null, + "blockHeight": "5421734", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5203", + "hash": "296EBDCC53F569CA35F50782181DDD536610206781F6042171DFAC8D47FA368A", + "id": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:-296EBDCC53F569CA35F50782181DDD536610206781F6042171DFAC8D47FA368A-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "OUT", + "value": "6203", + }, + Object { + "accountId": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:", + "blockHash": null, + "blockHeight": "5610846", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5193", + "hash": "689F1D9FB0064A27A0B94C14F8106BF12292889103EAA8B425E04D6E54893710", + "id": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:-689F1D9FB0064A27A0B94C14F8106BF12292889103EAA8B425E04D6E54893710-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "OUT", + "value": "483961", + }, + Object { + "accountId": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:", + "blockHash": null, + "blockHeight": "9692513", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "3077", + "hash": "82C002E20CD52761C43126FCED13C6934E390508DB89605784CEE7CF5E625297", + "id": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:-82C002E20CD52761C43126FCED13C6934E390508DB89605784CEE7CF5E625297-IN", + "operator": undefined, + "recipients": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 76, + "type": "IN", + "value": "10000", + }, + Object { + "accountId": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:", + "blockHash": null, + "blockHeight": "5421671", + "contract": undefined, + "extra": Object { + "validators": Array [], + }, + "fee": "5565", + "hash": "C1B14B063266C3F735A34EEF5687E617A0F5DB5988EAFC33264C6C38F65AE20D", + "id": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:-C1B14B063266C3F735A34EEF5687E617A0F5DB5988EAFC33264C6C38F65AE20D-OUT", + "operator": undefined, + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + ], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 0, + "type": "OUT", + "value": "6565", + }, + Object { + "accountId": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:", + "blockHash": null, + "blockHeight": "9692529", + "contract": undefined, + "extra": Object { + "validators": Array [ + Object { + "address": "cosmosvaloper17zcpywlhgcpk7ff505vr8mnc4wwpv5fcta6enz", + "amount": "", + }, + Object { + "address": "cosmosvaloper17zcpywlhgcpk7ff505vr8mnc4wwpv5fcta6enz", + "amount": "1", + }, + ], + }, + "fee": "8399", + "hash": "DF458FE6A82C310837D7A33735FA5298BCF71B0BFF7A4134641AAE30F6F10501", + "id": "js:2:cosmos:cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw:-DF458FE6A82C310837D7A33735FA5298BCF71B0BFF7A4134641AAE30F6F10501-DELEGATE", + "operator": undefined, + "recipients": Array [], + "senders": Array [], + "standard": undefined, + "tokenId": undefined, + "transactionSequenceNumber": 25, + "type": "DELEGATE", + "value": "8399", + }, + ], + Array [], +] +`; + exports[`dash currency bridge scanAccounts dash seed 1 1`] = ` Array [ Object { diff --git a/src/account/support.ts b/src/account/support.ts index e9cea50790..da83a94a65 100644 --- a/src/account/support.ts +++ b/src/account/support.ts @@ -20,7 +20,7 @@ import { getMainAccount } from "../account"; import { getAccountBridge } from "../bridge"; import jsBridges from "../generated/bridge/js"; -const experimentalIntegrations = ["tezos"]; +const experimentalIntegrations = ["tezos", "cosmos"]; export function shouldUseJS(currency: CryptoCurrency) { const jsBridge = jsBridges[currency.family]; diff --git a/src/env.ts b/src/env.ts index 29f1e051d2..6053ff4217 100644 --- a/src/env.ts +++ b/src/env.ts @@ -156,7 +156,7 @@ const envDefinitions = { desc: "location of the compound API", }, COSMOS_GAS_AMPLIFIER: { - def: 4, + def: 1.4, parser: intParser, desc: "estimate gas multiplier", }, diff --git a/src/families/bitcoin/bridge/js.ts b/src/families/bitcoin/bridge/js.ts index f20217437a..0a6f88ddaf 100644 --- a/src/families/bitcoin/bridge/js.ts +++ b/src/families/bitcoin/bridge/js.ts @@ -11,6 +11,7 @@ import { calculateFees } from "./../cache"; import { perCoinLogic } from "../logic"; import { makeAccountBridgeReceive } from "../../../bridge/jsHelpers"; import * as explorerConfigAPI from "../../../api/explorerConfig"; + const receive = makeAccountBridgeReceive({ injectGetAddressParams: (account) => { const perCoin = perCoinLogic[account.currency.id]; diff --git a/src/families/cosmos/api/Cosmos.ts b/src/families/cosmos/api/Cosmos.ts new file mode 100644 index 0000000000..19f0d878b9 --- /dev/null +++ b/src/families/cosmos/api/Cosmos.ts @@ -0,0 +1,287 @@ +import { getEnv } from "../../../env"; +import BigNumber from "bignumber.js"; +import network from "../../../network"; +import { Operation } from "../../../types"; +import { patchOperationWithHash } from "../../../operation"; + +const defaultEndpoint = getEnv( + "API_COSMOS_BLOCKCHAIN_EXPLORER_API_ENDPOINT" +).replace(/\/$/, ""); + +export const getAccountInfo = async (address: string): Promise => { + 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<{ address: string; accountNumber: number; sequence: number }> => { + const response = { + address: address, + accountNumber: 0, + sequence: 0, + }; + + try { + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/auth/v1beta1/accounts/${address}`, + }); + + if (data.account.address) { + response.address = data.account.address; + } + + if (data.account.account_number) { + response.accountNumber = parseInt(data.account.account_number); + } + + if (data.account.sequence) { + response.sequence = parseInt(data.account.sequence); + } + // eslint-disable-next-line no-empty + } catch (e) {} + return response; +}; + +export const getChainId = async (): Promise => { + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/node_info`, + }); + + return data.node_info.network; +}; + +const getHeight = async (): Promise => { + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/base/tendermint/v1beta1/blocks/latest`, + }); + + return data.block.header.height; +}; + +const getAllBalances = async (address: string): Promise => { + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/bank/v1beta1/balances/${address}`, + }); + + let amount = new BigNumber(0); + + 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}`, + }); + + status = statusMap[data2.validator.status] || "unbonded"; + + delegations.push({ + validatorAddress: d.delegation.validator_address, + amount: new BigNumber(d.balance.amount), + pendingRewards: new BigNumber(0), + status, + }); + } + + const { data: data3 } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/distribution/v1beta1/delegators/${address}/rewards`, + }); + + 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; +}; + +const getRedelegations = async (address: string): Promise => { + const redelegations: Array = []; + + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/staking/v1beta1/delegators/${address}/redelegations`, + }); + + for (const r of data.redelegation_responses) { + for (const entry of r.entries) { + redelegations.push({ + 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; +}; + +const getUnbondings = async (address: string): Promise => { + const unbondings: Array = []; + + const { data } = await network({ + method: "GET", + url: `${defaultEndpoint}/cosmos/staking/v1beta1/delegators/${address}/unbonding_delegations`, + }); + + for (const u of data.unbonding_responses) { + for (const entry of u.entries) { + unbondings.push({ + validatorAddress: u.validator_address, + amount: new BigNumber(entry.initial_balance), + completionDate: new Date(entry.completion_time), + }); + } + } + + return unbondings; +}; + +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; +}; + +const getTransactions = async (address: string): Promise => { + const receive = await network({ + method: "GET", + url: + `${defaultEndpoint}/cosmos/tx/v1beta1/txs?events=` + + encodeURI(`transfer.recipient='${address}'`), + }); + + const send = await network({ + method: "GET", + url: + `${defaultEndpoint}/cosmos/tx/v1beta1/txs?events=` + + encodeURI(`message.sender='${address}'`), + }); + + return [...receive.data.tx_responses, ...send.data.tx_responses]; +}; + +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 simulate = async (tx_bytes: Array): Promise => { + try { + const { data } = await network({ + method: "POST", + url: `${defaultEndpoint}/cosmos/tx/v1beta1/simulate`, + data: { + tx_bytes: tx_bytes, + }, + }); + + return new BigNumber(data?.gas_info?.gas_used || 0); + } catch (e) { + return new BigNumber(0); + } +}; + +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, data.tx_response.txhash); +}; diff --git a/src/families/cosmos/bridge/js.ts b/src/families/cosmos/bridge/js.ts new file mode 100644 index 0000000000..022199942f --- /dev/null +++ b/src/families/cosmos/bridge/js.ts @@ -0,0 +1,68 @@ +import createTransaction from "../js-createTransaction"; +import estimateMaxSpendable from "../js-estimateMaxSpendable"; +import getTransactionStatus from "../js-getTransactionStatus"; +import prepareTransaction from "../js-prepareTransaction"; +import signOperation from "../js-signOperation"; +import { sync, getAccountShape } from "../js-synchronisation"; +import updateTransaction from "../js-updateTransaction"; +import { AccountBridge, CurrencyBridge, CryptoCurrency } from "../../../types"; +import type { CosmosValidatorItem, Transaction } from "../types"; +import { getValidators, hydrateValidators } from "../validators"; +import { + makeAccountBridgeReceive, + makeScanAccounts, +} from "../../../bridge/jsHelpers"; +import { broadcast } from "../api/Cosmos"; +import { + asSafeCosmosPreloadData, + setCosmosPreloadData, +} from "../preloadedData"; + +const receive = makeAccountBridgeReceive(); + +const getPreloadStrategy = (_currency) => ({ + preloadMaxAge: 30 * 1000, +}); + +const currencyBridge: CurrencyBridge = { + getPreloadStrategy, + preload: async (currency: CryptoCurrency) => { + const validators = await getValidators(currency); + setCosmosPreloadData({ + validators, + }); + return Promise.resolve({ + validators, + }); + }, + hydrate: (data: { validators?: CosmosValidatorItem[] }) => { + if (!data || typeof data !== "object") return; + const { validators } = data; + if ( + !validators || + typeof validators !== "object" || + !Array.isArray(validators) + ) + return; + hydrateValidators(validators); + setCosmosPreloadData(asSafeCosmosPreloadData(data)); + }, + scanAccounts: makeScanAccounts(getAccountShape), +}; + +const accountBridge: AccountBridge = { + createTransaction, + updateTransaction, + prepareTransaction, + estimateMaxSpendable, + getTransactionStatus, + sync, + receive, + signOperation, + broadcast, +}; + +export default { + currencyBridge, + accountBridge, +}; diff --git a/src/families/cosmos/js-buildTransaction.ts b/src/families/cosmos/js-buildTransaction.ts new file mode 100644 index 0000000000..66b932f1ad --- /dev/null +++ b/src/families/cosmos/js-buildTransaction.ts @@ -0,0 +1,233 @@ +import { Account } from "../../types"; +import { Transaction } from "./types"; +import { + makeAuthInfoBytes, + Registry, + TxBodyEncodeObject, +} from "@cosmjs/proto-signing"; +import { + MsgDelegate, + MsgUndelegate, + MsgBeginRedelegate, +} from "cosmjs-types/cosmos/staking/v1beta1/tx"; +import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx"; +import { SignMode } from "cosmjs-types/cosmos/tx/signing/v1beta1/signing"; +import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; +import { getAccount } from "./api/Cosmos"; +import BigNumber from "bignumber.js"; + +export const buildTransaction = async ( + account: Account, + transaction: Transaction +): Promise => { + const msg: Array<{ typeUrl: string; value: any }> = []; + + // Ledger Live is able to build transaction atomically, + // Take care expected data are complete before push msg. + // Otherwise, the transaction is silently returned intact. + + let isComplete = true; + + switch (transaction.mode) { + case "send": + if (!transaction.recipient || transaction.amount.lte(0)) { + isComplete = false; + } else { + msg.push({ + typeUrl: "/cosmos.bank.v1beta1.MsgSend", + value: { + fromAddress: account.freshAddress, + toAddress: transaction.recipient, + amount: [ + { + denom: account.currency.units[1].code, + amount: transaction.amount.toString(), + }, + ], + }, + }); + } + break; + + case "delegate": + if (!transaction.validators || transaction.validators.length < 1) { + isComplete = false; + } else { + transaction.validators.forEach((validator) => { + if (!validator.address || validator.amount.lte(0)) { + isComplete = false; + } + + msg.push({ + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: { + delegatorAddress: account.freshAddress, + validatorAddress: validator.address, + amount: { + denom: account.currency.units[1].code, + amount: validator.amount.toString(), + }, + }, + }); + }); + } + break; + + case "undelegate": + if ( + !transaction.validators || + transaction.validators.length < 1 || + !transaction.validators[0].address || + transaction.validators[0].amount.lte(0) + ) { + isComplete = false; + } else { + msg.push({ + typeUrl: "/cosmos.staking.v1beta1.MsgUndelegate", + value: { + delegatorAddress: account.freshAddress, + validatorAddress: transaction.validators[0].address, + amount: { + denom: account.currency.units[1].code, + amount: transaction.validators[0].amount.toString(), + }, + }, + }); + } + break; + + case "redelegate": + if ( + !transaction.cosmosSourceValidator || + !transaction.validators || + transaction.validators.length < 1 || + !transaction.validators[0].address || + transaction.validators[0].amount.lte(0) + ) { + isComplete = false; + } else { + msg.push({ + typeUrl: "/cosmos.staking.v1beta1.MsgBeginRedelegate", + value: { + validatorSrcAddress: transaction.cosmosSourceValidator, + delegatorAddress: account.freshAddress, + validatorDstAddress: transaction.validators[0].address, + amount: { + denom: account.currency.units[1].code, + amount: transaction.validators[0].amount.toString(), + }, + }, + }); + } + break; + + case "claimReward": + if ( + !transaction.validators || + transaction.validators.length < 1 || + !transaction.validators[0].address + ) { + isComplete = false; + } else { + msg.push({ + typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + value: { + delegatorAddress: account.freshAddress, + validatorAddress: transaction.validators[0].address, + }, + }); + } + break; + + case "claimRewardCompound": + if ( + !transaction.validators || + transaction.validators.length < 1 || + !transaction.validators[0].address || + transaction.validators[0].amount.lte(0) + ) { + isComplete = false; + } else { + msg.push({ + typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + value: { + delegatorAddress: account.freshAddress, + validatorAddress: transaction.validators[0].address, + }, + }); + + msg.push({ + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: { + delegatorAddress: account.freshAddress, + validatorAddress: transaction.validators[0].address, + amount: { + denom: account.currency.units[1].code, + amount: transaction.validators[0].amount.toString(), + }, + }, + }); + } + break; + } + + if (!isComplete) { + return []; + } + + return msg; +}; + +export const postBuildTransaction = async ( + account: Account, + transaction: Transaction, + pubkey: any, + unsignedPayload: any, + signature: Uint8Array +): Promise => { + const txBodyFields: TxBodyEncodeObject = { + typeUrl: "/cosmos.tx.v1beta1.TxBody", + value: { + messages: unsignedPayload, + memo: transaction.memo || "", + }, + }; + + const registry = new Registry([ + ["/cosmos.staking.v1beta1.MsgDelegate", MsgDelegate], + ["/cosmos.staking.v1beta1.MsgUndelegate", MsgUndelegate], + ["/cosmos.staking.v1beta1.MsgBeginRedelegate", MsgBeginRedelegate], + [ + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + MsgWithdrawDelegatorReward, + ], + ]); + + const { sequence } = await getAccount(account.freshAddress); + + const txBodyBytes = registry.encode(txBodyFields); + + const authInfoBytes = makeAuthInfoBytes( + [{ pubkey, sequence }], + [ + { + amount: transaction.fees?.toString() || new BigNumber(2500).toString(), + denom: account.currency.units[1].code, + }, + ], + transaction.gas?.toNumber() || new BigNumber(250000).toNumber(), + SignMode.SIGN_MODE_LEGACY_AMINO_JSON + ); + + const txRaw = TxRaw.fromPartial({ + bodyBytes: txBodyBytes, + authInfoBytes, + signatures: [signature], + }); + + const tx_bytes = Array.from(Uint8Array.from(TxRaw.encode(txRaw).finish())); + + return tx_bytes; +}; + +export default buildTransaction; diff --git a/src/families/cosmos/js-createTransaction.ts b/src/families/cosmos/js-createTransaction.ts new file mode 100644 index 0000000000..1027809f3a --- /dev/null +++ b/src/families/cosmos/js-createTransaction.ts @@ -0,0 +1,23 @@ +import { BigNumber } from "bignumber.js"; +import type { CosmosDelegationInfo, Transaction } from "./types"; + +/** + * Create an empty transaction + * + * @returns {Transaction} + */ +const createTransaction = (): Transaction => ({ + family: "cosmos", + mode: "send", + amount: new BigNumber(0), + fees: null, + gas: null, + recipient: "", + useAllAmount: false, + networkInfo: null, + memo: null, + cosmosSourceValidator: null, + validators: [] as CosmosDelegationInfo[], +}); + +export default createTransaction; diff --git a/src/families/cosmos/js-estimateMaxSpendable.ts b/src/families/cosmos/js-estimateMaxSpendable.ts new file mode 100644 index 0000000000..687f1057f6 --- /dev/null +++ b/src/families/cosmos/js-estimateMaxSpendable.ts @@ -0,0 +1,33 @@ +import { getAbandonSeedAddress } from "@ledgerhq/cryptoassets"; +import { BigNumber } from "bignumber.js"; +import { getMainAccount } from "../../account"; +import type { Account, AccountLike } from "../../types"; +import createTransaction from "./js-createTransaction"; +import getTransactionStatus from "./js-getTransactionStatus"; +import prepareTransaction from "./js-prepareTransaction"; +import type { Transaction } from "./types"; + +const estimateMaxSpendable = async ({ + account, + parentAccount, + transaction, +}: { + account: AccountLike; + parentAccount: Account; + transaction: Transaction; +}): Promise => { + const mainAccount = getMainAccount(account, parentAccount); + + const t = await prepareTransaction(mainAccount, { + ...createTransaction(), + ...transaction, + recipient: + transaction?.recipient || getAbandonSeedAddress(mainAccount.currency.id), + useAllAmount: true, + }); + + const s = await getTransactionStatus(mainAccount, t); + return s.amount; +}; + +export default estimateMaxSpendable; diff --git a/src/families/cosmos/js-getTransactionStatus.ts b/src/families/cosmos/js-getTransactionStatus.ts new file mode 100644 index 0000000000..5acce2e087 --- /dev/null +++ b/src/families/cosmos/js-getTransactionStatus.ts @@ -0,0 +1,292 @@ +import { + AmountRequired, + FeeNotLoaded, + InvalidAddress, + InvalidAddressBecauseDestinationIsAlsoSource, + NotEnoughBalance, + RecipientRequired, + RecommendUndelegation, +} from "@ledgerhq/errors"; +import { + ClaimRewardsFeesWarning, + CosmosDelegateAllFundsWarning, + CosmosRedelegationInProgress, + CosmosTooManyValidators, + NotEnoughDelegationBalance, +} from "../../errors"; +import { Account } from "../../types"; +import { StatusErrorMap, Transaction, TransactionStatus } from "./types"; +import { BigNumber } from "bignumber.js"; +import { + COSMOS_MAX_DELEGATIONS, + COSMOS_MAX_REDELEGATIONS, + COSMOS_MAX_UNBONDINGS, + getMaxEstimatedBalance, +} from "./logic"; +import invariant from "invariant"; +import { isValidRecipent } from "./api/Cosmos"; + +export const getTransactionStatus = async ( + a: Account, + t: Transaction +): Promise => { + if (t.mode === "send") { + // We isolate the send transaction that it's a little bit different from the rest + return await getSendTransactionStatus(a, t); + } else if (t.mode === "delegate") { + return await getDelegateTransactionStatus(a, t); + } + + const errors: StatusErrorMap = {}; + const warnings: StatusErrorMap = {}; + // here we only treat about all other mode than delegate and send + if ( + t.validators.some( + (v) => !v.address || !v.address.includes("cosmosvaloper") + ) || + t.validators.length === 0 + ) + errors.recipient = new InvalidAddress(undefined, { + currencyName: a.currency.name, + }); + + if (t.mode === "redelegate") { + const redelegationError = redelegationStatusError(a, t); + + if (redelegationError) { + // Note : note sure if I have to put this error on this field + errors.redelegation = redelegationError; + } + } else if (t.mode === "undelegate") { + invariant( + a.cosmosResources && + a.cosmosResources.unbondings.length < COSMOS_MAX_UNBONDINGS, + "unbondings should not have more than 6 entries" + ); + if (t.validators.length === 0) + errors.recipient = new InvalidAddress(undefined, { + currencyName: a.currency.name, + }); + const [first] = t.validators; + const unbondingError = first && isDelegable(a, first.address, first.amount); + + if (unbondingError) { + errors.unbonding = unbondingError; + } + } + + const validatorAmount = t.validators.reduce( + (old, current) => old.plus(current.amount), + new BigNumber(0) + ); + + if (t.mode !== "claimReward" && validatorAmount.lte(0)) { + errors.amount = new AmountRequired(); + } + + const estimatedFees = t.fees || new BigNumber(0); + + if (!t.fees) { + errors.fees = new FeeNotLoaded(); + } + + let totalSpent = estimatedFees; + + if (["claimReward", "claimRewardCompound"].includes(t.mode)) { + const { cosmosResources } = a; + invariant(cosmosResources, "cosmosResources should exist"); + const claimReward = + t.validators.length && cosmosResources + ? cosmosResources.delegations.find( + (delegation) => + delegation.validatorAddress === t.validators[0].address + ) + : null; + + if (claimReward && estimatedFees.gt(claimReward.pendingRewards)) { + warnings.claimReward = new ClaimRewardsFeesWarning(); + } + } + + if ( + !errors.recipient && + !errors.amount && + (validatorAmount.lt(0) || totalSpent.gt(a.spendableBalance)) + ) { + errors.amount = new NotEnoughBalance(); + totalSpent = new BigNumber(0); + } + + return Promise.resolve({ + errors, + warnings, + estimatedFees, + amount: new BigNumber(0), + totalSpent, + }); +}; + +const getDelegateTransactionStatus = async ( + a: Account, + t: Transaction +): Promise => { + const errors: StatusErrorMap = {}; + const warnings: StatusErrorMap = {}; + if ( + t.validators.some( + (v) => !v.address || !v.address.includes("cosmosvaloper") + ) || + t.validators.length === 0 + ) + errors.recipient = new InvalidAddress(undefined, { + currencyName: a.currency.name, + }); + + if (t.validators.length > COSMOS_MAX_DELEGATIONS) { + errors.validators = new CosmosTooManyValidators(); + } + + let amount = t.validators.reduce( + (old, current) => old.plus(current.amount), + new BigNumber(0) + ); + + if (amount.eq(0)) { + errors.amount = new AmountRequired(); + } + + const estimatedFees = t.fees || new BigNumber(0); + + if (!t.fees) { + errors.fees = new FeeNotLoaded(); + } + + let totalSpent = amount.plus(estimatedFees); + + if (totalSpent.eq(a.spendableBalance)) { + warnings.delegate = new CosmosDelegateAllFundsWarning(); + } + + if ( + !errors.recipient && + !errors.amount && + (amount.lt(0) || totalSpent.gt(a.spendableBalance)) + ) { + errors.amount = new NotEnoughBalance(); + amount = new BigNumber(0); + totalSpent = new BigNumber(0); + } + + return Promise.resolve({ + errors, + warnings, + estimatedFees, + amount, + totalSpent, + }); +}; + +const getSendTransactionStatus = async ( + a: Account, + t: Transaction +): Promise => { + const errors: StatusErrorMap = {}; + const warnings: StatusErrorMap = {}; + + if (!t.recipient) { + errors.recipient = new RecipientRequired(""); + } else if (a.freshAddress === t.recipient) { + errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource(); + } else { + if (!(await isValidRecipent(t.recipient))) { + errors.recipient = new InvalidAddress(undefined, { + currencyName: a.currency.name, + }); + } + } + + let amount = t.amount; + + if (amount.lte(0) && !t.useAllAmount) { + errors.amount = new AmountRequired(); + } + + const estimatedFees = t.fees || new BigNumber(0); + + if (!t.fees || !t.fees.gt(0)) { + errors.fees = new FeeNotLoaded(); + } + + amount = t.useAllAmount ? getMaxEstimatedBalance(a, estimatedFees) : amount; + const totalSpent = amount.plus(estimatedFees); + + if ( + (amount.lte(0) && t.useAllAmount) || // if use all Amount sets an amount at 0 + (!errors.recipient && !errors.amount && totalSpent.gt(a.spendableBalance)) // if spendable balance lower than total + ) { + errors.amount = new NotEnoughBalance(); + } + + if ( + a.cosmosResources && + a.cosmosResources.delegations.length > 0 && + t.useAllAmount + ) { + warnings.amount = new RecommendUndelegation(); + } + + return Promise.resolve({ + errors, + warnings, + estimatedFees, + amount, + totalSpent, + }); +}; + +const redelegationStatusError = (a: Account, t: Transaction) => { + if (a.cosmosResources) { + const redelegations = a.cosmosResources.redelegations; + invariant( + redelegations.length < COSMOS_MAX_REDELEGATIONS, + "redelegation should not have more than 6 entries" + ); + if ( + redelegations.some((redelegation) => { + const dstValidator = redelegation.validatorDstAddress; + return ( + dstValidator === t.cosmosSourceValidator && + redelegation.completionDate > new Date() + ); + }) + ) + return new CosmosRedelegationInProgress(); + if (t.cosmosSourceValidator === t.validators[0].address) + return new InvalidAddressBecauseDestinationIsAlsoSource(); + } + + return isDelegable(a, t.cosmosSourceValidator, t.validators[0].amount); +}; + +const isDelegable = ( + a: Account, + address: string | undefined | null, + amount: BigNumber +) => { + const { cosmosResources } = a; + invariant(cosmosResources, "cosmosResources should exist"); + + if ( + cosmosResources && + cosmosResources.delegations.some( + (delegation) => + delegation.validatorAddress === address && delegation.amount.lt(amount) + ) + ) { + return new NotEnoughDelegationBalance(); + } + + return null; +}; + +export default getTransactionStatus; diff --git a/src/families/cosmos/js-prepareTransaction.ts b/src/families/cosmos/js-prepareTransaction.ts new file mode 100644 index 0000000000..95657d5013 --- /dev/null +++ b/src/families/cosmos/js-prepareTransaction.ts @@ -0,0 +1,131 @@ +import { Account } from "../../types"; +import { Transaction } from "./types"; +import BigNumber from "bignumber.js"; +import { simulate } from "./api/Cosmos"; +import { encodePubkey } from "@cosmjs/proto-signing"; +import { getEnv } from "../../env"; +import { buildTransaction, postBuildTransaction } from "./js-buildTransaction"; +import { getMaxEstimatedBalance } from "./logic"; +import { CacheRes, makeLRUCache } from "../../cache"; + +export const calculateFees: CacheRes< + Array<{ + account: Account; + transaction: Transaction; + }>, + { + estimatedFees: BigNumber; + estimatedGas: BigNumber; + } +> = makeLRUCache( + async ({ + account, + transaction, + }): Promise<{ + estimatedFees: BigNumber; + estimatedGas: BigNumber; + }> => { + return await getEstimatedFees(account, transaction); + }, + ({ account, transaction }) => + `${account.id}_${account.currency.id}_${transaction.amount.toString()}_${ + transaction.recipient + }_${String(transaction.useAllAmount)}_${transaction.mode}_${ + transaction.validators + ? transaction.validators.map((v) => v.address).join("-") + : "" + }_${transaction.memo ? transaction.memo.toString() : ""}_${ + transaction.cosmosSourceValidator ? transaction.cosmosSourceValidator : "" + }` +); + +const getEstimatedFees = async ( + account: Account, + transaction: Transaction +): Promise => { + let gasQty = new BigNumber(250000); + const gasPrice = new BigNumber(getEnv("COSMOS_GAS_PRICE")); + + const unsignedPayload = await buildTransaction(account, transaction); + + // be sure payload is complete + if (unsignedPayload) { + const pubkey = encodePubkey({ + type: "tendermint/PubKeySecp256k1", + value: Buffer.from(account.seedIdentifier, "hex").toString("base64"), + }); + + const tx_bytes = await postBuildTransaction( + account, + transaction, + pubkey, + unsignedPayload, + new Uint8Array(Buffer.from(account.seedIdentifier, "hex")) + ); + + const gasUsed = await simulate(tx_bytes); + + if (gasUsed.gt(0)) { + gasQty = gasUsed + // Don't known what is going on, + // Ledger Live Desktop return half of what it should, + // Ledger Live Common CLI do the math correctly. + // Use coeff 2 as trick.. + // .multipliedBy(new BigNumber(getEnv("COSMOS_GAS_AMPLIFIER"))) + .multipliedBy(new BigNumber(getEnv("COSMOS_GAS_AMPLIFIER") * 2)) + .integerValue(); + } + } + + const estimatedGas = gasQty; + + const estimatedFees = gasPrice.multipliedBy(gasQty).integerValue(); + + return { estimatedFees, estimatedGas }; +}; + +export const prepareTransaction = async ( + account: Account, + transaction: Transaction +): Promise => { + let memo = transaction.memo; + let amount = transaction.amount; + + if (transaction.mode !== "send" && !transaction.memo) { + memo = "Ledger Live"; + } + + const { estimatedFees, estimatedGas } = await calculateFees({ + account, + transaction: { + ...transaction, + amount: transaction.useAllAmount + ? account.spendableBalance.minus(new BigNumber(2500)) + : amount, + memo, + }, + }); + + if (transaction.useAllAmount) { + amount = getMaxEstimatedBalance(account, estimatedFees); + } + + if ( + transaction.memo !== memo || + !estimatedFees.eq(transaction.fees || new BigNumber(0)) || + !estimatedGas.eq(transaction.gas || new BigNumber(0)) || + !amount.eq(transaction.amount) + ) { + return { + ...transaction, + memo, + fees: estimatedFees, + gas: estimatedGas, + amount, + }; + } + + return transaction; +}; + +export default prepareTransaction; diff --git a/src/families/cosmos/js-signOperation.ts b/src/families/cosmos/js-signOperation.ts new file mode 100644 index 0000000000..d8fb56515e --- /dev/null +++ b/src/families/cosmos/js-signOperation.ts @@ -0,0 +1,181 @@ +import { + Account, + Operation, + OperationType, + SignOperationEvent, +} from "../../types"; +import type { Transaction } from "./types"; +import { getAccount, getChainId } from "./api/Cosmos"; +import { Observable } from "rxjs"; +import { withDevice } from "../../hw/deviceAccess"; +import { encodePubkey } from "@cosmjs/proto-signing"; +import { encodeOperationId } from "../../operation"; +import { LedgerSigner } from "@cosmjs/ledger-amino"; +import { AminoTypes } from "@cosmjs/stargate"; +import { stringToPath } from "@cosmjs/crypto"; +import { buildTransaction, postBuildTransaction } from "./js-buildTransaction"; +import BigNumber from "bignumber.js"; + +const aminoTypes = new AminoTypes({ prefix: "cosmos" }); + +const signOperation = ({ + account, + deviceId, + transaction, +}: { + account: Account; + deviceId: any; + transaction: Transaction; +}): Observable => + withDevice(deviceId)((transport) => + Observable.create((o) => { + let cancelled; + + async function main() { + const { accountNumber, sequence } = await getAccount( + account.freshAddress + ); + + const chainId = await getChainId(); + + const hdPaths: any = stringToPath("m/" + account.freshAddressPath); + + const ledgerSigner = new LedgerSigner(transport, { + hdPaths: [hdPaths], + }); + + o.next({ type: "device-signature-requested" }); + + const accounts = await ledgerSigner.getAccounts(); + + let pubkey; + + accounts.forEach((a) => { + if (a.address == account.freshAddress) { + pubkey = encodePubkey({ + type: "tendermint/PubKeySecp256k1", + value: Buffer.from(a.pubkey).toString("base64"), + }); + } + }); + + const unsignedPayload = await buildTransaction(account, transaction); + + const msgs = unsignedPayload.map((msg) => aminoTypes.toAmino(msg)); + + // Note: + // We don't use Cosmos App, + // Cosmos App support legacy StdTx and required to be ordered in a strict way, + // Cosmos API expects a different sorting, resulting in a separate signature. + // https://github.com/LedgerHQ/app-cosmos/blob/6c194daa28936e273f9548eabca9e72ba04bb632/app/src/tx_parser.c#L52 + + const signed = await ledgerSigner.signAmino(account.freshAddress, { + chain_id: chainId, + account_number: accountNumber.toString(), + sequence: sequence.toString(), + fee: { + amount: [ + { + denom: account.currency.units[1].code, + amount: transaction.fees?.toString() as string, + }, + ], + gas: transaction.gas?.toString() as string, + }, + msgs: msgs, + memo: transaction.memo || "", + }); + + const tx_bytes = await postBuildTransaction( + account, + transaction, + pubkey, + unsignedPayload, + new Uint8Array(Buffer.from(signed.signature.signature, "base64")) + ); + + const signature = Buffer.from(tx_bytes).toString("hex"); + + if (cancelled) { + return; + } + + o.next({ type: "device-signature-granted" }); + + const hash = ""; // resolved at broadcast time + const accountId = account.id; + const fee = transaction.fees || new BigNumber(0); + const extra = {}; + + const type: OperationType = + transaction.mode === "undelegate" + ? "UNDELEGATE" + : transaction.mode === "delegate" + ? "DELEGATE" + : transaction.mode === "redelegate" + ? "REDELEGATE" + : ["claimReward", "claimRewardCompound"].includes(transaction.mode) + ? "REWARD" + : "OUT"; + + const senders: string[] = []; + const recipients: string[] = []; + + if (transaction.mode === "send") { + senders.push(account.freshAddress); + recipients.push(transaction.recipient); + } + + if (transaction.mode === "redelegate") { + Object.assign(extra, { + cosmosSourceValidator: transaction.cosmosSourceValidator, + }); + } + + if (transaction.mode !== "send") { + Object.assign(extra, { + validators: transaction.validators, + }); + } + + // build optimistic operation + const operation: Operation = { + id: encodeOperationId(accountId, hash, type), + hash, + type, + value: transaction.useAllAmount + ? account.spendableBalance + : transaction.amount.plus(fee), + fee, + extra, + blockHash: null, + blockHeight: null, + senders, + recipients, + accountId, + date: new Date(), + transactionSequenceNumber: sequence, + }; + + o.next({ + type: "signed", + signedOperation: { + operation, + signature, + expirationDate: null, + }, + }); + } + + main().then( + () => o.complete(), + (e) => o.error(e) + ); + + return () => { + cancelled = true; + }; + }) + ); + +export default signOperation; diff --git a/src/families/cosmos/js-synchronisation.ts b/src/families/cosmos/js-synchronisation.ts new file mode 100644 index 0000000000..a9491b52ca --- /dev/null +++ b/src/families/cosmos/js-synchronisation.ts @@ -0,0 +1,238 @@ +import { Account, Operation, OperationType } from "../../types"; +import { BigNumber } from "bignumber.js"; +import { makeSync, GetAccountShape, mergeOps } from "../../bridge/jsHelpers"; +import { encodeAccountId } from "../../account"; +import { getAccountInfo } from "./api/Cosmos"; +import { pubkeyToAddress, decodeBech32Pubkey } from "@cosmjs/amino"; +import { encodeOperationId } from "../../operation"; +import { CosmosDelegationInfo } from "./types"; + +const txToOps = (info: any, id: string, txs: any): Operation[] => { + const { address, currency } = info; + const ops: Operation[] = []; + + for (const tx of txs) { + let fees = new BigNumber(0); + + tx.tx.auth_info.fee.amount.forEach((elem) => { + fees = fees.plus(elem.amount); + }); + + const op: Operation = { + id: "", + hash: tx.txhash, + type: "" as OperationType, + value: new BigNumber(0), + fee: fees, + blockHash: null, + blockHeight: tx.height, + senders: [] as string[], + recipients: [] as string[], + accountId: id, + date: new Date(tx.timestamp), + extra: { + validators: [] as CosmosDelegationInfo[], + }, + transactionSequenceNumber: parseInt( + tx.tx.auth_info.signer_infos[0].sequence + ), + }; + + tx.logs.forEach((log) => { + log.events.forEach((message) => { + // parse attributes as key:value + const attributes: { [id: string]: any } = {}; + message.attributes.forEach( + (item) => (attributes[item.key] = item.value) + ); + + // https://docs.cosmos.network/v0.42/modules/staking/07_events.html + switch (message.type) { + case "transfer": + if ( + attributes.sender && + attributes.recipient && + attributes.amount + ) { + op.senders.push(attributes.sender); + op.recipients.push(attributes.recipient); + + if (attributes.amount.indexOf(currency.units[1].code) != -1) { + op.value = op.value.plus( + attributes.amount.replace(currency.units[1].code, "") + ); + } + + if (!op.type && attributes.sender === address) { + op.type = "OUT"; + op.value = op.value.plus(fees); + } else if (!op.type && attributes.recipient === address) { + op.type = "IN"; + } + } + break; + + case "withdraw_rewards": + if ( + (attributes.amount && + attributes.amount.indexOf(currency.units[1].code) != -1) || + // handle specifc case with empty amount value like + // tx DF458FE6A82C310837D7A33735FA5298BCF71B0BFF7A4134641AAE30F6F1050 + attributes.amount === "" + ) { + op.type = "REWARD"; + op.value = new BigNumber(fees); + op.extra.validators.push({ + address: attributes.validator, + amount: attributes.amount.replace(currency.units[1].code, ""), + }); + } + break; + + case "delegate": + if ( + attributes.amount && + attributes.amount.indexOf(currency.units[1].code) != -1 + ) { + op.type = "DELEGATE"; + op.value = new BigNumber(fees); + op.extra.validators.push({ + address: attributes.validator, + amount: attributes.amount.replace(currency.units[1].code, ""), + }); + } + break; + + case "redelegate": + if ( + attributes.amount && + attributes.amount.indexOf(currency.units[1].code) != -1 && + attributes.destination_validator && + attributes.source_validator + ) { + op.type = "REDELEGATE"; + op.value = new BigNumber(fees); + op.extra.validators.push({ + address: attributes.destination_validator, + amount: attributes.amount.replace(currency.units[1].code, ""), + }); + op.extra.cosmosSourceValidator = attributes.source_validator; + } + break; + + case "unbond": + if ( + attributes.amount && + attributes.amount.indexOf(currency.units[1].code) != -1 && + attributes.validator + ) { + op.type = "UNDELEGATE"; + op.value = new BigNumber(fees); + op.extra.validators.push({ + address: attributes.validator, + amount: attributes.amount.replace(currency.units[1].code, ""), + }); + } + break; + } + }); + }); + + if (!["IN", "OUT"].includes(op.type)) { + op.senders = []; + op.recipients = []; + } + + op.id = encodeOperationId(id, tx.txhash, op.type); + + if (op.type) { + ops.push(op); + } + } + + return ops; +}; + +const postSync = (initial: Account, parent: Account) => parent; + +export const getAccountShape: GetAccountShape = async (info) => { + const { address, currency, derivationMode, initialAccount } = info; + let xpubOrAddress = address; + + if (address.match("cosmospub")) { + const pubkey = decodeBech32Pubkey(address); + xpubOrAddress = pubkeyToAddress(pubkey as any, "cosmos"); + } + + const accountId = encodeAccountId({ + type: "js", + version: "2", + currencyId: currency.id, + xpubOrAddress, + derivationMode, + }); + + const { + balances, + blockHeight, + txs, + delegations, + redelegations, + unbondings, + withdrawAddress, + } = await getAccountInfo(xpubOrAddress); + + const oldOperations = initialAccount?.operations || []; + const newOperations = txToOps(info, accountId, txs); + const operations = mergeOps(oldOperations, newOperations); + + let balance = balances; + let delegatedBalance = new BigNumber(0); + let pendingRewardsBalance = new BigNumber(0); + let unbondingBalance = new BigNumber(0); + + for (const delegation of delegations) { + delegatedBalance = delegatedBalance.plus(delegation.amount); + balance = balance.plus(delegation.amount); + + pendingRewardsBalance = pendingRewardsBalance.plus( + delegation.pendingRewards + ); + } + + for (const unbonding of unbondings) { + unbondingBalance = unbondingBalance.plus(unbonding.amount); + } + + let spendableBalance = balance.minus(unbondingBalance.plus(delegatedBalance)); + + if (spendableBalance.lt(0)) { + spendableBalance = new BigNumber(0); + } + + const shape = { + id: accountId, + xpub: xpubOrAddress, + balance: balance, + spendableBalance, + operationsCount: operations.length, + blockHeight, + cosmosResources: { + delegations, + redelegations, + unbondings, + delegatedBalance, + pendingRewardsBalance, + unbondingBalance, + withdrawAddress, + }, + }; + + if (shape.spendableBalance && shape.spendableBalance.lt(0)) { + shape.spendableBalance = new BigNumber(0); + } + + return { ...shape, operations }; +}; + +export const sync = makeSync(getAccountShape, postSync); diff --git a/src/families/cosmos/js-updateTransaction.ts b/src/families/cosmos/js-updateTransaction.ts new file mode 100644 index 0000000000..cab42dd6b1 --- /dev/null +++ b/src/families/cosmos/js-updateTransaction.ts @@ -0,0 +1,21 @@ +import { Transaction } from "./types"; + +const updateTransaction = ( + t: Transaction, + patch: Partial +): Transaction => { + if ("mode" in patch && patch.mode !== t.mode) { + return { ...t, ...patch, gas: null, fees: null }; + } + + if ( + "validators" in patch && + patch.validators?.length !== t.validators.length + ) { + return { ...t, ...patch, gas: null, fees: null }; + } + + return { ...t, ...patch }; +}; + +export default updateTransaction; diff --git a/src/families/cosmos/libcore-buildTransaction.ts b/src/families/cosmos/libcore-buildTransaction.ts index cb36f3e72c..926d851e9e 100644 --- a/src/families/cosmos/libcore-buildTransaction.ts +++ b/src/families/cosmos/libcore-buildTransaction.ts @@ -136,7 +136,10 @@ export async function cosmosBuildTransaction({ const gasRequest = await core.CosmosGasLimitRequest.init( memoTransaction, messages, - String(getEnv("COSMOS_GAS_AMPLIFIER")) + // COSMOS_GAS_AMPLIFIER env use by JS implementation + // Set as 4 int in order to not break libcore implementation + // String(getEnv("COSMOS_GAS_AMPLIFIER")) + String(4) ); estimatedGas = await libcoreBigIntToBigNumber( // NOTE: With new cosmos code, this call might fail if the account hasn't been synchronized diff --git a/src/families/cosmos/specs.ts b/src/families/cosmos/specs.ts index 9777073eb7..445eb6902e 100644 --- a/src/families/cosmos/specs.ts +++ b/src/families/cosmos/specs.ts @@ -16,7 +16,6 @@ import { pickSiblings } from "../../bot/specs"; import type { AppSpec } from "../../bot/types"; import { toOperationRaw } from "../../account"; import { - COSMOS_MIN_SAFE, canClaimRewards, canDelegate, canUndelegate, @@ -24,18 +23,50 @@ import { getMaxDelegationAvailable, } from "./logic"; import { DeviceModelId } from "@ledgerhq/devices"; + +const minAmount = new BigNumber(20000); +const maxAccounts = 32; + +// amounts of delegation are not exact so we are applying an approximation +function approximateValue(value) { + return "~" + value.div(100).integerValue().times(100).toString(); +} + +function approximateExtra(extra) { + extra = { ...extra }; + if (extra.validators && Array.isArray(extra.validators)) { + extra.validators = extra.validators.map((v) => { + if (!v) return v; + const { amount, ...rest } = v; + if (!amount || typeof amount !== "string") return v; + return { ...rest, amount: approximateValue(new BigNumber(amount)) }; + }); + } + return extra; +} + const cosmos: AppSpec = { name: "Cosmos", currency: getCryptoCurrencyById("cosmos"), appQuery: { model: DeviceModelId.nanoS, - firmware: "<2", + firmware: "2.0.0", appName: "Cosmos", }, + testTimeout: 2 * 60 * 1000, transactionCheck: ({ maxSpendable }) => { - invariant(maxSpendable.gt(COSMOS_MIN_SAFE), "balance is too low"); + invariant(maxSpendable.gt(minAmount), "balance is too low"); }, - test: ({ operation, optimisticOperation }) => { + test: ({ account, operation, optimisticOperation }) => { + const allOperationsMatchingId = account.operations.filter( + (op) => op.id === operation.id + ); + if (allOperationsMatchingId.length > 1) { + console.warn(allOperationsMatchingId); + } + expect({ allOperationsMatchingId }).toEqual({ + allOperationsMatchingId: [operation], + }); const opExpected: Record = toOperationRaw({ ...optimisticOperation, }); @@ -44,30 +75,33 @@ const cosmos: AppSpec = { delete opExpected.date; delete opExpected.blockHash; delete opExpected.blockHeight; - expect(toOperationRaw(operation)).toMatchObject(opExpected); // TODO check it is between operation.value-fees (excluded) and operation.value - - /* - // balance move - expect(account.balance.toString()).toBe( - accountBeforeTransaction.balance.minus(operation.value).toString() - ); - */ + const extra = opExpected.extra; + delete opExpected.extra; + const op = toOperationRaw(operation); + expect(op).toMatchObject(opExpected); + expect(approximateExtra(op.extra)).toMatchObject(approximateExtra(extra)); }, mutations: [ { name: "send some", - maxRun: 2, + test: ({ account, accountBeforeTransaction, operation }) => { + expect(account.balance.toString()).toBe( + accountBeforeTransaction.balance.minus(operation.value).toString() + ); + }, transaction: ({ account, siblings, bridge, maxSpendable }) => { + const amount = maxSpendable + .times(0.3 + 0.4 * Math.random()) + .integerValue(); + invariant(amount.gt(0), "random amount to be positive"); return { transaction: bridge.createTransaction(account), updates: [ { - recipient: pickSiblings(siblings, 7).freshAddress, + recipient: pickSiblings(siblings, maxAccounts).freshAddress, }, { - amount: maxSpendable - .times(0.3 + 0.4 * Math.random()) - .integerValue(), + amount, }, Math.random() < 0.5 ? { @@ -86,7 +120,7 @@ const cosmos: AppSpec = { transaction: bridge.createTransaction(account), updates: [ { - recipient: pickSiblings(siblings, 7).freshAddress, + recipient: pickSiblings(siblings, maxAccounts).freshAddress, }, { useAllAmount: true, @@ -100,24 +134,25 @@ const cosmos: AppSpec = { }, { name: "delegate new validators", - maxRun: 2, + maxRun: 1, transaction: ({ account, bridge }) => { invariant( - account.index % 10 > 0, - "one out of 10 accounts is not going to delegate" + account.index % 2 > 0, + "only one out of 2 accounts is not going to delegate" ); invariant(canDelegate(account), "can delegate"); const { cosmosResources } = account; invariant(cosmosResources, "cosmos"); invariant( - (cosmosResources as CosmosResources).delegations.length < 10, + (cosmosResources as CosmosResources).delegations.length < 3, "already enough delegations" ); const data = getCurrentCosmosPreloadData(); - const count = 1 + Math.floor(5 * Math.random()); - let remaining = getMaxDelegationAvailable(account, count).times( - Math.random() - ); + const count = 1 + Math.floor(2 * Math.random()); + let remaining = getMaxDelegationAvailable(account, count) + .minus(minAmount.times(2)) + .times(0.5 * Math.random()); + invariant(remaining.gt(0), "not enough funds in account for delegate"); const all = data.validators.filter( (v) => !(cosmosResources as CosmosResources).delegations.some( @@ -162,17 +197,10 @@ const cosmos: AppSpec = { invariant(d, "delegated %s must be found in account", v.address); expect({ address: v.address, - // we round last digit - amount: "~" + v.amount.div(10).integerValue().times(10).toString(), + amount: approximateValue(v.amount), }).toMatchObject({ address: (d as CosmosDelegation).validatorAddress, - amount: - "~" + - (d as CosmosDelegation).amount - .div(10) - .integerValue() - .times(10) - .toString(), + amount: approximateValue((d as CosmosDelegation).amount), }); }); }, @@ -202,6 +230,12 @@ const cosmos: AppSpec = { ) ); invariant(undelegateCandidate, "already pending"); + + const amount = (undelegateCandidate as CosmosDelegation).amount // most of the time, undelegate all + .times(Math.random() > 0.3 ? 1 : Math.random()) + .integerValue(); + invariant(amount.gt(0), "random amount to be positive"); + return { transaction: bridge.createTransaction(account), updates: [ @@ -214,9 +248,7 @@ const cosmos: AppSpec = { { address: (undelegateCandidate as CosmosDelegation) .validatorAddress, - amount: (undelegateCandidate as CosmosDelegation).amount // most of the time, undelegate all - .times(Math.random() > 0.3 ? 1 : Math.random()) - .integerValue(), + amount, }, ], }, @@ -233,17 +265,10 @@ const cosmos: AppSpec = { invariant(d, "undelegated %s must be found in account", v.address); expect({ address: v.address, - // we round last digit - amount: "~" + v.amount.div(10).integerValue().times(10).toString(), + amount: approximateValue(v.amount), }).toMatchObject({ address: (d as CosmosUnbonding).validatorAddress, - amount: - "~" + - (d as CosmosUnbonding).amount - .div(10) - .integerValue() - .times(10) - .toString(), + amount: approximateValue((d as CosmosUnbonding).amount), }); }); }, @@ -267,6 +292,13 @@ const cosmos: AppSpec = { (sourceDelegation as CosmosDelegation).validatorAddress ) ); + const amount = (sourceDelegation as CosmosDelegation).amount + .times( + // most of the time redelegate all + Math.random() > 0.3 ? 1 : Math.random() + ) + .integerValue(); + invariant(amount.gt(0), "random amount to be positive"); return { transaction: bridge.createTransaction(account), updates: [ @@ -278,12 +310,7 @@ const cosmos: AppSpec = { validators: [ { address: (delegation as CosmosDelegation).validatorAddress, - amount: (sourceDelegation as CosmosDelegation).amount - .times( - // most of the time redelegate all - Math.random() > 0.3 ? 1 : Math.random() - ) - .integerValue(), + amount, }, ], }, @@ -294,32 +321,34 @@ const cosmos: AppSpec = { const { cosmosResources } = account; invariant(cosmosResources, "cosmos"); transaction.validators.forEach((v) => { - const d = (cosmosResources as CosmosResources).redelegations - .slice(0) // recent first - .sort( - // FIXME: valueOf for date arithmetic operations in typescript - (a, b) => b.completionDate.valueOf() - a.completionDate.valueOf() - ) // find the related redelegation - .find( - (d) => - d.validatorDstAddress === v.address && - d.validatorSrcAddress === transaction.cosmosSourceValidator - ); - invariant(d, "redelegated %s must be found in account", v.address); - expect({ - address: v.address, - // we round last digit - amount: "~" + v.amount.div(10).integerValue().times(10).toString(), - }).toMatchObject({ - address: (d as CosmosRedelegation).validatorDstAddress, - amount: - "~" + - (d as CosmosRedelegation).amount - .div(10) - .integerValue() - .times(10) - .toString(), - }); + // we possibly are moving from one existing delegation to another existing. + // in that case it's not a redelegation, it effects immediately + const existing = ( + cosmosResources as CosmosResources + ).delegations.find((d) => d.validatorAddress === v.address); + if (!existing) { + // in other case, we will find it in a redelegation + const d = (cosmosResources as CosmosResources).redelegations + .slice(0) // recent first + .sort( + // FIXME: valueOf for date arithmetic operations in typescript + (a, b) => + b.completionDate.valueOf() - a.completionDate.valueOf() + ) // find the related redelegation + .find( + (d) => + d.validatorDstAddress === v.address && + d.validatorSrcAddress === transaction.cosmosSourceValidator + ); + invariant(d, "redelegated %s must be found in account", v.address); + expect({ + address: v.address, + amount: approximateValue(v.amount), + }).toMatchObject({ + address: (d as CosmosRedelegation).validatorDstAddress, + amount: approximateValue((d as CosmosRedelegation).amount), + }); + } }); }, }, @@ -330,12 +359,10 @@ const cosmos: AppSpec = { const { cosmosResources } = account; invariant(cosmosResources, "cosmos"); const delegation = sample( - (cosmosResources as CosmosResources).delegations.filter( - // FIXME - // (d) => canClaimRewards(account, d) && d.pendingRewards.gt(2000) - (d) => d.pendingRewards.gt(2000) + (cosmosResources as CosmosResources).delegations.filter((d) => + d.pendingRewards.gt(1000) ) - ); + ) as CosmosDelegation; invariant(delegation, "no delegation to claim"); return { transaction: bridge.createTransaction(account), @@ -345,13 +372,8 @@ const cosmos: AppSpec = { memo: "LedgerLiveBot", validators: [ { - address: (delegation as CosmosDelegation).validatorAddress, - // TODO: the test should be - // amount: delegation.pendingRewards, - // but it won't work until COIN-665 is fixed until then, - // amount is set to 0 in - // src/families/cosmos/libcore-buildOperation in the REWARD case - amount: new BigNumber(0), + address: delegation.validatorAddress, + amount: delegation.pendingRewards, }, ], }, diff --git a/src/families/cosmos/test-dataset.ts b/src/families/cosmos/test-dataset.ts index 43220ee22c..33042932da 100644 --- a/src/families/cosmos/test-dataset.ts +++ b/src/families/cosmos/test-dataset.ts @@ -455,9 +455,9 @@ const cosmos: CurrenciesData = { ], }; const dataset: DatasetTest = { - implementations: ["libcore"], + implementations: ["js"], currencies: { - // cosmos, // LL-7872 + cosmos, }, }; export default dataset; diff --git a/src/families/cosmos/validators.ts b/src/families/cosmos/validators.ts index 1138d468c4..11946e1891 100644 --- a/src/families/cosmos/validators.ts +++ b/src/families/cosmos/validators.ts @@ -181,8 +181,8 @@ const getRewardsState = makeLRUCache( ); const getStargateRewardsState = makeLRUCache( - async (_currency: CryptoCurrency) => { - // Fake numbers until Gaia fixes its endpoints + async (currency: CryptoCurrency) => { + /* return { targetBondedRatio: 0.01, communityPoolCommission: 0.0, @@ -196,81 +196,102 @@ const getStargateRewardsState = makeLRUCache( averageDailyFees: 0, currentValueInflation: 0.01, }; - /* - // All obtained values are strings ; so sometimes we will need to parse them as numbers - const inflationUrl = `${getBaseApiUrl( - currency - )}/cosmos/mint/v1beta1/inflation`; - const { data: inflationData } = await network({ - url: inflationUrl, - method: "GET", - }); - const currentValueInflation = parseFloat(inflationData.inflation); - const inflationParametersUrl = `${getBaseApiUrl( - currency - )}/cosmos/mint/v1beta1/params`; - const { data: inflationParametersData } = await network({ - url: inflationParametersUrl, - method: "GET", - }); - const inflationRateChange = parseFloat( - inflationParametersData.params.inflation_rate_change - ); - const inflationMaxRate = parseFloat( - inflationParametersData.params.inflation_max - ); - const inflationMinRate = parseFloat( - inflationParametersData.params.inflation_min - ); - const targetBondedRatio = parseFloat( - inflationParametersData.params.goal_bonded - ); - // Source for seconds per year : https://github.com/gavinly/CosmosParametersWiki/blob/master/Mint.md#notes-3 - // 365.24 (days) * 24 (hours) * 60 (minutes) * 60 (seconds) = 31556736 seconds - const assumedTimePerBlock = - 31556736.0 / parseFloat(inflationParametersData.params.blocks_per_year); - const communityTaxUrl = `${getBaseApiUrl( - currency - )}/cosmos/distribution/v1beta1/params`; - const { data: communityTax } = await network({ - url: communityTaxUrl, - method: "GET", - }); - const communityPoolCommission = parseFloat( - communityTax.params.community_tax - ); - const supplyUrl = `${getBaseApiUrl(currency)}/cosmos/bank/v1beta1/supply/${ - currency.id == "cosmos_testnet" ? "umuon" : "uatom" - }`; - const { data: totalSupplyData } = await network({ - url: supplyUrl, - method: "GET", - }); - const totalSupply = parseUatomStrAsAtomNumber( - totalSupplyData.amount.amount - ); - const ratioUrl = `${getBaseApiUrl(currency)}/cosmos/staking/v1beta1/pool`; - const { data: ratioData } = await network({ url: ratioUrl, method: "GET" }); - const actualBondedRatio = - parseUatomStrAsAtomNumber(ratioData.pool.bonded_tokens) / totalSupply; - // Arbitrary value in ATOM. - const averageDailyFees = 20; - // Arbitrary value in seconds - const averageTimePerBlock = 7.5; - return { - targetBondedRatio, - communityPoolCommission, - assumedTimePerBlock, - inflationRateChange, - inflationMaxRate, - inflationMinRate, - actualBondedRatio, - averageTimePerBlock, - totalSupply, - averageDailyFees, - currentValueInflation, - }; - */ + */ + + // All obtained values are strings ; so sometimes we will need to parse them as numbers + const inflationUrl = `${getBaseApiUrl( + currency + )}/cosmos/mint/v1beta1/inflation`; + + const { data: inflationData } = await network({ + url: inflationUrl, + method: "GET", + }); + + const currentValueInflation = parseFloat(inflationData.inflation); + + const inflationParametersUrl = `${getBaseApiUrl( + currency + )}/cosmos/mint/v1beta1/params`; + + const { data: inflationParametersData } = await network({ + url: inflationParametersUrl, + method: "GET", + }); + + const inflationRateChange = parseFloat( + inflationParametersData.params.inflation_rate_change + ); + + const inflationMaxRate = parseFloat( + inflationParametersData.params.inflation_max + ); + + const inflationMinRate = parseFloat( + inflationParametersData.params.inflation_min + ); + + const targetBondedRatio = parseFloat( + inflationParametersData.params.goal_bonded + ); + + // Source for seconds per year : https://github.com/gavinly/CosmosParametersWiki/blob/master/Mint.md#notes-3 + // 365.24 (days) * 24 (hours) * 60 (minutes) * 60 (seconds) = 31556736 seconds + const assumedTimePerBlock = + 31556736.0 / parseFloat(inflationParametersData.params.blocks_per_year); + + const communityTaxUrl = `${getBaseApiUrl( + currency + )}/cosmos/distribution/v1beta1/params`; + + const { data: communityTax } = await network({ + url: communityTaxUrl, + method: "GET", + }); + + const communityPoolCommission = parseFloat( + communityTax.params.community_tax + ); + + const supplyUrl = `${getBaseApiUrl(currency)}/cosmos/bank/v1beta1/supply/${ + currency.id == "cosmos_testnet" ? "umuon" : "uatom" + }`; + + const { data: totalSupplyData } = await network({ + url: supplyUrl, + method: "GET", + }); + + const totalSupply = parseUatomStrAsAtomNumber( + totalSupplyData.amount.amount + ); + + const ratioUrl = `${getBaseApiUrl(currency)}/cosmos/staking/v1beta1/pool`; + + const { data: ratioData } = await network({ url: ratioUrl, method: "GET" }); + + const actualBondedRatio = + parseUatomStrAsAtomNumber(ratioData.pool.bonded_tokens) / totalSupply; + + // Arbitrary value in ATOM. + const averageDailyFees = 20; + + // Arbitrary value in seconds + const averageTimePerBlock = 7.5; + + return { + targetBondedRatio, + communityPoolCommission, + assumedTimePerBlock, + inflationRateChange, + inflationMaxRate, + inflationMinRate, + actualBondedRatio, + averageTimePerBlock, + totalSupply, + averageDailyFees, + currentValueInflation, + }; }, (currency: CryptoCurrency) => currency.id ); diff --git a/src/families/crypto_org/specs.ts b/src/families/crypto_org/specs.ts index dcbb439205..b390971b92 100644 --- a/src/families/crypto_org/specs.ts +++ b/src/families/crypto_org/specs.ts @@ -5,12 +5,6 @@ import { pickSiblings } from "../../bot/specs"; import type { AppSpec } from "../../bot/types"; import { DeviceModelId } from "@ledgerhq/devices"; -// this spec defines a set of "rules" that the bot will follow -// to accumulate transactions on accounts. -// right now, it will rotate the funds among 3 accounts by doing a 50% send each run. -// TODO: more cases to cover -// TODO: more assertions could be written - const sharedMutations = ({ maxAccount }) => [ { name: "move 50%", @@ -46,9 +40,29 @@ const crypto_org_croeseid: AppSpec = { "balance is too low" ); }, - mutations: sharedMutations({ maxAccount: 3 }), + mutations: sharedMutations({ maxAccount: 5 }), +}; + +const crypto_org: AppSpec = { + name: "Crypto.org", + currency: getCryptoCurrencyById("crypto_org"), + appQuery: { + model: DeviceModelId.nanoS, + appName: "Crypto.orgChain", + }, + testTimeout: 4 * 60 * 1000, + transactionCheck: ({ maxSpendable }) => { + invariant( + maxSpendable.gt( + parseCurrencyUnit(getCryptoCurrencyById("crypto_org").units[0], "0.01") + ), + "balance is too low" + ); + }, + mutations: sharedMutations({ maxAccount: 5 }), }; export default { crypto_org_croeseid, + crypto_org, }; diff --git a/src/generated/bridge/js.ts b/src/generated/bridge/js.ts index 27a9878846..b3ffa91806 100644 --- a/src/generated/bridge/js.ts +++ b/src/generated/bridge/js.ts @@ -4,6 +4,8 @@ import bitcoin from "../../families/bitcoin/bridge/js"; import celo from "../../families/celo/bridge/js"; +import cosmos from "../../families/cosmos/bridge/js"; + import crypto_org from "../../families/crypto_org/bridge/js"; import elrond from "../../families/elrond/bridge/js"; @@ -31,6 +33,7 @@ export default { algorand, bitcoin, celo, + cosmos, crypto_org, elrond, ethereum, diff --git a/yarn.lock b/yarn.lock index 2afe6da26c..f4b931594a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -452,6 +452,16 @@ "@cosmjs/math" "^0.25.0-alpha.2" "@cosmjs/utils" "^0.25.0-alpha.2" +"@cosmjs/amino@0.26.6": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.26.6.tgz#eb652ec4551e820f6b460935375a37b1cb73e7a2" + integrity sha512-O2MNJTduMnQzr7cK9PmvselY7XVCV+GxjC0vR/NBJmKZt7+GgGnHTLbbdOJr5MAQcESCwTkGAnnctw7hhoEjqw== + dependencies: + "@cosmjs/crypto" "0.26.6" + "@cosmjs/encoding" "0.26.6" + "@cosmjs/math" "0.26.6" + "@cosmjs/utils" "0.26.6" + "@cosmjs/amino@^0.25.0-alpha.2", "@cosmjs/amino@^0.25.4": version "0.25.4" resolved "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.25.4.tgz" @@ -462,6 +472,22 @@ "@cosmjs/math" "^0.25.4" "@cosmjs/utils" "^0.25.4" +"@cosmjs/crypto@0.26.6", "@cosmjs/crypto@^0.26.5": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.26.6.tgz#4ee84e8707406a951a43eac34ffc83ff6c6030f3" + integrity sha512-nR8gXZH6NljKL4vArkCmDCVA10hMtHHaJQYGlHpYufnXbbx4614FnzOd8Y/CkunhjFGM0jn/WFT4rCjbPYzuUw== + dependencies: + "@cosmjs/encoding" "0.26.6" + "@cosmjs/math" "0.26.6" + "@cosmjs/utils" "0.26.6" + bip39 "^3.0.2" + bn.js "^4.11.8" + elliptic "^6.5.3" + js-sha3 "^0.8.0" + libsodium-wrappers "^0.7.6" + ripemd160 "^2.0.2" + sha.js "^2.4.11" + "@cosmjs/crypto@^0.24.1": version "0.24.1" resolved "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.24.1.tgz" @@ -505,6 +531,15 @@ bech32 "^1.1.4" readonly-date "^1.0.0" +"@cosmjs/encoding@0.26.6": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.26.6.tgz#80de9f1a4b5b4cc203b16b4190b9a42da7de1a49" + integrity sha512-dU0P2Um9ZB5yHpQYq+a6XnPKV4LD1kHd3nggbD0smn7wTwWW1XJKlms40SBZHtbm4dW9wPaPGf4yOkwwBdJO+w== + dependencies: + base64-js "^1.3.0" + bech32 "^1.1.4" + readonly-date "^1.0.0" + "@cosmjs/encoding@^0.24.1": version "0.24.1" resolved "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.24.1.tgz" @@ -523,6 +558,14 @@ bech32 "^1.1.4" readonly-date "^1.0.0" +"@cosmjs/json-rpc@0.26.6": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.26.6.tgz#a41d706f419281a4586553fe68a65f773fb56d1a" + integrity sha512-cxHEdiqeHxUHsOxUiaWUMF7idoto+5UtqvKiZyHdcy7Xvjx4j8d3FIG4p1LYh0Qbt4sHpRzzFLN4AMrhLz12OA== + dependencies: + "@cosmjs/stream" "0.26.6" + xstream "^11.14.0" + "@cosmjs/json-rpc@^0.25.0-alpha.2", "@cosmjs/json-rpc@^0.25.4": version "0.25.4" resolved "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.25.4.tgz" @@ -543,6 +586,19 @@ axios "^0.21.1" fast-deep-equal "^3.1.3" +"@cosmjs/ledger-amino@^0.26.5": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/ledger-amino/-/ledger-amino-0.26.6.tgz#4fd342229f3de3059e193f8db3f88877074aabe0" + integrity sha512-L5KDfEq7EswV4ku2SbWlozfKVv9WJWtap4/7SMXKH0XrYWOIz0AYeBfM0OGtJQjuHAiD/1QJ8pam/kjUL3+quQ== + dependencies: + "@cosmjs/amino" "0.26.6" + "@cosmjs/crypto" "0.26.6" + "@cosmjs/encoding" "0.26.6" + "@cosmjs/math" "0.26.6" + "@cosmjs/utils" "0.26.6" + ledger-cosmos-js "^2.1.8" + semver "^7.3.2" + "@cosmjs/math@0.23.1": version "0.23.1" resolved "https://registry.npmjs.org/@cosmjs/math/-/math-0.23.1.tgz" @@ -550,6 +606,13 @@ dependencies: bn.js "^4.11.8" +"@cosmjs/math@0.26.6": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.26.6.tgz#11f3273634bab69187c6361533a14c72f611f6a2" + integrity sha512-nblvidxwugM/kh1Vx95s7MQ596r5ap1ZUpjHYJTLbnYvnObHvfYvM3qb8SJzY0u7x5+u9E0oSFzLwMRfUTEQ3g== + dependencies: + bn.js "^4.11.8" + "@cosmjs/math@^0.24.1": version "0.24.1" resolved "https://registry.npmjs.org/@cosmjs/math/-/math-0.24.1.tgz" @@ -573,6 +636,18 @@ long "^4.0.0" protobufjs "~6.10.2" +"@cosmjs/proto-signing@0.26.6", "@cosmjs/proto-signing@^0.26.5": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.26.6.tgz#c05b84437634d1b19e36bd4b8864f3b41a08329a" + integrity sha512-wwR/ObID/g3bCt+I9Xv0a7Qmhu/+cRacFyh4tFY9ak+M6Q+5eyn+Gpj0MVLWG9cRPT7W1uVnr+8HRLhUEHExqg== + dependencies: + "@cosmjs/amino" "0.26.6" + "@cosmjs/crypto" "0.26.6" + "@cosmjs/math" "0.26.6" + cosmjs-types "^0.2.0" + long "^4.0.0" + protobufjs "~6.10.2" + "@cosmjs/proto-signing@^0.25.0-alpha.2": version "0.25.4" resolved "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.25.4.tgz" @@ -582,6 +657,16 @@ long "^4.0.0" protobufjs "~6.10.2" +"@cosmjs/socket@0.26.6": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.26.6.tgz#17aefcc2de412c26c93c5b31d71f1c4b8e06491d" + integrity sha512-JLizR/QlRJ+nBE/A4QfhinTLycI7a20w0hgHhkq9UUvRlFEh+j6bBK7TilDYZpX0Yjb+wJhCt7wHTiJo+uLjSA== + dependencies: + "@cosmjs/stream" "0.26.6" + isomorphic-ws "^4.0.1" + ws "^7" + xstream "^11.14.0" + "@cosmjs/socket@^0.25.0-alpha.2", "@cosmjs/socket@^0.25.4": version "0.25.4" resolved "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.25.4.tgz" @@ -608,6 +693,31 @@ long "^4.0.0" protobufjs "~6.10.2" +"@cosmjs/stargate@^0.26.5": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.26.6.tgz#4f37647cce45298547a9b226506f9667033d0671" + integrity sha512-R5BolHkZGyblL0nNb0xXxwzDml57DYe2UE9jdlsOOJ7L/auZvThKxlfP473H/OHqsqwc7G2JRoCENtfvZRvTig== + dependencies: + "@confio/ics23" "^0.6.3" + "@cosmjs/amino" "0.26.6" + "@cosmjs/encoding" "0.26.6" + "@cosmjs/math" "0.26.6" + "@cosmjs/proto-signing" "0.26.6" + "@cosmjs/stream" "0.26.6" + "@cosmjs/tendermint-rpc" "0.26.6" + "@cosmjs/utils" "0.26.6" + cosmjs-types "^0.2.0" + long "^4.0.0" + protobufjs "~6.10.2" + xstream "^11.14.0" + +"@cosmjs/stream@0.26.6": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.26.6.tgz#5474da08b5e8dd46de61ce9f16e39ad9751e9779" + integrity sha512-4Tfh1UlSCEBl+yqPeu+4q1uqwkKbx5gqYU/JDL81cLHW5QpxUA83F59+Pr9XohcnrHUmSt3DoDPqIlAoIdft1Q== + dependencies: + xstream "^11.14.0" + "@cosmjs/stream@^0.25.0-alpha.2", "@cosmjs/stream@^0.25.4": version "0.25.4" resolved "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.25.4.tgz" @@ -630,6 +740,21 @@ readonly-date "^1.0.0" xstream "^11.14.0" +"@cosmjs/tendermint-rpc@0.26.6": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.26.6.tgz#36d3aec1b004f1a08ef3f3e0e15f6d0a5dc5e303" + integrity sha512-mXK09xsu68EM08KRhZ5Hg0o8zhN2WoXLjdDfQ+DGbpJLZQePpzzXKaMYY4eqwvECB6zsImpMVtfXoHMfK623kA== + dependencies: + "@cosmjs/crypto" "0.26.6" + "@cosmjs/encoding" "0.26.6" + "@cosmjs/json-rpc" "0.26.6" + "@cosmjs/math" "0.26.6" + "@cosmjs/socket" "0.26.6" + "@cosmjs/stream" "0.26.6" + axios "^0.21.2" + readonly-date "^1.0.0" + xstream "^11.14.0" + "@cosmjs/tendermint-rpc@^0.25.0-alpha.2": version "0.25.4" resolved "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.25.4.tgz" @@ -645,6 +770,11 @@ readonly-date "^1.0.0" xstream "^11.14.0" +"@cosmjs/utils@0.26.6": + version "0.26.6" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.26.6.tgz#134ef1ea0675580c2cc7524589d09f3d42c678a7" + integrity sha512-Zx60MMI1vffX8c2UbUMlszrGIug3TWa25bD7NF3blJ5k/MVCZFsPafEZ+jEi7kcqoxdhMhgJTI6AmUhnMfq9SQ== + "@cosmjs/utils@^0.24.1": version "0.24.1" resolved "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.24.1.tgz" @@ -1328,7 +1458,7 @@ "@ledgerhq/errors" "^6.10.0" events "^3.3.0" -"@ledgerhq/hw-transport@^5.11.0", "@ledgerhq/hw-transport@^5.19.1", "@ledgerhq/hw-transport@^5.51.1": +"@ledgerhq/hw-transport@^5.11.0", "@ledgerhq/hw-transport@^5.19.1", "@ledgerhq/hw-transport@^5.25.0", "@ledgerhq/hw-transport@^5.51.1": version "5.51.1" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz#8dd14a8e58cbee4df0c29eaeef983a79f5f22578" integrity sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw== @@ -2896,7 +3026,7 @@ axios@0.26.0, axios@^0.26.0: dependencies: follow-redirects "^1.14.8" -axios@^0.21.1: +axios@^0.21.1, axios@^0.21.2: version "0.21.4" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== @@ -3838,6 +3968,14 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmjs-types@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cosmjs-types/-/cosmjs-types-0.2.1.tgz#bfa8e7721939e46f0fbd7848a82b3b47a2f7b3f2" + integrity sha512-EUG6TgdWkYHBzXjo5tZ82L+0QLijTu/rZGNIbJ/n07ST30GmptYkPmO+REX7qF4YUtli//Rfy0rrNzH9IMrMmw== + dependencies: + long "^4.0.0" + protobufjs "~6.11.2" + country-data@^0.0.31: version "0.0.31" resolved "https://registry.yarnpkg.com/country-data/-/country-data-0.0.31.tgz#80966b8e1d147fa6d6a589d32933f8793774956d" @@ -6666,6 +6804,16 @@ leb128@^0.0.5: bn.js "^5.0.0" buffer-pipe "0.0.3" +ledger-cosmos-js@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/ledger-cosmos-js/-/ledger-cosmos-js-2.1.8.tgz#b409ecd1e77f630e6fb212a9f602fe5c6e8f054b" + integrity sha512-Gl7SWMq+3R9OTkF1hLlg5+1geGOmcHX9OdS+INDsGNxSiKRWlsWCvQipGoDnRIQ6CPo2i/Ze58Dw0Mt/l3UYyA== + dependencies: + "@babel/runtime" "^7.11.2" + "@ledgerhq/hw-transport" "^5.25.0" + bech32 "^1.1.4" + ripemd160 "^2.0.2" + leven@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" @@ -7693,9 +7841,9 @@ protobufjs@6.10.1: "@types/node" "^13.7.0" long "^4.0.0" -protobufjs@^6.8.8: +protobufjs@^6.8.8, protobufjs@~6.11.2: version "6.11.2" - resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== dependencies: "@protobufjs/aspromise" "^1.1.2"