From 5ff5aeae4a00b12599c2b63738e707022dd6ddb4 Mon Sep 17 00:00:00 2001 From: henri-ly <33897120+henri-ly@users.noreply.github.com> Date: Fri, 22 May 2020 18:46:24 +0200 Subject: [PATCH] WIP Cosmos (#661) --- cli/src/live-common-setup-base.js | 1 + cli/yarn.lock | 46 +- scripts/sync-families-dispatch.sh | 1 + .../__snapshots__/all.libcore.js.snap | 1782 +++++++++++++++++ src/__tests__/mock-bridges.js | 1 + src/__tests__/test-helpers/setup.js | 1 + src/derivation.js | 4 + src/env.js | 16 + src/errors.js | 27 + src/families/cosmos/app/common.js | 83 + src/families/cosmos/app/helperV1.js | 76 + src/families/cosmos/app/helperV2.js | 53 + src/families/cosmos/app/index.js | 280 +++ src/families/cosmos/bridge/libcore.js | 243 +++ src/families/cosmos/bridge/mock.js | 2 +- src/families/cosmos/cli-transaction.js | 125 ++ src/families/cosmos/hw-getAddress.js | 23 + src/families/cosmos/libcore-broadcast.js | 26 + src/families/cosmos/libcore-buildOperation.js | 26 + .../cosmos/libcore-buildTransaction.js | 98 + .../cosmos/libcore-getFeesForTransaction.js | 24 + .../cosmos/libcore-postBuildAccount.js | 142 ++ src/families/cosmos/libcore-signOperation.js | 84 + src/families/cosmos/message.js | 119 ++ src/families/cosmos/mock.js | 34 +- src/families/cosmos/preloadedData.js | 3 - src/families/cosmos/preloadedData.mock.js | 138 +- src/families/cosmos/serialization.js | 52 +- src/families/cosmos/test-dataset.js | 347 ++++ src/families/cosmos/test-specifics.js | 194 ++ src/families/cosmos/types.js | 445 +++- src/families/cosmos/validators.js | 193 ++ src/generated/bridge/libcore.js | 2 + src/generated/cli-transaction.js | 2 + src/generated/hw-getAddress.js | 2 + src/generated/libcore-buildOperation.js | 2 + .../libcore-getFeesForTransaction.js | 2 + src/generated/libcore-postBuildAccount.js | 6 + src/generated/test-dataset.js | 2 + src/generated/test-specifics.js | 2 + src/libcore/buildAccount/index.js | 8 + src/libcore/syncAccount.js | 1 + src/mock/account.js | 4 +- src/types/operation.js | 3 +- 44 files changed, 4675 insertions(+), 50 deletions(-) create mode 100644 src/families/cosmos/app/common.js create mode 100644 src/families/cosmos/app/helperV1.js create mode 100644 src/families/cosmos/app/helperV2.js create mode 100644 src/families/cosmos/app/index.js create mode 100644 src/families/cosmos/bridge/libcore.js create mode 100644 src/families/cosmos/cli-transaction.js create mode 100644 src/families/cosmos/hw-getAddress.js create mode 100644 src/families/cosmos/libcore-broadcast.js create mode 100644 src/families/cosmos/libcore-buildOperation.js create mode 100644 src/families/cosmos/libcore-buildTransaction.js create mode 100644 src/families/cosmos/libcore-getFeesForTransaction.js create mode 100644 src/families/cosmos/libcore-postBuildAccount.js create mode 100644 src/families/cosmos/libcore-signOperation.js create mode 100644 src/families/cosmos/message.js create mode 100644 src/families/cosmos/test-dataset.js create mode 100644 src/families/cosmos/test-specifics.js create mode 100644 src/families/cosmos/validators.js create mode 100644 src/generated/libcore-postBuildAccount.js diff --git a/cli/src/live-common-setup-base.js b/cli/src/live-common-setup-base.js index 24ae5d419d..63461474f6 100644 --- a/cli/src/live-common-setup-base.js +++ b/cli/src/live-common-setup-base.js @@ -48,6 +48,7 @@ setSupportedCurrencies([ "ethereum_ropsten", "tron", "stellar", + "cosmos" ]); for (const k in process.env) setEnvUnsafe(k, process.env[k]); diff --git a/cli/yarn.lock b/cli/yarn.lock index 96b4f8cd86..07098ae3a2 100644 --- a/cli/yarn.lock +++ b/cli/yarn.lock @@ -830,6 +830,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.7.4": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" + integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" @@ -879,11 +886,25 @@ "@ledgerhq/logs" "^5.15.0" rxjs "^6.5.5" +"@ledgerhq/devices@^4.78.0": + version "4.78.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-4.78.0.tgz#149b572f0616096e2bd5eb14ce14d0061c432be6" + integrity sha512-tWKS5WM/UU82czihnVjRwz9SXNTQzWjGJ/7+j/xZ70O86nlnGJ1aaFbs5/WTzfrVKpOKgj1ZoZkAswX67i/JTw== + dependencies: + "@ledgerhq/errors" "^4.78.0" + "@ledgerhq/logs" "^4.72.0" + rxjs "^6.5.3" + "@ledgerhq/errors@5.15.0", "@ledgerhq/errors@^5.15.0": version "5.15.0" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.15.0.tgz#e5d5b5ad48fc07f6308b78b065242e158bc044a2" integrity sha512-ZlLhR7qaChPgEbvcqOptRepWGm8VhhwOM6kC1gx3WErutbtaOjUX8lLA4ButWFU2f+xTl2rS/5c86wC7qGqGXQ== +"@ledgerhq/errors@^4.78.0": + version "4.78.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-4.78.0.tgz#23daf3af54d03b1bda3e616002b555da1bdb705a" + integrity sha512-FX6zHZeiNtegBvXabK6M5dJ+8OV8kQGGaGtuXDeK/Ss5EmG4Ltxc6Lnhe8hiHpm9pCHtktOsnUVL7IFBdHhYUg== + "@ledgerhq/hw-app-btc@^5.15.2": version "5.15.2" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-5.15.2.tgz#a1c8b5a16915c92d4d21ad5e2962b0310c4473e8" @@ -994,6 +1015,15 @@ "@ledgerhq/errors" "^5.15.0" events "^3.1.0" +"@ledgerhq/hw-transport@^4.77.0": + version "4.78.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-4.78.0.tgz#714786658e1f2fbc0569e06e2abf8d15d310d931" + integrity sha512-xQu16OMPQjFYLjqCysij+8sXtdWv2YLxPrB6FoLvEWGTlQ7yL1nUBRQyzyQtWIYqZd4THQowQmzm1VjxuN6SZw== + dependencies: + "@ledgerhq/devices" "^4.78.0" + "@ledgerhq/errors" "^4.78.0" + events "^3.0.0" + "@ledgerhq/ledger-core@^6.4.1": version "6.4.1" resolved "https://registry.yarnpkg.com/@ledgerhq/ledger-core/-/ledger-core-6.4.1.tgz#605ad7c887e0a93311e77d1ed10a653b2f90409c" @@ -1053,6 +1083,11 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.15.0.tgz#870524ade408b50ce74971509aa170e9d85c0575" integrity sha512-QuAva3K3YFDtQidi8xAfOQcb+aExJus3p0GhPNscOE+r152klBdiZUHLp818zEeQZT7PRSm83gEknmeUYjGU9A== +"@ledgerhq/logs@^4.72.0": + version "4.72.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-4.72.0.tgz#43df23af013ad1135407e5cf33ca6e4c4c7708d5" + integrity sha512-o+TYF8vBcyySRsb2kqBDv/KMeme8a2nwWoG+lAWzbDmWfb2/MrVWYCVYDYvjXdSoI/Cujqy1i0gIDrkdxa9chA== + "@octokit/auth-token@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.0.tgz#b64178975218b99e4dfe948253f0673cbbb59d9f" @@ -1510,6 +1545,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bech32@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.3.tgz#bd47a8986bbb3eec34a56a097a84b8d3e9a2dfcd" + integrity sha512-yuVFUvrNcoJi0sv5phmqc6P+Fl1HjRDRNOOkHY2X/3LBy2bIGNSFx4fZ95HMaXHupuS7cZR15AsvtmCIF4UEyg== + before-after-hook@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-1.4.0.tgz#2b6bf23dca4f32e628fd2747c10a37c74a4b484d" @@ -2407,7 +2447,7 @@ ethjs-util@^0.1.3: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -events@^3.1.0: +events@^3.0.0, events@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== @@ -4445,7 +4485,7 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -ripemd160@2, ripemd160@^2.0.0, ripemd160@^2.0.1: +ripemd160@2, ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== @@ -4578,7 +4618,7 @@ rxjs-compat@^6.5.5: resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.5.5.tgz#073c40510f29c45a2a5fc02dde87f8c3ad75f2c2" integrity sha512-F42sssVbUyWH4vJswEo6m+Eh02xHv3q93n8S7nUJO58R7sbc3CvJIOts605zdaBhWa1xMB9aVSyqPqhQ5q3eXg== -rxjs@^6.5.5: +rxjs@^6.5.3, rxjs@^6.5.5: version "6.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== diff --git a/scripts/sync-families-dispatch.sh b/scripts/sync-families-dispatch.sh index 2e8d58133b..f9a059736a 100755 --- a/scripts/sync-families-dispatch.sh +++ b/scripts/sync-families-dispatch.sh @@ -11,6 +11,7 @@ libcore-buildOperation.js \ libcore-buildSubAccounts.js \ libcore-getFeesForTransaction.js \ libcore-postSyncPatch.js \ +libcore-postBuildAccount.js \ libcore-getAccountNetworkInfo.js \ transaction.js \ bridge/js.js \ diff --git a/src/__tests__/__snapshots__/all.libcore.js.snap b/src/__tests__/__snapshots__/all.libcore.js.snap index c9ebc3d42b..d3762f26ea 100644 --- a/src/__tests__/__snapshots__/all.libcore.js.snap +++ b/src/__tests__/__snapshots__/all.libcore.js.snap @@ -13766,6 +13766,1788 @@ Array [ ] `; +exports[`cosmos currency bridge scanAccounts cosmos seed 1 1`] = ` +Array [ + Object { + "balance": "2175548", + "cosmosResources": Object { + "delegatedBalance": "2110238", + "unbondingBalance": "6000", + "withdrawAddress": "", + }, + "currencyId": "cosmos", + "derivationMode": "", + "freshAddress": "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + "freshAddressPath": "44'/118'/0'/0/0", + "freshAddresses": Array [ + Object { + "address": "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + "derivationPath": "44'/118'/0'/0/0", + }, + ], + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "index": 0, + "name": "Cosmos 1", + "operationsCount": 89, + "pendingOperations": Array [], + "seedIdentifier": "0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a", + "spendableBalance": "2175548", + "starred": false, + "unitMagnitude": 6, + "xpub": "cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4", + }, + Object { + "balance": "50", + "cosmosResources": Object { + "delegatedBalance": "0", + "unbondingBalance": "0", + "withdrawAddress": "", + }, + "currencyId": "cosmos", + "derivationMode": "", + "freshAddress": "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + "freshAddressPath": "44'/118'/1'/0/0", + "freshAddresses": Array [ + Object { + "address": "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + "derivationPath": "44'/118'/1'/0/0", + }, + ], + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "index": 1, + "name": "Cosmos 2", + "operationsCount": 10, + "pendingOperations": Array [], + "seedIdentifier": "0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a", + "spendableBalance": "50", + "starred": false, + "unitMagnitude": 6, + "xpub": "cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy", + }, + Object { + "balance": "0", + "cosmosResources": Object { + "delegatedBalance": "0", + "unbondingBalance": "0", + "withdrawAddress": "", + }, + "currencyId": "cosmos", + "derivationMode": "", + "freshAddress": "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + "freshAddressPath": "44'/118'/2'/0/0", + "freshAddresses": Array [ + Object { + "address": "cosmos1cgc696ay2pg6d4gcejek2y8la66j7e5y3c7kyw", + "derivationPath": "44'/118'/2'/0/0", + }, + ], + "id": "libcore:1:cosmos:cosmospub1addwnpepqw8lnqncgq428ervelgzq4sae9ey4d3aw9uu55rus92t2ftu04fqqx7dfgv:", + "index": 2, + "name": "Cosmos 3", + "operationsCount": 0, + "pendingOperations": Array [], + "seedIdentifier": "0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a", + "spendableBalance": "0", + "starred": false, + "unitMagnitude": 6, + "xpub": "cosmospub1addwnpepqw8lnqncgq428ervelgzq4sae9ey4d3aw9uu55rus92t2ftu04fqqx7dfgv", + }, +] +`; + +exports[`cosmos currency bridge scanAccounts cosmos seed 1 2`] = ` +Array [ + Array [ + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "1F09BD80C383BAA7F666EA2322966EC246B9D321B315AA9BCF048F0729AF5FCD-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-1F09BD80C383BAA7F666EA2322966EC246B9D321B315AA9BCF048F0729AF5FCD-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "888", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "1F09BD80C383BAA7F666EA2322966EC246B9D321B315AA9BCF048F0729AF5FCD-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-1F09BD80C383BAA7F666EA2322966EC246B9D321B315AA9BCF048F0729AF5FCD-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "220AF8A3A6BAD628B60285988A3F63CD7369755863F571B670533B5F74655F53-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-220AF8A3A6BAD628B60285988A3F63CD7369755863F571B670533B5F74655F53-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "1000000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "5000", + "hash": "220AF8A3A6BAD628B60285988A3F63CD7369755863F571B670533B5F74655F53-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-220AF8A3A6BAD628B60285988A3F63CD7369755863F571B670533B5F74655F53-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "5000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "255CFDAF101CFD78405EFC568BA3FCA0A48B02DB58805F79C3E31F433B631128-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-255CFDAF101CFD78405EFC568BA3FCA0A48B02DB58805F79C3E31F433B631128-0-OUT", + "recipients": Array [ + "cosmos19up3syt0rsd2wvndz2c8scxfjew0fegykq8v8v", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "255CFDAF101CFD78405EFC568BA3FCA0A48B02DB58805F79C3E31F433B631128-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-255CFDAF101CFD78405EFC568BA3FCA0A48B02DB58805F79C3E31F433B631128-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "25B9CB2414084A84525C37B0DADA25B20111262C2706263BC2B3FD30CF958C13-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-25B9CB2414084A84525C37B0DADA25B20111262C2706263BC2B3FD30CF958C13-0-OUT", + "recipients": Array [ + "cosmos19epaqpmrehhg4sg9aen42xhj34r6nppf2zc04d", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "90872", + "hash": "25B9CB2414084A84525C37B0DADA25B20111262C2706263BC2B3FD30CF958C13-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-25B9CB2414084A84525C37B0DADA25B20111262C2706263BC2B3FD30CF958C13-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "90872", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "3A1033395A67ACD9765F3CD1FAA1D61556CA807B7193E8B99B3EC739804288FD-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-3A1033395A67ACD9765F3CD1FAA1D61556CA807B7193E8B99B3EC739804288FD-0-OUT", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "1000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1131", + "hash": "3A1033395A67ACD9765F3CD1FAA1D61556CA807B7193E8B99B3EC739804288FD-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-3A1033395A67ACD9765F3CD1FAA1D61556CA807B7193E8B99B3EC739804288FD-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "1131", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "5BB4B7E7E8159AF6247F8ECFE6FD4ADDF96F29136452C3ECE1B78F2A73B46B7F-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-5BB4B7E7E8159AF6247F8ECFE6FD4ADDF96F29136452C3ECE1B78F2A73B46B7F-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "5000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "659416", + "hash": "5BB4B7E7E8159AF6247F8ECFE6FD4ADDF96F29136452C3ECE1B78F2A73B46B7F-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-5BB4B7E7E8159AF6247F8ECFE6FD4ADDF96F29136452C3ECE1B78F2A73B46B7F-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "659416", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "625F4AF5A59BF457082BE1159998C6DE55446C3DEE4962270E6758B7E3548A55-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-625F4AF5A59BF457082BE1159998C6DE55446C3DEE4962270E6758B7E3548A55-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1914", + "hash": "625F4AF5A59BF457082BE1159998C6DE55446C3DEE4962270E6758B7E3548A55-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-625F4AF5A59BF457082BE1159998C6DE55446C3DEE4962270E6758B7E3548A55-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "1914", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "66C2630A7D26D1FB50E57F3FBC0648CF4EF0BB5833CD7678ECF04E24169633D1-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-66C2630A7D26D1FB50E57F3FBC0648CF4EF0BB5833CD7678ECF04E24169633D1-0-OUT", + "recipients": Array [ + "cosmos19epaqpmrehhg4sg9aen42xhj34r6nppf2zc04d", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "11000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "66C2630A7D26D1FB50E57F3FBC0648CF4EF0BB5833CD7678ECF04E24169633D1-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-66C2630A7D26D1FB50E57F3FBC0648CF4EF0BB5833CD7678ECF04E24169633D1-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "6858E0874AA1DD5CF9FB6DAE6CD27A3C495C68939B3565FFC4FF3BD282F25BD5-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-6858E0874AA1DD5CF9FB6DAE6CD27A3C495C68939B3565FFC4FF3BD282F25BD5-0-OUT", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1273", + "hash": "6858E0874AA1DD5CF9FB6DAE6CD27A3C495C68939B3565FFC4FF3BD282F25BD5-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-6858E0874AA1DD5CF9FB6DAE6CD27A3C495C68939B3565FFC4FF3BD282F25BD5-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "1273", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "72B2067628E8A2805713D247C3F526DD7663DDA6D676F0DA8C1182CD7DC004EB-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-72B2067628E8A2805713D247C3F526DD7663DDA6D676F0DA8C1182CD7DC004EB-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "5000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "72B2067628E8A2805713D247C3F526DD7663DDA6D676F0DA8C1182CD7DC004EB-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-72B2067628E8A2805713D247C3F526DD7663DDA6D676F0DA8C1182CD7DC004EB-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "7560CECEB26CDAB9C9C6BEB89B61AEC6C3952D9BC479373F2D2BECC7F6A3172E-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-7560CECEB26CDAB9C9C6BEB89B61AEC6C3952D9BC479373F2D2BECC7F6A3172E-0-OUT", + "recipients": Array [ + "cosmos19epaqpmrehhg4sg9aen42xhj34r6nppf2zc04d", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "7560CECEB26CDAB9C9C6BEB89B61AEC6C3952D9BC479373F2D2BECC7F6A3172E-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-7560CECEB26CDAB9C9C6BEB89B61AEC6C3952D9BC479373F2D2BECC7F6A3172E-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "7D3E7F850C23D6E9108102125D31EC589FBF6D486A675F43780BE41227CD052E-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-7D3E7F850C23D6E9108102125D31EC589FBF6D486A675F43780BE41227CD052E-0-IN", + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1p45t493487wfew26mayzktn3d8ydmkjfl6pchj", + ], + "type": "IN", + "value": "1095000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "7D3E7F850C23D6E9108102125D31EC589FBF6D486A675F43780BE41227CD052E-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-7D3E7F850C23D6E9108102125D31EC589FBF6D486A675F43780BE41227CD052E-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1p45t493487wfew26mayzktn3d8ydmkjfl6pchj", + ], + "type": "FEES", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "7DD69A2BE9C89FFD38882C4BC50659767998E13883C5205B4CE7DBE34E7FA0F8-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-7DD69A2BE9C89FFD38882C4BC50659767998E13883C5205B4CE7DBE34E7FA0F8-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "20000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "7DD69A2BE9C89FFD38882C4BC50659767998E13883C5205B4CE7DBE34E7FA0F8-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-7DD69A2BE9C89FFD38882C4BC50659767998E13883C5205B4CE7DBE34E7FA0F8-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "7F4C075C3AFF343376912E5F5BCF5A41EE65709D824B2D58D6D74EB29112023C-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-7F4C075C3AFF343376912E5F5BCF5A41EE65709D824B2D58D6D74EB29112023C-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "1850", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "5000", + "hash": "7F4C075C3AFF343376912E5F5BCF5A41EE65709D824B2D58D6D74EB29112023C-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-7F4C075C3AFF343376912E5F5BCF5A41EE65709D824B2D58D6D74EB29112023C-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "5000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "8644487ACD66EA1053B4A18788BA519B04B41D552DCF64B40DE889E8BA9108FA-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-8644487ACD66EA1053B4A18788BA519B04B41D552DCF64B40DE889E8BA9108FA-0-OUT", + "recipients": Array [ + "cosmos19up3syt0rsd2wvndz2c8scxfjew0fegykq8v8v", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "777", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "8644487ACD66EA1053B4A18788BA519B04B41D552DCF64B40DE889E8BA9108FA-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-8644487ACD66EA1053B4A18788BA519B04B41D552DCF64B40DE889E8BA9108FA-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "871B42A3A4336B50589018A327014E7DBA7184EEA8F7256F817A512B4EDB2744-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-871B42A3A4336B50589018A327014E7DBA7184EEA8F7256F817A512B4EDB2744-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "5000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "871B42A3A4336B50589018A327014E7DBA7184EEA8F7256F817A512B4EDB2744-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-871B42A3A4336B50589018A327014E7DBA7184EEA8F7256F817A512B4EDB2744-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "8CA59762E9684968A58BD29B8101351DAAEA353CE03E31C3E1D26748B6DF60F1-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-8CA59762E9684968A58BD29B8101351DAAEA353CE03E31C3E1D26748B6DF60F1-0-OUT", + "recipients": Array [ + "cosmos19epaqpmrehhg4sg9aen42xhj34r6nppf2zc04d", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "1000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "8CA59762E9684968A58BD29B8101351DAAEA353CE03E31C3E1D26748B6DF60F1-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-8CA59762E9684968A58BD29B8101351DAAEA353CE03E31C3E1D26748B6DF60F1-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "9169B2D1259FFFE02B7091E57396FCF2382E1A3ADCD8FBCE0683BEFACED27C1C-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-9169B2D1259FFFE02B7091E57396FCF2382E1A3ADCD8FBCE0683BEFACED27C1C-0-IN", + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1p45t493487wfew26mayzktn3d8ydmkjfl6pchj", + ], + "type": "IN", + "value": "11000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "433", + "hash": "9169B2D1259FFFE02B7091E57396FCF2382E1A3ADCD8FBCE0683BEFACED27C1C-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-9169B2D1259FFFE02B7091E57396FCF2382E1A3ADCD8FBCE0683BEFACED27C1C-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1p45t493487wfew26mayzktn3d8ydmkjfl6pchj", + ], + "type": "FEES", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "946924B71965D66BC0C9FD3A94091B9D029BD32099B1EA8D7B9990AA9DF9DAC0-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-946924B71965D66BC0C9FD3A94091B9D029BD32099B1EA8D7B9990AA9DF9DAC0-0-OUT", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1129", + "hash": "946924B71965D66BC0C9FD3A94091B9D029BD32099B1EA8D7B9990AA9DF9DAC0-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-946924B71965D66BC0C9FD3A94091B9D029BD32099B1EA8D7B9990AA9DF9DAC0-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "1129", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "99B318553EE737402D60ABDD17FDA9CE8572920F7420EE3AD4A860E474FAD4FF-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-99B318553EE737402D60ABDD17FDA9CE8572920F7420EE3AD4A860E474FAD4FF-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "1000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "99B318553EE737402D60ABDD17FDA9CE8572920F7420EE3AD4A860E474FAD4FF-1", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-99B318553EE737402D60ABDD17FDA9CE8572920F7420EE3AD4A860E474FAD4FF-1-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "2000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "5786", + "hash": "99B318553EE737402D60ABDD17FDA9CE8572920F7420EE3AD4A860E474FAD4FF-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-99B318553EE737402D60ABDD17FDA9CE8572920F7420EE3AD4A860E474FAD4FF-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "5786", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "A13D4A86B72561C97BE62C8824F86FF2E90B10ACE4D133463357CD38F6A81262-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-A13D4A86B72561C97BE62C8824F86FF2E90B10ACE4D133463357CD38F6A81262-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "70000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "A13D4A86B72561C97BE62C8824F86FF2E90B10ACE4D133463357CD38F6A81262-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-A13D4A86B72561C97BE62C8824F86FF2E90B10ACE4D133463357CD38F6A81262-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "A1822C7792FA943DF699F215DE271643D1BB4DE932AF108CE5A843CAA46162AE-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-A1822C7792FA943DF699F215DE271643D1BB4DE932AF108CE5A843CAA46162AE-0-OUT", + "recipients": Array [ + "cosmos10l6h3qw05u7qduqgafj8wlrx3fjhr8523sm0c3", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "500000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "A1822C7792FA943DF699F215DE271643D1BB4DE932AF108CE5A843CAA46162AE-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-A1822C7792FA943DF699F215DE271643D1BB4DE932AF108CE5A843CAA46162AE-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "AB4337B3B51B2728F408ABBFF3FF0ED7EF26E6F24EE5EC8A2CFAA3F1D0CE4851-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-AB4337B3B51B2728F408ABBFF3FF0ED7EF26E6F24EE5EC8A2CFAA3F1D0CE4851-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "1000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "AB4337B3B51B2728F408ABBFF3FF0ED7EF26E6F24EE5EC8A2CFAA3F1D0CE4851-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-AB4337B3B51B2728F408ABBFF3FF0ED7EF26E6F24EE5EC8A2CFAA3F1D0CE4851-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "B280B1F66895EDFD2783542AAF96071F1D9C60F4A3B639EFCE5B7DA37D6ED40A-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B280B1F66895EDFD2783542AAF96071F1D9C60F4A3B639EFCE5B7DA37D6ED40A-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "1000000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "5000", + "hash": "B280B1F66895EDFD2783542AAF96071F1D9C60F4A3B639EFCE5B7DA37D6ED40A-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B280B1F66895EDFD2783542AAF96071F1D9C60F4A3B639EFCE5B7DA37D6ED40A-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "5000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "B30600B75F1E6866ACFAED34A5CE880C87E6D47D846A396E2CA167A723B7FA4B-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B30600B75F1E6866ACFAED34A5CE880C87E6D47D846A396E2CA167A723B7FA4B-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "150000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "B30600B75F1E6866ACFAED34A5CE880C87E6D47D846A396E2CA167A723B7FA4B-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B30600B75F1E6866ACFAED34A5CE880C87E6D47D846A396E2CA167A723B7FA4B-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "B3DCF4FC3098DB1B75605ADB0089BC2893AC7659398627363BDD15DF401487E4-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B3DCF4FC3098DB1B75605ADB0089BC2893AC7659398627363BDD15DF401487E4-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "2850", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "B3DCF4FC3098DB1B75605ADB0089BC2893AC7659398627363BDD15DF401487E4-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B3DCF4FC3098DB1B75605ADB0089BC2893AC7659398627363BDD15DF401487E4-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "B6437B68287D837329DED6EFE0182408D99A706682262545AF918BEB429E2492-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B6437B68287D837329DED6EFE0182408D99A706682262545AF918BEB429E2492-0-IN", + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos19up3syt0rsd2wvndz2c8scxfjew0fegykq8v8v", + ], + "type": "IN", + "value": "3000000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "B6437B68287D837329DED6EFE0182408D99A706682262545AF918BEB429E2492-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B6437B68287D837329DED6EFE0182408D99A706682262545AF918BEB429E2492-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos19up3syt0rsd2wvndz2c8scxfjew0fegykq8v8v", + ], + "type": "FEES", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "B7407516A37C9CA14134201AB6F0829063AE38FC041CA4F8D2F0993F5916F7D0-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B7407516A37C9CA14134201AB6F0829063AE38FC041CA4F8D2F0993F5916F7D0-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "2476", + "hash": "B7407516A37C9CA14134201AB6F0829063AE38FC041CA4F8D2F0993F5916F7D0-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B7407516A37C9CA14134201AB6F0829063AE38FC041CA4F8D2F0993F5916F7D0-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "2476", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "B77CED8542683276EA63B732E25FC1F5B11F67071F397AB0A0A15CC543B0C526-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B77CED8542683276EA63B732E25FC1F5B11F67071F397AB0A0A15CC543B0C526-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "200000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "B77CED8542683276EA63B732E25FC1F5B11F67071F397AB0A0A15CC543B0C526-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B77CED8542683276EA63B732E25FC1F5B11F67071F397AB0A0A15CC543B0C526-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "B90A4B05E428C4697499385424E0E537A0E157B61D15D952BECB72CB80FAB89F-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B90A4B05E428C4697499385424E0E537A0E157B61D15D952BECB72CB80FAB89F-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "10000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "22878", + "hash": "B90A4B05E428C4697499385424E0E537A0E157B61D15D952BECB72CB80FAB89F-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-B90A4B05E428C4697499385424E0E537A0E157B61D15D952BECB72CB80FAB89F-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "22878", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "BE207B65FBCA16D5C5469E8175916099B2CBBC3105FEB6C1FA4E56B9FD045999-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-BE207B65FBCA16D5C5469E8175916099B2CBBC3105FEB6C1FA4E56B9FD045999-0-OUT", + "recipients": Array [ + "cosmos1357tdmwl6uegc7dtpdkf8uxnk3arz2v7slc459", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "200000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "BE207B65FBCA16D5C5469E8175916099B2CBBC3105FEB6C1FA4E56B9FD045999-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-BE207B65FBCA16D5C5469E8175916099B2CBBC3105FEB6C1FA4E56B9FD045999-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "BF6D8323A7304A6CDE376888D33207934BEED3F8D632A688FF4E87CFBC9458C1-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-BF6D8323A7304A6CDE376888D33207934BEED3F8D632A688FF4E87CFBC9458C1-0-OUT", + "recipients": Array [ + "cosmos1neswla9vknec2hnz9dvapykp6r8478ucf59png", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "300", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1273", + "hash": "BF6D8323A7304A6CDE376888D33207934BEED3F8D632A688FF4E87CFBC9458C1-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-BF6D8323A7304A6CDE376888D33207934BEED3F8D632A688FF4E87CFBC9458C1-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "1273", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "C1ADE889DC9D950047579CC2B8257CFF95FD568B6435196E530D13D3DB0DC6ED-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-C1ADE889DC9D950047579CC2B8257CFF95FD568B6435196E530D13D3DB0DC6ED-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "10000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "291624", + "hash": "C1ADE889DC9D950047579CC2B8257CFF95FD568B6435196E530D13D3DB0DC6ED-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-C1ADE889DC9D950047579CC2B8257CFF95FD568B6435196E530D13D3DB0DC6ED-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "291624", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "C95A73C51F4A4430642FD258277CC01F701904C97CC3F2F99AAA356483906335-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-C95A73C51F4A4430642FD258277CC01F701904C97CC3F2F99AAA356483906335-0-OUT", + "recipients": Array [ + "cosmos19up3syt0rsd2wvndz2c8scxfjew0fegykq8v8v", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "C95A73C51F4A4430642FD258277CC01F701904C97CC3F2F99AAA356483906335-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-C95A73C51F4A4430642FD258277CC01F701904C97CC3F2F99AAA356483906335-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "CB953AC4860AE10E241A414C5D3BD801CF89DC50A3ADC9D4851511A6261E379C-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-CB953AC4860AE10E241A414C5D3BD801CF89DC50A3ADC9D4851511A6261E379C-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "200000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "CB953AC4860AE10E241A414C5D3BD801CF89DC50A3ADC9D4851511A6261E379C-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-CB953AC4860AE10E241A414C5D3BD801CF89DC50A3ADC9D4851511A6261E379C-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "D28E868EA328264D564B0D0085F0D9392DDFAB575B4A5140713D36B5EC83BDEC-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-D28E868EA328264D564B0D0085F0D9392DDFAB575B4A5140713D36B5EC83BDEC-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "500000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "D28E868EA328264D564B0D0085F0D9392DDFAB575B4A5140713D36B5EC83BDEC-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-D28E868EA328264D564B0D0085F0D9392DDFAB575B4A5140713D36B5EC83BDEC-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "D4034A92ACEF6D0C2DFC1BE2CF02D42C5A709504F580E04A268547E0940D93ED-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-D4034A92ACEF6D0C2DFC1BE2CF02D42C5A709504F580E04A268547E0940D93ED-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "6000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "D4034A92ACEF6D0C2DFC1BE2CF02D42C5A709504F580E04A268547E0940D93ED-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-D4034A92ACEF6D0C2DFC1BE2CF02D42C5A709504F580E04A268547E0940D93ED-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "DA4F650C9E93837430892DEF9656D72776B4F9322D4645AF14B6493A6C30183E-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-DA4F650C9E93837430892DEF9656D72776B4F9322D4645AF14B6493A6C30183E-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "500", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "DA4F650C9E93837430892DEF9656D72776B4F9322D4645AF14B6493A6C30183E-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-DA4F650C9E93837430892DEF9656D72776B4F9322D4645AF14B6493A6C30183E-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "E00E9D116F850DAE40A3D8DE36EBDF99EDC873025866C5C62CF086328BACC0F5-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-E00E9D116F850DAE40A3D8DE36EBDF99EDC873025866C5C62CF086328BACC0F5-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "50000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "E00E9D116F850DAE40A3D8DE36EBDF99EDC873025866C5C62CF086328BACC0F5-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-E00E9D116F850DAE40A3D8DE36EBDF99EDC873025866C5C62CF086328BACC0F5-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "E4492B702E3A25CCB989F138754B8D6D6A270E9D34C4D7E80D36D1090E60BD4D-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-E4492B702E3A25CCB989F138754B8D6D6A270E9D34C4D7E80D36D1090E60BD4D-0-OUT", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "10", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1129", + "hash": "E4492B702E3A25CCB989F138754B8D6D6A270E9D34C4D7E80D36D1090E60BD4D-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-E4492B702E3A25CCB989F138754B8D6D6A270E9D34C4D7E80D36D1090E60BD4D-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "1129", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "E97DD8E00D805D9425999C3EDFA412080846DE4E6C35132AFA1BE26A4307D692-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-E97DD8E00D805D9425999C3EDFA412080846DE4E6C35132AFA1BE26A4307D692-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "10000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "35764", + "hash": "E97DD8E00D805D9425999C3EDFA412080846DE4E6C35132AFA1BE26A4307D692-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-E97DD8E00D805D9425999C3EDFA412080846DE4E6C35132AFA1BE26A4307D692-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "35764", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "F2004D95EDBB9EF519C7C859EDF01D6CC905F22101EE5129ED5F3AFE3FCA27D3-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-F2004D95EDBB9EF519C7C859EDF01D6CC905F22101EE5129ED5F3AFE3FCA27D3-0-OUT", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1131", + "hash": "F2004D95EDBB9EF519C7C859EDF01D6CC905F22101EE5129ED5F3AFE3FCA27D3-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-F2004D95EDBB9EF519C7C859EDF01D6CC905F22101EE5129ED5F3AFE3FCA27D3-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "1131", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "F4A4A4EBB362ED11359A722954FDF15D7A9C893A9DD9F8AA978ECD8013572227-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-F4A4A4EBB362ED11359A722954FDF15D7A9C893A9DD9F8AA978ECD8013572227-0-NONE", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "NONE", + "value": "10000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "F4A4A4EBB362ED11359A722954FDF15D7A9C893A9DD9F8AA978ECD8013572227-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-F4A4A4EBB362ED11359A722954FDF15D7A9C893A9DD9F8AA978ECD8013572227-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "F9FDD8DF1F2F83F7233548DF26A509EDB5360B966FF1CA291B4C6CCB752A9464-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-F9FDD8DF1F2F83F7233548DF26A509EDB5360B966FF1CA291B4C6CCB752A9464-0-OUT", + "recipients": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "OUT", + "value": "10", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "3575", + "hash": "F9FDD8DF1F2F83F7233548DF26A509EDB5360B966FF1CA291B4C6CCB752A9464-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:-F9FDD8DF1F2F83F7233548DF26A509EDB5360B966FF1CA291B4C6CCB752A9464-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "3575", + }, + ], + Array [ + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "3A1033395A67ACD9765F3CD1FAA1D61556CA807B7193E8B99B3EC739804288FD-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-3A1033395A67ACD9765F3CD1FAA1D61556CA807B7193E8B99B3EC739804288FD-0-IN", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "IN", + "value": "1000", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1131", + "hash": "3A1033395A67ACD9765F3CD1FAA1D61556CA807B7193E8B99B3EC739804288FD-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-3A1033395A67ACD9765F3CD1FAA1D61556CA807B7193E8B99B3EC739804288FD-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "6858E0874AA1DD5CF9FB6DAE6CD27A3C495C68939B3565FFC4FF3BD282F25BD5-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-6858E0874AA1DD5CF9FB6DAE6CD27A3C495C68939B3565FFC4FF3BD282F25BD5-0-IN", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "IN", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1273", + "hash": "6858E0874AA1DD5CF9FB6DAE6CD27A3C495C68939B3565FFC4FF3BD282F25BD5-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-6858E0874AA1DD5CF9FB6DAE6CD27A3C495C68939B3565FFC4FF3BD282F25BD5-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "946924B71965D66BC0C9FD3A94091B9D029BD32099B1EA8D7B9990AA9DF9DAC0-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-946924B71965D66BC0C9FD3A94091B9D029BD32099B1EA8D7B9990AA9DF9DAC0-0-IN", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "IN", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1129", + "hash": "946924B71965D66BC0C9FD3A94091B9D029BD32099B1EA8D7B9990AA9DF9DAC0-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-946924B71965D66BC0C9FD3A94091B9D029BD32099B1EA8D7B9990AA9DF9DAC0-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "E4492B702E3A25CCB989F138754B8D6D6A270E9D34C4D7E80D36D1090E60BD4D-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-E4492B702E3A25CCB989F138754B8D6D6A270E9D34C4D7E80D36D1090E60BD4D-0-IN", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "IN", + "value": "10", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1129", + "hash": "E4492B702E3A25CCB989F138754B8D6D6A270E9D34C4D7E80D36D1090E60BD4D-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-E4492B702E3A25CCB989F138754B8D6D6A270E9D34C4D7E80D36D1090E60BD4D-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "0", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "0", + "hash": "F2004D95EDBB9EF519C7C859EDF01D6CC905F22101EE5129ED5F3AFE3FCA27D3-0", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-F2004D95EDBB9EF519C7C859EDF01D6CC905F22101EE5129ED5F3AFE3FCA27D3-0-IN", + "recipients": Array [ + "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "IN", + "value": "100", + }, + Object { + "accountId": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:", + "blockHash": null, + "blockHeight": 667957, + "extra": Object {}, + "fee": "1131", + "hash": "F2004D95EDBB9EF519C7C859EDF01D6CC905F22101EE5129ED5F3AFE3FCA27D3-fees", + "id": "libcore:1:cosmos:cosmospub1addwnpepqf3y4jpkjr277cneyugywe7k0xh0w0fa8j25f2lyyp436rzx8j207wd7ygy:-F2004D95EDBB9EF519C7C859EDF01D6CC905F22101EE5129ED5F3AFE3FCA27D3-fees-FEES", + "recipients": Array [ + "", + ], + "senders": Array [ + "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + ], + "type": "FEES", + "value": "0", + }, + ], + Array [], +] +`; + exports[`dash currency bridge scanAccounts dash seed 1 1`] = ` Array [ Object { diff --git a/src/__tests__/mock-bridges.js b/src/__tests__/mock-bridges.js index 0d5c4d0730..db064d219f 100644 --- a/src/__tests__/mock-bridges.js +++ b/src/__tests__/mock-bridges.js @@ -20,6 +20,7 @@ const mockedCoins = [ "ripple", "tezos", "stellar", + "cosmos", ]; mockedCoins.map(getCryptoCurrencyById).forEach((currency) => { diff --git a/src/__tests__/test-helpers/setup.js b/src/__tests__/test-helpers/setup.js index 134fbf65b4..0128e8b318 100644 --- a/src/__tests__/test-helpers/setup.js +++ b/src/__tests__/test-helpers/setup.js @@ -44,4 +44,5 @@ setSupportedCurrencies([ "ethereum_ropsten", "tron", "stellar", + "cosmos", ]); diff --git a/src/derivation.js b/src/derivation.js index d26296d032..aaa1bd25f1 100644 --- a/src/derivation.js +++ b/src/derivation.js @@ -34,6 +34,10 @@ const extraConfigPerCurrency: { [_: string]: LibcoreConfig } = { TEZOS_PROTOCOL_UPDATE: "TEZOS_PROTOCOL_UPDATE_BABYLON", TEZOS_NODE: () => getEnv("API_TEZOS_NODE"), }, + cosmos: { + BLOCKCHAIN_EXPLORER_API_ENDPOINT: () => + getEnv("API_COSMOS_BLOCKCHAIN_EXPLORER_API_ENDPOINT"), + }, }; const modes = Object.freeze({ diff --git a/src/env.js b/src/env.js index 1af572a68e..6d84678f2b 100644 --- a/src/env.js +++ b/src/env.js @@ -32,6 +32,11 @@ const stringParser = (v: mixed): ?string => typeof v === "string" ? v : undefined; const envDefinitions = { + API_COSMOS_BLOCKCHAIN_EXPLORER_API_ENDPOINT: { + def: "https://cosmos.coin-proxy.dev.aws.ledger.fr", + parser: stringParser, + desc: "node API for cosmos", + }, API_TEZOS_BAKER: { parser: stringParser, def: "https://tezos-bakers.api.live.ledger.com", @@ -70,6 +75,17 @@ const envDefinitions = { desc: "force implementation for ALL currency bridges (affects scanning accounts)", }, + COSMOS_GAS_AMPLIFIER: { + def: 2, + parser: intParser, + desc: "estimate gas multiplier", + }, + COSMOS_GAS_PRICE: { + def: 0.025, + parser: floatParser, + desc: + "gasLimit * gasPrice to determine the fees price. A too low GAS_PRICE will get rejected before the transaction is broadcast", + }, DEBUG_HTTP_RESPONSE: { def: false, parser: boolParser, diff --git a/src/errors.js b/src/errors.js index e21fb9c5fe..b963036c98 100644 --- a/src/errors.js +++ b/src/errors.js @@ -66,3 +66,30 @@ export const AccountAwaitingSendPendingOperations = createCustomErrorClass( ); export const SourceHasMultiSign = createCustomErrorClass("SourceHasMultiSign"); + +export const CosmosRedelegationInProgress = createCustomErrorClass( + "CosmosRedelegationInProgress" +); + +// Note : info of this code can be found here : +// https://github.com/cosmos/cosmos-sdk/blob/v0.37.9/types/errors.go#L28 +export const CosmosBroadcastError = { + "0": createCustomErrorClass("CosmosBroadcastCodeOK"), + "1": createCustomErrorClass("CosmosBroadcastCodeInternal"), + "2": createCustomErrorClass("CosmosBroadcastCodeTxDecode"), + "3": createCustomErrorClass("CosmosBroadcastCodeInvalidSequence"), + "4": createCustomErrorClass("CosmosBroadcastCodeUnauthorized"), + "5": createCustomErrorClass("CosmosBroadcastCodeInsufficientFunds"), + "6": createCustomErrorClass("CosmosBroadcastCodeUnknownRequest"), + "7": createCustomErrorClass("CosmosBroadcastCodeInvalidAddress"), + "8": createCustomErrorClass("CosmosBroadcastCodeInvalidPubKey"), + "9": createCustomErrorClass("CosmosBroadcastCodeUnknownAddress"), + "10": createCustomErrorClass(" CosmosBroadcastCodeInsufficientCoins"), + "11": createCustomErrorClass(" CosmosBroadcastCodeInvalidCoins"), + "12": createCustomErrorClass(" CosmosBroadcastCodeOutOfGas"), + "13": createCustomErrorClass(" CosmosBroadcastCodeMemoTooLarge"), + "14": createCustomErrorClass(" CosmosBroadcastCodeInsufficientFee"), + "15": createCustomErrorClass(" CosmosBroadcastCodeTooManySignatures"), + "16": createCustomErrorClass(" CosmosBroadcastCodeGasOverflow"), + "17": createCustomErrorClass(" CosmosBroadcastCodeNoSignatures"), +}; diff --git a/src/families/cosmos/app/common.js b/src/families/cosmos/app/common.js new file mode 100644 index 0000000000..f37870658b --- /dev/null +++ b/src/families/cosmos/app/common.js @@ -0,0 +1,83 @@ +export const CLA = 0x55; +export const CHUNK_SIZE = 250; +export const APP_KEY = "CSM"; + +export const INS = { + GET_VERSION: 0x00, + INS_PUBLIC_KEY_SECP256K1: 0x01, // Obsolete + SIGN_SECP256K1: 0x02, + GET_ADDR_SECP256K1: 0x04, +}; + +export const PAYLOAD_TYPE = { + INIT: 0x00, + ADD: 0x01, + LAST: 0x02, +}; + +const ERROR_DESCRIPTION = { + 1: "U2F: Unknown", + 2: "U2F: Bad request", + 3: "U2F: Configuration unsupported", + 4: "U2F: Device Ineligible", + 5: "U2F: Timeout", + 14: "Timeout", + 0x9000: "No errors", + 0x9001: "Device is busy", + 0x6802: "Error deriving keys", + 0x6400: "Execution Error", + 0x6700: "Wrong Length", + 0x6982: "Empty Buffer", + 0x6983: "Output buffer too small", + 0x6984: "Data is invalid", + 0x6985: "Conditions not satisfied", + 0x6986: "Transaction rejected", + 0x6a80: "Bad key handle", + 0x6b00: "Invalid P1/P2", + 0x6d00: "Instruction not supported", + 0x6e00: "Cosmos app does not seem to be open", + 0x6f00: "Unknown error", + 0x6f01: "Sign/verify error", +}; + +export function errorCodeToString(statusCode) { + if (statusCode in ERROR_DESCRIPTION) return ERROR_DESCRIPTION[statusCode]; + return `Unknown Status Code: ${statusCode}`; +} + +export function processErrorResponse(response) { + return { + return_code: response.statusCode, + error_message: errorCodeToString(response.statusCode), + }; +} + +export async function getVersion(transport) { + return transport.send(CLA, INS.GET_VERSION, 0, 0).then((response) => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + + let targetId = 0; + if (response.length >= 9) { + /* eslint-disable no-bitwise */ + targetId = + (response[5] << 24) + + (response[6] << 16) + + (response[7] << 8) + + (response[8] << 0); + /* eslint-enable no-bitwise */ + } + + return { + return_code: returnCode, + error_message: errorCodeToString(returnCode), + // /// + test_mode: response[0] !== 0, + major: response[1], + minor: response[2], + patch: response[3], + device_locked: response[4] === 1, + target_id: targetId.toString(16), + }; + }, processErrorResponse); +} diff --git a/src/families/cosmos/app/helperV1.js b/src/families/cosmos/app/helperV1.js new file mode 100644 index 0000000000..06c9f90d56 --- /dev/null +++ b/src/families/cosmos/app/helperV1.js @@ -0,0 +1,76 @@ +import { CLA, errorCodeToString, INS, processErrorResponse } from "./common"; + +export function serializePathv1(path) { + if (path.length > 10) { + throw new Error("Invalid path. Length should be <= 10"); + } + const buf = Buffer.alloc(1 + 4 * path.length); + buf.writeUInt8(path.length, 0); + for (let i = 0; i < path.length; i += 1) { + let v = path[i]; + if (i < 3) { + // eslint-disable-next-line no-bitwise + v |= 0x80000000; // Harden + } + buf.writeInt32LE(v, 1 + i * 4); + } + return buf; +} + +export async function signSendChunkv1(app, chunkIdx, chunkNum, chunk) { + return app.transport + .send(CLA, INS.SIGN_SECP256K1, chunkIdx, chunkNum, chunk, [ + 0x9000, + 0x6984, + 0x6a80, + ]) + .then((response) => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + let errorMessage = errorCodeToString(returnCode); + + if (returnCode === 0x6a80 || returnCode === 0x6984) { + errorMessage = `${errorMessage} : ${response + .slice(0, response.length - 2) + .toString("ascii")}`; + } + + let signature = null; + if (response.length > 2) { + signature = response.slice(0, response.length - 2); + } + + return { + signature, + return_code: returnCode, + error_message: errorMessage, + }; + }, processErrorResponse); +} + +function compressPublicKey(publicKey) { + if (publicKey.length !== 65) { + throw new Error("decompressed public key length should be 65 bytes"); + } + const y = publicKey.slice(33, 65); + // eslint-disable-next-line no-bitwise + const z = Buffer.from([2 + (y[y.length - 1] & 1)]); + return Buffer.concat([z, publicKey.slice(1, 33)]); +} + +export async function publicKeyv1(app, data) { + return app.transport + .send(CLA, INS.INS_PUBLIC_KEY_SECP256K1, 0, 0, data, [0x9000]) + .then((response) => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + const pk = Buffer.from(response.slice(0, 65)); + + return { + pk, + compressed_pk: compressPublicKey(pk), + return_code: returnCode, + error_message: errorCodeToString(returnCode), + }; + }, processErrorResponse); +} diff --git a/src/families/cosmos/app/helperV2.js b/src/families/cosmos/app/helperV2.js new file mode 100644 index 0000000000..5cc9cef766 --- /dev/null +++ b/src/families/cosmos/app/helperV2.js @@ -0,0 +1,53 @@ +import { signSendChunkv1 } from "./helperV1"; +import { + CLA, + errorCodeToString, + INS, + PAYLOAD_TYPE, + processErrorResponse, +} from "./common"; + +// I think this part is the only thing we really change from the ledger-cosmos-js +// https://github.com/gagbo/ledger-cosmos-js/pull/1/files +export function serializePathv2(path) { + const buf = Buffer.alloc(20); + // HACK : without the >>>, + // the bitwise implicitly casts the result to be a signed int32, + // which fails the internal type check of Buffer in case of overload. + buf.writeUInt32LE((0x80000000 | path[0]) >>> 0, 0); + buf.writeUInt32LE((0x80000000 | path[1]) >>> 0, 4); + buf.writeUInt32LE((0x80000000 | path[2]) >>> 0, 8); + buf.writeUInt32LE(path[3], 12); + buf.writeUInt32LE(path[4], 16); + + return buf; +} + +export async function signSendChunkv2(app, chunkIdx, chunkNum, chunk) { + let payloadType = PAYLOAD_TYPE.ADD; + if (chunkIdx === 1) { + payloadType = PAYLOAD_TYPE.INIT; + } + if (chunkIdx === chunkNum) { + payloadType = PAYLOAD_TYPE.LAST; + } + + return signSendChunkv1(app, payloadType, 0, chunk); +} + +export async function publicKeyv2(app, data) { + return app.transport + .send(CLA, INS.GET_ADDR_SECP256K1, 0, 0, data, [0x9000]) + .then((response) => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + const compressedPk = Buffer.from(response.slice(0, 33)); + + return { + pk: "OBSOLETE PROPERTY", + compressed_pk: compressedPk, + return_code: returnCode, + error_message: errorCodeToString(returnCode), + }; + }, processErrorResponse); +} diff --git a/src/families/cosmos/app/index.js b/src/families/cosmos/app/index.js new file mode 100644 index 0000000000..8bd2fd665b --- /dev/null +++ b/src/families/cosmos/app/index.js @@ -0,0 +1,280 @@ +/** ****************************************************************************** + * (c) 2019 ZondaX GmbH + * (c) 2016-2017 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ + +// const crypto = require("crypto"); +import { publicKeyv1, serializePathv1, signSendChunkv1 } from "./helperV1"; +import { publicKeyv2, serializePathv2, signSendChunkv2 } from "./helperV2"; +import { + APP_KEY, + CHUNK_SIZE, + CLA, + INS, + errorCodeToString, + getVersion, + processErrorResponse, +} from "./common"; + +export default class CosmosApp { + constructor(transport, scrambleKey = APP_KEY) { + if (!transport) { + throw new Error("Transport has not been defined"); + } + + this.transport = transport; + transport.decorateAppAPIMethods( + this, + ["getVersion", "sign", "getAddressAndPubKey", "appInfo", "deviceInfo"], + scrambleKey + ); + } + + static serializeHRP(hrp) { + if (hrp == null || hrp.length < 3 || hrp.length > 83) { + throw new Error("Invalid HRP"); + } + const buf = Buffer.alloc(1 + hrp.length); + buf.writeUInt8(hrp.length, 0); + buf.write(hrp, 1); + return buf; + } + + async serializePath(path) { + this.versionResponse = await getVersion(this.transport); + switch (this.versionResponse.major) { + case 1: + return serializePathv1(path); + case 2: + return serializePathv2(path); + default: + return { + return_code: 0x6400, + error_message: "App Version is not supported", + }; + } + } + + async signGetChunks(path, message) { + const serializedPath = await this.serializePath(path); + + const chunks = []; + chunks.push(serializedPath); + const buffer = Buffer.from(message); + + for (let i = 0; i < buffer.length; i += CHUNK_SIZE) { + let end = i + CHUNK_SIZE; + if (i > buffer.length) { + end = buffer.length; + } + chunks.push(buffer.slice(i, end)); + } + + return chunks; + } + + async getVersion() { + this.versionResponse = await getVersion(this.transport); + return this.versionResponse; + } + + async appInfo() { + return this.transport.send(0xb0, 0x01, 0, 0).then((response) => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + + const result = {}; + + let appName = "err"; + let appVersion = "err"; + let flagLen = 0; + let flagsValue = 0; + + if (response[0] !== 1) { + // Ledger responds with format ID 1. There is no spec for any format != 1 + result.error_message = "response format ID not recognized"; + result.return_code = 0x9001; + } else { + const appNameLen = response[1]; + appName = response.slice(2, 2 + appNameLen).toString("ascii"); + let idx = 2 + appNameLen; + const appVersionLen = response[idx]; + idx += 1; + appVersion = response.slice(idx, idx + appVersionLen).toString("ascii"); + idx += appVersionLen; + const appFlagsLen = response[idx]; + idx += 1; + flagLen = appFlagsLen; + flagsValue = response[idx]; + } + + return { + return_code: returnCode, + error_message: errorCodeToString(returnCode), + // // + appName, + appVersion, + flagLen, + flagsValue, + // eslint-disable-next-line no-bitwise + flag_recovery: (flagsValue & 1) !== 0, + // eslint-disable-next-line no-bitwise + flag_signed_mcu_code: (flagsValue & 2) !== 0, + // eslint-disable-next-line no-bitwise + flag_onboarded: (flagsValue & 4) !== 0, + // eslint-disable-next-line no-bitwise + flag_pin_validated: (flagsValue & 128) !== 0, + }; + }, processErrorResponse); + } + + async deviceInfo() { + return this.transport + .send(0xe0, 0x01, 0, 0, Buffer.from([]), [0x9000, 0x6e00]) + .then((response) => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + + if (returnCode === 0x6e00) { + return { + return_code: returnCode, + error_message: "This command is only available in the Dashboard", + }; + } + + const targetId = response.slice(0, 4).toString("hex"); + + let pos = 4; + const secureElementVersionLen = response[pos]; + pos += 1; + const seVersion = response + .slice(pos, pos + secureElementVersionLen) + .toString(); + pos += secureElementVersionLen; + + const flagsLen = response[pos]; + pos += 1; + const flag = response.slice(pos, pos + flagsLen).toString("hex"); + pos += flagsLen; + + const mcuVersionLen = response[pos]; + pos += 1; + // Patch issue in mcu version + let tmp = response.slice(pos, pos + mcuVersionLen); + if (tmp[mcuVersionLen - 1] === 0) { + tmp = response.slice(pos, pos + mcuVersionLen - 1); + } + const mcuVersion = tmp.toString(); + + return { + return_code: returnCode, + error_message: errorCodeToString(returnCode), + // // + targetId, + seVersion, + flag, + mcuVersion, + }; + }, processErrorResponse); + } + + async publicKey(path) { + const serializedPath = await this.serializePath(path); + + switch (this.versionResponse.major) { + case 1: + return publicKeyv1(this, serializedPath); + case 2: { + const data = Buffer.concat([ + CosmosApp.serializeHRP("cosmos"), + serializedPath, + ]); + return publicKeyv2(this, data); + } + default: + return { + return_code: 0x6400, + error_message: "App Version is not supported", + }; + } + } + + async getAddressAndPubKey(path, hrp, validateOnDevice) { + const serializedPath = await this.serializePath(path); + const data = Buffer.concat([CosmosApp.serializeHRP(hrp), serializedPath]); + return this.transport + .send(CLA, INS.GET_ADDR_SECP256K1, validateOnDevice ? 1 : 0, 0, data, [ + 0x9000, + ]) + .then((response) => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + + const compressedPk = Buffer.from(response.slice(0, 33)); + const bech32Address = Buffer.from(response.slice(33, -2)).toString(); + + return { + bech32_address: bech32Address, + compressed_pk: compressedPk, + return_code: returnCode, + error_message: errorCodeToString(returnCode), + }; + }, processErrorResponse); + } + + async signSendChunk(chunkIdx, chunkNum, chunk) { + switch (this.versionResponse.major) { + case 1: + return signSendChunkv1(this, chunkIdx, chunkNum, chunk); + case 2: + return signSendChunkv2(this, chunkIdx, chunkNum, chunk); + default: + return { + return_code: 0x6400, + error_message: "App Version is not supported", + }; + } + } + + async sign(path, message) { + const chunks = await this.signGetChunks(path, message); + + return this.signSendChunk(1, chunks.length, chunks[0], [0x9000]).then( + async (response) => { + let result = { + return_code: response.return_code, + error_message: response.error_message, + signature: null, + }; + + for (let i = 1; i < chunks.length; i += 1) { + // eslint-disable-next-line no-await-in-loop + result = await this.signSendChunk(1 + i, chunks.length, chunks[i]); + if (result.return_code !== 0x9000) { + break; + } + } + + return { + return_code: result.return_code, + error_message: result.error_message, + // /// + signature: result.signature, + }; + }, + processErrorResponse + ); + } +} diff --git a/src/families/cosmos/bridge/libcore.js b/src/families/cosmos/bridge/libcore.js new file mode 100644 index 0000000000..210737de95 --- /dev/null +++ b/src/families/cosmos/bridge/libcore.js @@ -0,0 +1,243 @@ +// @flow +import { scanAccounts } from "../../../libcore/scanAccounts"; +import { sync } from "../../../libcore/syncAccount"; +import type { AccountBridge, CurrencyBridge } from "../../../types"; +import type { Transaction } from "../types"; +import invariant from "invariant"; +import { BigNumber } from "bignumber.js"; +import broadcast from "../libcore-broadcast"; +import signOperation from "../libcore-signOperation"; +import { getMainAccount } from "../../../account"; +import { makeLRUCache } from "../../../cache"; +import { validateRecipient } from "../../../bridge/shared"; +import { getFeesForTransaction } from "../../../libcore/getFeesForTransaction"; +import { + NotEnoughBalance, + InvalidAddressBecauseDestinationIsAlsoSource, + InvalidAddress, +} from "@ledgerhq/errors"; +import { CosmosRedelegationInProgress } from "../../../errors"; +import { + setCosmosPreloadData, + asSafeCosmosPreloadData, +} from "../preloadedData"; +import { getValidators, hydrateValidators } from "../validators"; + +export const COSMOS_MAX_REDELEGATIONS = 6; +export const COSMOS_MAX_UNBONDINGS = 6; + +const calculateFees = makeLRUCache( + async (a, t) => { + return getFeesForTransaction({ + account: a, + transaction: t, + }); + }, + (a, t) => + `${a.id}_${t.amount.toString()}_${t.recipient}_${ + t.gasLimit ? t.gasLimit.toString() : "" + }_${t.fees ? t.fees.toString() : ""} + _${String(t.useAllAmount)}_${t.mode}_${ + t.validators ? t.validators.map((v) => v.address).join("-") : "" + }_${t.memo ? t.memo.toString() : ""}_${ + t.cosmosSourceValidator ? t.cosmosSourceValidator : "" + }` +); + +const createTransaction = () => ({ + family: "cosmos", + mode: "send", + amount: BigNumber(0), + fees: null, + gasLimit: null, + recipient: "", + useAllAmount: false, + networkInfo: null, + memo: null, + cosmosSourceValidator: null, + validators: [], +}); + +const updateTransaction = (t, patch) => ({ ...t, ...patch }); + +const redelegationStatusError = (a, t) => { + if (a.cosmosResources) { + const redelegations = a.cosmosResources.redelegations; + + invariant( + redelegations.length < COSMOS_MAX_REDELEGATIONS, + "redelegation should not have more than 6 entries" + ); + + for (let i = 0; redelegations.length < i; i++) { + let dstValidator = redelegations[i].validatorDstAddress; + if ( + dstValidator === t.cosmosSourceValidator && + redelegations[i].completionDate > new Date() + ) { + return new CosmosRedelegationInProgress(); + } + } + } + return null; +}; + +const getMaxEstimatedBalance = (a, t, estimatedFees) => { + const { cosmosResources } = a; + let blockBalance = BigNumber(0); + if (cosmosResources) { + blockBalance = cosmosResources.pendingRewardsBalance + .plus(cosmosResources.unbondingBalance) + .plus(cosmosResources.delegatedBalance); + } + + return a.balance.minus(estimatedFees).minus(blockBalance); +}; + +const getTransactionStatus = async (a, t) => { + const errors = {}; + const warnings = {}; + + 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" + ); + } else if ( + ["delegate", "claimReward", "claimRewardCompound"].includes(t.mode) + ) { + if ( + t.validators.some( + (v) => !v.address || !v.address.includes("cosmosvaloper") + ) + ) + errors.recipient = new InvalidAddress(null, { + currencyName: a.currency.name, + }); + } else { + if (a.freshAddress === t.recipient) { + errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource(); + } else { + const { recipientError, recipientWarning } = await validateRecipient( + a.currency, + t.recipient + ); + + if (recipientError) { + errors.recipient = recipientError; + } + + if (recipientWarning) { + warnings.recipient = recipientWarning; + } + } + } + + let estimatedFees = BigNumber(0); + let amount = t.amount; + + if (!errors.recipient) { + await calculateFees(a, t).then( + (res) => { + estimatedFees = res.estimatedFees; + }, + (error) => { + if (error.name === "NotEnoughBalance") { + errors.amount = error; + } else { + throw error; + } + } + ); + } + + if (!amount && t.mode !== "send") { + amount = t.validators.reduce( + (old, current) => old.plus(current.amount), + BigNumber(0) + ); + } + + let totalSpent = !t.useAllAmount + ? amount.plus(estimatedFees) + : getMaxEstimatedBalance(a, t, estimatedFees); + + if ( + !errors.recipient && + !errors.amount && + (amount.lt(0) || totalSpent.gt(a.balance)) + ) { + errors.amount = new NotEnoughBalance(); + totalSpent = BigNumber(0); + amount = BigNumber(0); + } + + return Promise.resolve({ + errors, + warnings, + estimatedFees, + amount, + totalSpent, + }); +}; + +const prepareTransaction = async (a, t) => { + return t; +}; + +const currencyBridge: CurrencyBridge = { + preload: async () => { + const validators = await getValidators(); + setCosmosPreloadData({ validators }); + return Promise.resolve({ validators }); + }, + hydrate: (data: mixed) => { + if (!data || typeof data !== "object") return; + const { validators } = data; + if ( + !validators || + typeof validators !== "object" || + !Array.isArray(validators) + ) + return; + hydrateValidators(validators); + setCosmosPreloadData(asSafeCosmosPreloadData(data)); + }, + scanAccounts, +}; + +const estimateMaxSpendable = async ({ + account, + parentAccount, + transaction, +}) => { + const mainAccount = getMainAccount(account, parentAccount); + const t = await prepareTransaction(mainAccount, { + ...createTransaction(), + recipient: "rHsMGQEkVNJmpGWs8XUBoTBiAAbwxZN5v3", // public testing seed abandonx11,about + ...transaction, + useAllAmount: true, + }); + const s = await getTransactionStatus(mainAccount, t); + return s.amount; +}; + +const accountBridge: AccountBridge = { + createTransaction, + updateTransaction, + prepareTransaction, + getTransactionStatus, + estimateMaxSpendable, + sync, + signOperation, + broadcast, +}; + +export default { currencyBridge, accountBridge }; diff --git a/src/families/cosmos/bridge/mock.js b/src/families/cosmos/bridge/mock.js index bbab0538c6..83d672b2ad 100644 --- a/src/families/cosmos/bridge/mock.js +++ b/src/families/cosmos/bridge/mock.js @@ -33,7 +33,7 @@ const createTransaction = (): Transaction => ({ fees: null, gasLimit: null, memo: null, - validators: null, + validators: [], cosmosSourceValidator: null, networkInfo: null, useAllAmount: false, diff --git a/src/families/cosmos/cli-transaction.js b/src/families/cosmos/cli-transaction.js new file mode 100644 index 0000000000..acff462a40 --- /dev/null +++ b/src/families/cosmos/cli-transaction.js @@ -0,0 +1,125 @@ +// @flow +import { from, Observable } from "rxjs"; +import { map } from "rxjs/operators"; +import invariant from "invariant"; +import flatMap from "lodash/flatMap"; +import zipWith from "lodash/zipWith"; +import { BigNumber } from "bignumber.js"; +import { getValidators } from "./validators"; +import type { Transaction, AccountLike } from "../../types"; +import type { CosmosDelegationInfo } from "./types"; + +const options = [ + { + name: "mode", + type: String, + desc: "mode of transaction: send, deletage, undelegate", + }, + { + name: "fees", + type: String, + desc: "how much fees", + }, + { + name: "gasLimit", + type: String, + desc: "how much gasLimit. default is estimated with the recipient", + }, + { + name: "memo", + type: String, + desc: "add a memo to a transaction", + }, + { + name: "cosmosSourceValidator", + type: String, + desc: "for redelegate, add a source validator", + }, + { + name: "cosmosValidator", + type: String, + multiple: true, + desc: "address of recipient validator that will receive the delegate", + }, + { + name: "cosmosAmountValidator", + type: String, + multiple: true, + desc: "Amount that the validator will receive", + }, +]; + +function inferTransactions( + transactions: Array<{ account: AccountLike, transaction: Transaction }>, + opts: Object, + { inferAmount }: * +): Transaction[] { + return flatMap(transactions, ({ transaction, account }) => { + invariant(transaction.family === "cosmos", "cosmos family"); + + const validatorsAddresses: string[] = opts["cosmosValidator"] || []; + const validatorsAmounts: BigNumber[] = ( + opts["cosmosAmountValidator"] || [] + ).map((value) => { + return inferAmount(account, value); + }); + + const validators: CosmosDelegationInfo[] = zipWith( + validatorsAddresses, + validatorsAmounts, + (address, amount) => ({ + address, + amount: amount || BigNumber(0), + }) + ); + + return { + ...transaction, + family: "cosmos", + mode: opts.mode || "send", + memo: opts.memo, + fees: opts.fees ? inferAmount(account, opts.fees) : null, + gasLimit: opts.gasLimit ? new BigNumber(opts.gasLimit) : null, + validators: validators, + cosmosSourceValidator: opts.cosmosSourceValidator, + }; + }); +} + +const cosmosValidatorsFormatters = { + json: (list) => JSON.stringify(list), + default: (list) => + list + .map( + (v) => + `${v.validatorAddress} "${v.name}" ${v.votingPower} ${v.commission} ${v.estimatedYearlyRewardsRate}` + ) + .join("\n"), +}; + +const cosmosValidators = { + args: [ + { + name: "format", + desc: Object.keys(cosmosValidatorsFormatters).join(" | "), + type: String, + }, + ], + job: ({ format }: $Shape<{ format: string }>): Observable => + from(getValidators()).pipe( + map((validators) => { + const f = + cosmosValidatorsFormatters[format] || + cosmosValidatorsFormatters.default; + return f(validators); + }) + ), +}; + +export default { + options, + inferTransactions, + commands: { + cosmosValidators, + }, +}; diff --git a/src/families/cosmos/hw-getAddress.js b/src/families/cosmos/hw-getAddress.js new file mode 100644 index 0000000000..e83e59d9cc --- /dev/null +++ b/src/families/cosmos/hw-getAddress.js @@ -0,0 +1,23 @@ +// @flow + +import type { Resolver } from "../../hw/getAddress/types"; +import CosmosApp from "./app"; +import BIPPath from "bip32-path"; + +const resolver: Resolver = async (transport, { path, verify }) => { + const cosmos = new CosmosApp(transport); + const bipPath = BIPPath.fromString(path).toPathArray(); + + const r = await cosmos.getAddressAndPubKey( + bipPath, + "cosmos", + verify || false + ); + return { + address: r.bech32_address, + publicKey: r.compressed_pk.toString("hex"), + path, + }; +}; + +export default resolver; diff --git a/src/families/cosmos/libcore-broadcast.js b/src/families/cosmos/libcore-broadcast.js new file mode 100644 index 0000000000..720ebf58c7 --- /dev/null +++ b/src/families/cosmos/libcore-broadcast.js @@ -0,0 +1,26 @@ +// @flow +import type { Operation } from "../../types"; +import type { CosmosBroadcastResponse } from "./types"; +import { makeBroadcast } from "../../libcore/broadcast"; +import { CosmosBroadcastError } from "../../errors"; + +async function broadcast({ + coreAccount, + signedOperation: { operation, signature }, +}): Promise { + const cosmosLikeAccount = await coreAccount.asCosmosLikeAccount(); + const res = await cosmosLikeAccount.broadcastRawTransaction(signature); + const parsed: CosmosBroadcastResponse = JSON.parse(res); + if (parsed.code) { + throw new CosmosBroadcastError[parsed.code](); + } + + // Note : 0 is the index of transaction because cosmos can contains 1 or more operations in a transaction + return { + ...operation, + hash: parsed.txhash, + id: `${operation.accountId}-${parsed.txhash}-0-${operation.type}`, + }; +} + +export default makeBroadcast({ broadcast }); diff --git a/src/families/cosmos/libcore-buildOperation.js b/src/families/cosmos/libcore-buildOperation.js new file mode 100644 index 0000000000..7cfff76d91 --- /dev/null +++ b/src/families/cosmos/libcore-buildOperation.js @@ -0,0 +1,26 @@ +// @flow + +import type { Operation } from "../../types"; +import type { CoreOperation } from "../../libcore/types"; + +async function cosmosBuildOperation({ + coreOperation, +}: { + coreOperation: CoreOperation, +}) { + const cosmosLikeOperation = await coreOperation.asCosmosLikeOperation(); + const cosmosLikeTransaction = await cosmosLikeOperation.getTransaction(); + const hash = await cosmosLikeTransaction.getHash(); + const message = await cosmosLikeOperation.getMessage(); + const out: $Shape = { + hash: `${hash}-${await message.getIndex()}`, + }; + + if (message && (await message.getRawMessageType()) === "internal/MsgFees") { + out.type = "FEES"; + } + + return out; +} + +export default cosmosBuildOperation; diff --git a/src/families/cosmos/libcore-buildTransaction.js b/src/families/cosmos/libcore-buildTransaction.js new file mode 100644 index 0000000000..508dcc9554 --- /dev/null +++ b/src/families/cosmos/libcore-buildTransaction.js @@ -0,0 +1,98 @@ +// @flow +import type { + Transaction, + CoreCosmosLikeTransaction, + CoreCosmosGasLimitRequest, +} from "./types"; +import type { Account } from "../../types"; +import type { Core, CoreAccount, CoreCurrency } from "../../libcore/types"; + +import { + bigNumberToLibcoreAmount, + libcoreBigIntToBigNumber, +} from "../../libcore/buildBigNumber"; +import { BigNumber } from "bignumber.js"; +import { cosmosCreateMessage } from "./message"; +import { getEnv } from "../../env"; +import { promiseAllBatched } from "../../promise"; + +export async function cosmosBuildTransaction({ + account, + core, + coreAccount, + coreCurrency, + transaction, + isCancelled, +}: { + account: Account, + core: Core, + coreAccount: CoreAccount, + coreCurrency: CoreCurrency, + transaction: Transaction, + isPartial: boolean, + isCancelled: () => boolean, +}): Promise { + const { fees, gasLimit, memo } = transaction; + + const cosmosLikeAccount = await coreAccount.asCosmosLikeAccount(); + if (isCancelled()) return; + + const transactionBuilder = await cosmosLikeAccount.buildTransaction(); + if (isCancelled()) return; + + const messages = await cosmosCreateMessage( + account.freshAddress, + transaction, + core + ); + + promiseAllBatched( + 3, + messages, + async (message) => await transactionBuilder.addMessage(message) + ); + + const memoTransaction = memo || ""; + await transactionBuilder.setMemo(memoTransaction); + + // Gas + let gas: BigNumber; + + if (gasLimit && gasLimit !== "0") { + gas = BigNumber(gasLimit); + } else { + const gasRequest: CoreCosmosGasLimitRequest = { + memo: memoTransaction, + amplifier: getEnv("COSMOS_GAS_AMPLIFIER"), + messages, + }; + gas = await libcoreBigIntToBigNumber( + await cosmosLikeAccount.estimateGas(gasRequest) + ); + } + const gasAmount = await bigNumberToLibcoreAmount(core, coreCurrency, gas); + if (isCancelled()) return; + + await transactionBuilder.setGas(gasAmount); + + const gasPrice = getEnv("COSMOS_GAS_PRICE"); + + const feesAmount = await bigNumberToLibcoreAmount( + core, + coreCurrency, + fees ? fees : gas.multipliedBy(gasPrice).integerValue(BigNumber.ROUND_CEIL) + ); + if (isCancelled()) return; + await transactionBuilder.setFee(feesAmount); + + // Signature information + const seq = await cosmosLikeAccount.getSequence(); + const accNum = await cosmosLikeAccount.getAccountNumber(); + + await transactionBuilder.setAccountNumber(accNum); + await transactionBuilder.setSequence(seq); + + return await transactionBuilder.build(); +} + +export default cosmosBuildTransaction; diff --git a/src/families/cosmos/libcore-getFeesForTransaction.js b/src/families/cosmos/libcore-getFeesForTransaction.js new file mode 100644 index 0000000000..7a2858f944 --- /dev/null +++ b/src/families/cosmos/libcore-getFeesForTransaction.js @@ -0,0 +1,24 @@ +// @flow + +import type { Account } from "../../types"; +import type { Core, CoreCurrency, CoreAccount } from "../../libcore/types"; +import type { Transaction } from "./types"; +import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; +import buildTransaction from "./libcore-buildTransaction"; + +async function cosmos(args: { + account: Account, + core: Core, + coreAccount: CoreAccount, + coreCurrency: CoreCurrency, + transaction: Transaction, + isPartial: boolean, + isCancelled: () => boolean, +}) { + const builded = await buildTransaction(args); + if (!builded) return; + const estimatedFees = await libcoreAmountToBigNumber(await builded.getFee()); + return { estimatedFees }; +} + +export default cosmos; diff --git a/src/families/cosmos/libcore-postBuildAccount.js b/src/families/cosmos/libcore-postBuildAccount.js new file mode 100644 index 0000000000..6da66e813e --- /dev/null +++ b/src/families/cosmos/libcore-postBuildAccount.js @@ -0,0 +1,142 @@ +// @flow +import type { Account } from "../../types"; +import type { CoreAccount } from "../../libcore/types"; +import type { CosmosResources, CoreCosmosLikeAccount } from "./types"; +import { BigNumber } from "bignumber.js"; +import { log } from "@ledgerhq/logs"; +import { + libcoreAmountToBigNumber, + libcoreBigIntToBigNumber, +} from "../../libcore/buildBigNumber"; +import { promiseAllBatched } from "../../promise"; + +const getValidatorStatus = async ( + cosmosAccount: CoreCosmosLikeAccount, + address +) => { + const status = ["unbonded", "unbonding", "bonded"]; + const validatorInfo = await cosmosAccount.getValidatorInfo(address); + return status[await validatorInfo.getActiveStatus()]; +}; + +const getFlattenDelegation = async (cosmosAccount) => { + const delegations = await cosmosAccount.getDelegations(); + const pendingRewards = await cosmosAccount.getPendingRewards(); + + return await promiseAllBatched(10, delegations, async (delegation) => { + const validatorAddress = await delegation.getValidatorAddress(); + + let reward; + for (let i = 0; i < pendingRewards.length; i++) { + if ( + (await pendingRewards[i].getValidatorAddress()) === validatorAddress + ) { + reward = await pendingRewards[i].getRewardAmount(); + break; + } + } + + return { + amount: await libcoreAmountToBigNumber( + await delegation.getDelegatedAmount() + ), + validatorAddress, + pendingRewards: reward + ? await libcoreAmountToBigNumber(reward) + : BigNumber(0), + status: await getValidatorStatus(cosmosAccount, validatorAddress), + }; + }); +}; + +const getFlattenRedelegations = async (cosmosAccount) => { + const redelegations = await cosmosAccount.getRedelegations(); + + const toFlatten = await promiseAllBatched( + 3, + redelegations, + async (redelegation) => + await promiseAllBatched( + 3, + await redelegation.getEntries(), + async (entry) => ({ + validatorSrcAddress: await redelegation.getSrcValidatorAddress(), + validatorDstAddress: await redelegation.getDstValidatorAddress(), + amount: await libcoreBigIntToBigNumber( + await entry.getInitialBalance() + ), + completionDate: await entry.getCompletionTime(), + }) + ) + ); + + return toFlatten.reduce((old, current) => [...old, ...current], []); +}; + +const getFlattenUnbonding = async (cosmosAccount) => { + const unbondings = await cosmosAccount.getUnbondings(); + + const toFlatten = await promiseAllBatched( + 3, + unbondings, + async (unbonding) => + await promiseAllBatched( + 3, + await unbonding.getEntries(), + async (entry) => ({ + validatorAddress: await unbonding.getValidatorAddress(), + amount: await libcoreBigIntToBigNumber( + await entry.getInitialBalance() + ), + completionDate: await entry.getCompletionTime(), + }) + ) + ); + + return toFlatten.reduce((old, current) => [...old, ...current], []); +}; + +const getCosmosResources = async ( + account: Account, + coreAccount +): Promise => { + const cosmosAccount = await coreAccount.asCosmosLikeAccount(); + const flattenDelegation = await getFlattenDelegation(cosmosAccount); + const flattenUnbonding = await getFlattenUnbonding(cosmosAccount); + const flattenRedelegation = await getFlattenRedelegations(cosmosAccount); + const res = { + delegations: flattenDelegation, + redelegations: flattenRedelegation, + unbondings: flattenUnbonding, + delegatedBalance: flattenDelegation.reduce( + (old, current) => old.plus(current.amount), + BigNumber(0) + ), + pendingRewardsBalance: flattenDelegation.reduce( + (old, current) => old.plus(current.pendingRewards), + BigNumber(0) + ), + unbondingBalance: flattenUnbonding.reduce( + (old, current) => old.plus(current.amount), + BigNumber(0) + ), + withdrawAddress: "", + }; + + return res; +}; + +const postBuildAccount = async ({ + account, + coreAccount, +}: { + account: Account, + coreAccount: CoreAccount, +}): Promise => { + log("cosmos/post-buildAccount", "getCosmosResources"); + account.cosmosResources = await getCosmosResources(account, coreAccount); + log("cosmos/post-buildAccount", "getCosmosResources DONE"); + return account; +}; + +export default postBuildAccount; diff --git a/src/families/cosmos/libcore-signOperation.js b/src/families/cosmos/libcore-signOperation.js new file mode 100644 index 0000000000..5bf07949e9 --- /dev/null +++ b/src/families/cosmos/libcore-signOperation.js @@ -0,0 +1,84 @@ +// @flow + +import { makeSignOperation } from "../../libcore/signOperation"; +import buildTransaction from "./libcore-buildTransaction"; +import type { Transaction, CoreCosmosLikeTransaction } from "./types"; +import { libcoreAmountToBigNumber } from "../../libcore/buildBigNumber"; +import CosmosApp from "./app"; +import BIPPath from "bip32-path"; + +async function signTransaction({ + account: { freshAddressPath, balance, id, freshAddress }, + transport, + transaction, + coreTransaction, + isCancelled, + onDeviceSignatureGranted, + onDeviceSignatureRequested, +}) { + const hwApp = new CosmosApp(transport); + const serialized = await coreTransaction.serializeForSignature(); + + const bipPath = BIPPath.fromString(freshAddressPath).toPathArray(); + + onDeviceSignatureRequested(); + const { signature } = await hwApp.sign(bipPath, serialized); + onDeviceSignatureGranted(); + + await coreTransaction.setDERSignature(signature.toString("hex")); + if (isCancelled()) return; + + // Serialize the transaction to be broadcast + // @param mode The supported broadcast modes include + // "block"(return after tx commit), (https://docs.cosmos.network/master/basics/tx-lifecycle.html#commit) + // "sync"(return afer CheckTx), (https://docs.cosmos.network/master/basics/tx-lifecycle.html#types-of-checks) and + // "async"(return right away). + const hex = await coreTransaction.serializeForBroadcast("block"); + + if (isCancelled()) return; + + const feesRaw = await coreTransaction.getFee(); + if (isCancelled()) return; + + const fee = await libcoreAmountToBigNumber(feesRaw); + if (isCancelled()) return; + + const recipients = [transaction.recipient]; + if (isCancelled()) return; + + const senders = [freshAddress]; + if (isCancelled()) return; + + const type = + transaction.mode === "undelegate" + ? "UNDELEGATE" + : transaction.mode === "delegate" + ? "DELEGATE" + : "OUT"; + + const op = { + id: `${id}--${type}`, + hash: "", + type, + value: transaction.useAllAmount ? balance : transaction.amount.plus(fee), + fee, + blockHash: null, + blockHeight: null, + senders, + recipients, + accountId: id, + date: new Date(), + extra: {}, + }; + + return { + operation: op, + expirationDate: null, + signature: hex, + }; +} + +export default makeSignOperation({ + buildTransaction, + signTransaction, +}); diff --git a/src/families/cosmos/message.js b/src/families/cosmos/message.js new file mode 100644 index 0000000000..49562eb36e --- /dev/null +++ b/src/families/cosmos/message.js @@ -0,0 +1,119 @@ +// @flow + +import type { Transaction, CosmosMessage } from "./types"; +import type { Core } from "../../libcore/types"; +import { promiseAllBatched } from "../../promise"; + +export const cosmosCreateMessage = async ( + freshAddress: string, + transaction: Transaction, + core: Core +): Promise => { + const { recipient } = transaction; + + switch (transaction.mode) { + case "send": + return [ + await core.CosmosLikeMessage.wrapMsgSend({ + fromAddress: freshAddress, + toAddress: recipient, + amount: [{ amount: transaction.amount.toString(), denom: "uatom" }], + }), + ]; + + case "delegate": { + const { validators } = transaction; + if (!validators || validators.length === 0) { + throw new Error("no validators"); + } + return await promiseAllBatched( + 2, + validators, + async (validator) => + await core.CosmosLikeMessage.wrapMsgDelegate({ + delegatorAddress: freshAddress, + validatorAddress: validator.address, + amount: { amount: validator.amount.toString(), denom: "uatom" }, + }) + ); + } + + case "undelegate": { + const { validators } = transaction; + if (!validators || validators.length === 0) { + throw new Error("no validators"); + } + return await promiseAllBatched( + 2, + validators, + async (validator) => + await core.CosmosLikeMessage.wrapMsgUndelegate({ + delegatorAddress: freshAddress, + validatorAddress: validator.address, + amount: { amount: validator.amount.toString(), denom: "uatom" }, + }) + ); + } + + case "redelegate": { + const { cosmosSourceValidator } = transaction; + if (!cosmosSourceValidator) { + throw new Error("source validator is empty"); + } + const { validators } = transaction; + if (!validators || validators.length === 0) { + throw new Error("no validators"); + } + return await promiseAllBatched( + 2, + validators, + async (validator) => + await core.CosmosLikeMessage.wrapMsgBeginRedelegate({ + delegatorAddress: freshAddress, + validatorSourceAddress: cosmosSourceValidator, + validatorDestinationAddress: validator.address, + amount: { amount: validator.amount.toString(), denom: "uatom" }, + }) + ); + } + + case "claimReward": { + const { validators } = transaction; + if (!validators || validators.length === 0) { + throw new Error("no validators"); + } + return await promiseAllBatched( + 2, + validators, + async (validator) => + await core.CosmosLikeMessage.wrapMsgWithdrawDelegationReward({ + delegatorAddress: freshAddress, + validatorAddress: validator.address, + }) + ); + } + + case "claimRewardCompound": { + const { validators } = transaction; + if (!validators || validators.length === 0) { + throw new Error("no validators"); + } + return [ + ...(await promiseAllBatched(2, validators, async (validator) => { + return await core.CosmosLikeMessage.wrapMsgWithdrawDelegationReward({ + delegatorAddress: freshAddress, + validatorAddress: validator.address, + }); + })), + ...(await promiseAllBatched(2, validators, async (validator) => { + return await core.CosmosLikeMessage.wrapMsgDelegate({ + delegatorAddress: freshAddress, + validatorAddress: validator.address, + amount: { amount: validator.amount.toString(), denom: "uatom" }, + }); + })), + ]; + } + } + throw new Error(`unknown message : ${transaction.mode}`); +}; diff --git a/src/families/cosmos/mock.js b/src/families/cosmos/mock.js index dbda944009..152902eb85 100644 --- a/src/families/cosmos/mock.js +++ b/src/families/cosmos/mock.js @@ -13,7 +13,7 @@ const { validators } = preloadedData; function setCosmosResources( account: Account, delegations: CosmosDelegation[], - unboundingBalance: BigNumber = BigNumber(0) + unbondingBalance: BigNumber = BigNumber(0) ): Account { /** format cosmosResources given the new delegations */ account.cosmosResources = { @@ -26,10 +26,12 @@ function setCosmosResources( (sum, { pendingRewards }) => sum.plus(pendingRewards), BigNumber(0) ), - unboundingBalance: account.cosmosResources - ? account.cosmosResources.unboundingBalance.plus(unboundingBalance) - : unboundingBalance, + unbondingBalance: account.cosmosResources + ? account.cosmosResources.unbondingBalance.plus(unbondingBalance) + : unbondingBalance, withdrawAddress: account.id, + unbondings: [], + redelegations: [], }; return account; @@ -94,8 +96,10 @@ function addDelegationOperation(account: Account, rng: Prando): Account { delegations: [], delegatedBalance: BigNumber(0), pendingRewardsBalance: BigNumber(0), - unboundingBalance: BigNumber(0), + unbondingBalance: BigNumber(0), withdrawAddress: "", + unbondings: [], + redelegations: [], }; if (spendableBalance.isZero()) return account; @@ -166,8 +170,10 @@ function addRedelegationOperation(account: Account, rng: Prando): Account { delegations: [], delegatedBalance: BigNumber(0), pendingRewardsBalance: BigNumber(0), - unboundingBalance: BigNumber(0), + unbondingBalance: BigNumber(0), withdrawAddress: "", + unbondings: [], + redelegations: [], }; if (!cosmosResources.delegations.length) return account; @@ -232,8 +238,10 @@ function addClaimRewardsOperation(account: Account, rng: Prando): Account { delegations: [], delegatedBalance: BigNumber(0), pendingRewardsBalance: BigNumber(0), - unboundingBalance: BigNumber(0), + unbondingBalance: BigNumber(0), withdrawAddress: "", + unbondings: [], + redelegations: [], }; if (!cosmosResources.delegations.length) return account; @@ -289,8 +297,10 @@ function addUndelegationOperation(account: Account, rng: Prando): Account { delegations: [], delegatedBalance: BigNumber(0), pendingRewardsBalance: BigNumber(0), - unboundingBalance: BigNumber(0), + unbondingBalance: BigNumber(0), withdrawAddress: "", + unbondings: [], + redelegations: [], }; if (!cosmosResources.delegations.length) return account; @@ -364,11 +374,11 @@ function genAccountEnhanceOperations(account: Account, rng: Prando): Account { function postSyncAccount(account: Account): Account { const cosmosResources = account.cosmosResources || {}; const delegatedBalance = cosmosResources.delegatedBalance || BigNumber(0); - const unboundingBalance = cosmosResources.unboundingBalance || BigNumber(0); + const unbondingBalance = cosmosResources.unbondingBalance || BigNumber(0); account.spendableBalance = account.balance .minus(delegatedBalance) - .minus(unboundingBalance); + .minus(unbondingBalance); return account; } @@ -388,8 +398,10 @@ function postScanAccount( delegations: [], delegatedBalance: BigNumber(0), pendingRewardsBalance: BigNumber(0), - unboundingBalance: BigNumber(0), + unbondingBalance: BigNumber(0), withdrawAddress: account.id, + unbondings: [], + redelegations: [], }; account.operations = []; } diff --git a/src/families/cosmos/preloadedData.js b/src/families/cosmos/preloadedData.js index c5f0f92ae3..507a26d837 100644 --- a/src/families/cosmos/preloadedData.js +++ b/src/families/cosmos/preloadedData.js @@ -40,11 +40,8 @@ export function asSafeCosmosPreloadData(data: mixed): CosmosPreloadData { } } - // $FlowFixMe TODO more validation - const rewardsState = data.rewardsState; return { validators, - rewardsState, }; } diff --git a/src/families/cosmos/preloadedData.mock.js b/src/families/cosmos/preloadedData.mock.js index b9d7016a7d..14fec415b2 100644 --- a/src/families/cosmos/preloadedData.mock.js +++ b/src/families/cosmos/preloadedData.mock.js @@ -11,765 +11,877 @@ const data: CosmosPreloadData = { votingPower: 94068472660 / (0.7202 * 254624972017526), // Doing the tokens / bondedAmount division manually name: "\u771f\u672c\u806a&IOSG", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1qwl879nx9t6kef4supyazayf7vjhennyh568ys", votingPower: 5614121688371 / (0.7202 * 254624972017526), name: "Certus One", commission: 0.125, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1qs8tnw2t8l6amtzvdemnnsq9dzk0ag0z52uzay", votingPower: 1601669095336 / (0.7202 * 254624972017526), name: "Castlenode", commission: 0.09, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1qaa9zej9a0ge3ugpx3pxyx602lxh3ztqgfnp42", votingPower: 64315316294 / (0.7202 * 254624972017526), name: "CCN", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1pz0lfq40sa63n0wany3v95x3yvznc5gyf8u28w", votingPower: 255876025886 / (0.7202 * 254624972017526), name: "Cobo", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1pgsjyvkg3y2m7qas534zzdhsqsxqyph2jh3uck", votingPower: 460520445065 / (0.7202 * 254624972017526), name: "OneSixtyTwo", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvtfc9y5", votingPower: 1079514188336 / (0.7202 * 254624972017526), name: "WeStaking", commission: 0.03, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1pjmngrwcsatsuyy8m3qrunaun67sr9x7z5r2qs", votingPower: 654800077136 / (0.7202 * 254624972017526), name: "Cypher Core", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1p650epkdwj0jte6sjc3ep0n3wz6jc9ehh8jutg", votingPower: 111581429426 / (0.7202 * 254624972017526), name: "Cosmos Suisse", commission: 0.295, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1zqgheeawp7cmqk27dgyctd80rd8ryhqs6la9wc", votingPower: 685457020651 / (0.7202 * 254624972017526), name: "melea-\u25ee\ud83d\udc41\u25ed", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1rpgtz9pskr5geavkjz02caqmeep7cwwpv73axj", votingPower: 3993867332045 / (0.7202 * 254624972017526), name: "Blockpower", commission: 0.03, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1r8kyvg4me2upnvlk26n2ay0zd5t4jktna8hhxp", votingPower: 250002000000 / (0.7202 * 254624972017526), name: "noma", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1rwh0cxa72d3yle3r4l8gd7vyphrmjy2kpe4x72", votingPower: 5232494848288 / (0.7202 * 254624972017526), name: "SparkPool", commission: 0.04, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1rcp29q3hpd246n6qak7jluqep4v006cdsc2kkl", votingPower: 243735228951 / (0.7202 * 254624972017526), name: "2nd only to Certus One in GoS: in3s.com", commission: 1.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1y0us8xvsvfvqkk9c6nt5cfyu5au5tww2ztve7q", votingPower: 51573500874 / (0.7202 * 254624972017526), name: "Swiss Staking", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper19yy989ka5usws6gsd8vl94y7l6ssgdwsrnscjc", votingPower: 500001550260 / (0.7202 * 254624972017526), name: "OKEx Pool", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1998928nfs697ep5d825y5jah0nq9zrtd00yyj7", votingPower: 106330127188 / (0.7202 * 254624972017526), name: "HLT", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper199mlc7fr6ll5t54w7tts7f4s0cvnqgc59nmuxf", votingPower: 572547259593 / (0.7202 * 254624972017526), name: "Velocity V1", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper19v9ej55ataqrfl39v83pf4e0dm69u89rngf928", votingPower: 14367183527 / (0.7202 * 254624972017526), name: "blockscape", commission: 0.0999, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper19j2hd230c3hw6ds843yu8akc0xgvdvyuz9v02v", votingPower: 441495456408 / (0.7202 * 254624972017526), name: "syncnode", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper19kwwdw0j64xhrmgkz49l0lmu5uyujjayxakwsn", votingPower: 630478139847 / (0.7202 * 254624972017526), name: "Firmamint", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1xym2qygmr9vanpa0m7ndk3n0qxgey3ffzcyd5c", votingPower: 100666973548 / (0.7202 * 254624972017526), name: "\ud83d\udc21grant.fish", commission: 1.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1x8rr4hcf54nz6hfckyy2n05sxss54h8wz9puzg", votingPower: 938952777415 / (0.7202 * 254624972017526), name: "cosmosgbt", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1x88j7vp2xnw3zec8ur3g4waxycyz7m0mahdv3p", votingPower: 2037638416848 / (0.7202 * 254624972017526), name: "Staking Facilities", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1x065cjlgejk2p2la0029akfvdy52gtq9mm58ta", votingPower: 148298393037 / (0.7202 * 254624972017526), name: "MathWallet\u9ea6\u5b50\u94b1\u5305", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7", votingPower: 5365254622915 / (0.7202 * 254624972017526), name: "iqlusion", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1gdg6qqe5a3u483unqlqsnullja23g0xvqkxtk0", votingPower: 82601384989 / (0.7202 * 254624972017526), name: "zugerselfdelegation", commission: 1.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1fqzqejwkk898fcslw4z4eeqjzesynvrdfr5hte", votingPower: 412328725338 / (0.7202 * 254624972017526), name: "commercio.network", commission: 0.09, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ff0dw8kawsnxkrgj7p65kvw7jxxakyf8n583gx", votingPower: 686014025437 / (0.7202 * 254624972017526), name: "Compass", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1fhr7e04ct0zslmkzqt9smakg3sxrdve6ulclj2", votingPower: 1217854931595 / (0.7202 * 254624972017526), name: "Stakin by POS Bakerz", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper12w6tynmjzq4l8zdla3v4x0jt8lt4rcz5gk7zg2", votingPower: 3491807623168 / (0.7202 * 254624972017526), name: "Huobi Wallet", commission: 0.02, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper124maqmcqv8tquy764ktz7cu0gxnzfw54n3vww8", votingPower: 606078648690 / (0.7202 * 254624972017526), name: "Simply Staking", commission: 0.07, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1tflk30mq5vgqjdly92kkhhq3raev2hnz6eete3", votingPower: 165498729711 / (0.7202 * 254624972017526), name: "Everstake", commission: 0.03, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ttfytaf43nkytzp8hkfjfgjc693ky4t3y2n2ku", votingPower: 68002415747 / (0.7202 * 254624972017526), name: "StarCluster", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1te8nxpc2myjfrhaty0dnzdhs5ahdh5agzuym9v", votingPower: 3521459291389 / (0.7202 * 254624972017526), name: "CoinoneNode", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1vrg6ruw00lhszl4sjgwt5ldvl8z0f7pfp5va85", votingPower: 342969167135 / (0.7202 * 254624972017526), name: "SSSnodes", commission: 0.018, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1vf44d85es37hwl9f4h9gv0e064m0lla60j9luj", votingPower: 7294838666798 / (0.7202 * 254624972017526), name: "MultiChain Ventures", commission: 0.02, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1vjn3559ncztu87qj8v4ryasgny7vjfx7jhxzu6", votingPower: 6831636468 / (0.7202 * 254624972017526), name: "Anonstake", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1v5y0tg0jllvxf5c3afml8s3awue0ymju89frut", votingPower: 5856592267830 / (0.7202 * 254624972017526), name: "Zero Knowledge Validator (ZKV)", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1vk706z2tfnqhdg6jrkngyx7f463jq58nj0x7p7", votingPower: 29544367171 / (0.7202 * 254624972017526), name: "Public Payments", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1d0aup392g3enru7eash83sedqclaxvp7fzh6gk", votingPower: 191759397236 / (0.7202 * 254624972017526), name: "Stir", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1dse76yk5jmj85jsd77ewsczc4k3u4s7a870wtj", votingPower: 399992036873 / (0.7202 * 254624972017526), name: "gf.network", commission: 0.08, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1de7qx00pz2j6gn9k88ntxxylelkazfk3g8fgh9", votingPower: 235286250874 / (0.7202 * 254624972017526), name: "Cosmic Validator", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1d7ufwp2rgfj7s7pfw2q7vm2lc9txmr8vh77ztr", votingPower: 117924360855 / (0.7202 * 254624972017526), name: "Cybernetic Destiny", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1wp9jne5t3e4au7u8gfep90g59j0qdhpeqvlg7n", votingPower: 90066203985 / (0.7202 * 254624972017526), name: "Newroad Network", commission: 0.08, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1wtv0kp6ydt03edd8kyr5arr4f3yc52vp3u2x3u", votingPower: 353702000001 / (0.7202 * 254624972017526), name: "kytzu", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1wdrypwex63geqswmcy5qynv4w3z3dyef2qmyna", votingPower: 457219720569 / (0.7202 * 254624972017526), name: "Genesis Lab", commission: 0.07, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1w0494h0l4mneaq7ajkrcjvn73m2n04l87j2nst", votingPower: 96455451062 / (0.7202 * 254624972017526), name: "Angel", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1w42lm7zv55jrh5ggpecg0v643qeatfkd9aqf3f", votingPower: 757388512720 / (0.7202 * 254624972017526), name: "Mythos", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1we6knm8qartmmh2r0qfpsz6pq0s7emv3e0meuw", votingPower: 1823932422233 / (0.7202 * 254624972017526), name: "Staked", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1wauge4px27c257nfn4k3329wteddqw7gs3n66u", votingPower: 170766070827 / (0.7202 * 254624972017526), name: "DappPub", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper102ruvpv2srmunfffxavttxnhezln6fnc54at8c", votingPower: 419847884853 / (0.7202 * 254624972017526), name: "Ztake.org", commission: 0.07, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper10wz6lfhmqfw6egg0062ytnawaj6vr89ly5g4yg", votingPower: 85001650000 / (0.7202 * 254624972017526), name: "BitMax Staking", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1000ya26q2cmh399q4c5aaacd9lmmdqp90kw2jn", votingPower: 457836803479 / (0.7202 * 254624972017526), name: "Staking Fund", commission: 0.12, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper10nzaaeh2kq28t3nqsh5m8kmyv90vx7ym5mpakx", votingPower: 73145988986 / (0.7202 * 254624972017526), name: "Blockdaemon", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper10e4vsut6suau8tk9m6dnrm0slgd6npe3jx5xpv", votingPower: 3590589521416 / (0.7202 * 254624972017526), name: "B-Harvest", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", votingPower: 2277639587814 / (0.7202 * 254624972017526), name: "validator.network | Security first. Highly available.", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1sd4tl9aljmmezzudugs7zlaya7pg2895ws8tfs", votingPower: 1424894200299 / (0.7202 * 254624972017526), name: "InfStones (Infinity Stones)", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1s05va5d09xlq3et8mapsesqh6r5lqy7mkhwshm", votingPower: 557176831790 / (0.7202 * 254624972017526), name: "Wetez", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ssm0d433seakyak8kcf93yefhknjleeds4y3em", votingPower: 1515889266517 / (0.7202 * 254624972017526), name: "IRISnet-Bianjie", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0", votingPower: 12672613496348 / (0.7202 * 254624972017526), name: "\ud83d\udc20stake.fish", commission: 0.04, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1s6x9fy4wc49wj9jx4jv6czredqsmp46h7vnk2q", votingPower: 727981844212 / (0.7202 * 254624972017526), name: "SNZPool", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1s6t3wzx6mcv3pjg5fp2ddzplm3gj4pg6d330wg", votingPower: 245002260000 / (0.7202 * 254624972017526), name: "omega3", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1s65zmn32zugl57ysj47s7vmfcek0rtd7he7wde", votingPower: 194202250000 / (0.7202 * 254624972017526), name: "firstblock", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1s7jnk7t6yqzensdgpvkvkag022udk842qdjdtd", votingPower: 337798355546 / (0.7202 * 254624972017526), name: "Blockscale", commission: 0.25, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper132juzk0gdmwuxvx4phug7m3ymyatxlh9734g4w", votingPower: 2771968523134 / (0.7202 * 254624972017526), name: "P2P.ORG - P2P Validator", commission: 0.01, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper130mdu9a0etmeuw52qfxk73pn0ga6gawkxsrlwf", votingPower: 1127721623309 / (0.7202 * 254624972017526), name: "jackzampolin", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper13sduv92y3xdhy3rpmhakrc3v7t37e7ps9l0kpv", votingPower: 1574051576303 / (0.7202 * 254624972017526), name: "nylira.net", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper13ce7hhqa0z3tpc2l7jm0lcvwe073hdkkpp2nst", votingPower: 61729191815 / (0.7202 * 254624972017526), name: "RockX", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1jxv0u20scum4trha72c7ltfgfqef6nsch7q6cu", votingPower: 302293155457 / (0.7202 * 254624972017526), name: "Ping.pub", commission: 0.02, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1j0vaeh27t4rll7zhmarwcuq8xtrmvqhudrgcky", votingPower: 1072128285988 / (0.7202 * 254624972017526), name: "chainflow-cosmos-prodval-01", commission: 0.12, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1jlr62guqwrwkdt4m3y00zh2rrsamhjf9num5xr", votingPower: 723902889269 / (0.7202 * 254624972017526), name: "StakeWith.Us", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1n3f5lm7xtlrp05z9ud2xk2cnvk2xnzkm2he6er", votingPower: 16031529975 / (0.7202 * 254624972017526), name: "AirGap \ud83d\udee1", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1n5pu2rtz4e2skaeatcmlexza7kheedzh8a2680", votingPower: 576087977754 / (0.7202 * 254624972017526), name: "BlockMatrix \ud83d\ude80", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1nm0rrq86ucezaf8uj35pq9fpwr5r82clzyvtd8", votingPower: 145001000000 / (0.7202 * 254624972017526), name: "Cthulhu", commission: 1.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper15r4tc0m6hc7z8drq3dzlrtcs6rq2q9l2nvwher", votingPower: 731460206153 / (0.7202 * 254624972017526), name: "DragonStake", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper159eexl76jlygrxnfreehl3j9tt70d8wfnn39fw", votingPower: 12063850000 / (0.7202 * 254624972017526), name: "fishegg.net", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper156gqf9837u7d4c4678yt3rl4ls9c5vuursrrzf", votingPower: 9777967988806 / (0.7202 * 254624972017526), name: "Binance Staking", commission: 0.025, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper15urq2dtp9qce4fyc85m6upwm9xul3049e02707", votingPower: 4772542296164 / (0.7202 * 254624972017526), name: "Chorus One", commission: 0.075, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1402ggxz5u6vm29sqztwqq8vxs3ke6dmwl2z5dk", votingPower: 9876491249 / (0.7202 * 254624972017526), name: "Cosmoon", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper14kn0kk33szpwus9nh8n87fjel8djx0y070ymmj", votingPower: 2283034789245 / (0.7202 * 254624972017526), name: "Forbole", commission: 0.095, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper14k4pzckkre6uxxyd2lnhnpp8sngys9m6hl6ml7", votingPower: 11377195150426 / (0.7202 * 254624972017526), name: "Polychain Labs", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper146kwpzhmleafmhtaxulfptyhnvwxzlvm87hwnm", votingPower: 85876621420 / (0.7202 * 254624972017526), name: "\ud83c\udf10 KysenPool.io", commission: 0.079, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper14az9dmutwtz4vuycvae8csm4wwwtm0aumtlppe", votingPower: 1745837039221 / (0.7202 * 254624972017526), name: "F4RM", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper14l0fp639yudfl46zauvv8rkzjgd4u0zk2aseys", votingPower: 2122758592391 / (0.7202 * 254624972017526), name: "ATEAM", commission: 0.099, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper14lultfckehtszvzw4ehu0apvsr77afvyju5zzy", votingPower: 11208313955328 / (0.7202 * 254624972017526), name: "DokiaCapital", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1k9a0cs97vul8w2vwknlfmpez6prv8klv03lv3d", votingPower: 900577706145 / (0.7202 * 254624972017526), name: "Stake Capital", commission: 0.08, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1kgquh04ffqvadekf6e47070gskm0s0h28cl7ht", votingPower: 25070019650 / (0.7202 * 254624972017526), name: "tokenweb.io", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1kgddca7qj96z0qcxr2c45z73cfl0c75p7f3s2e", votingPower: 580104464471 / (0.7202 * 254624972017526), name: "ChainLayer", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ktecz4dr56j9tsfh7nwg8s9suvhfu70qykhu5s", votingPower: 7177032902 / (0.7202 * 254624972017526), name: "Dawns.World", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1kj0h4kn4z5xvedu2nd9c4a9a559wvpuvu0h6qn", votingPower: 1564696374061 / (0.7202 * 254624972017526), name: "Cryptium Labs", commission: 0.11, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1kn3wugetjuy4zetlq6wadchfhvu3x740ae6z6x", votingPower: 2186095379303 / (0.7202 * 254624972017526), name: "HuobiPool", commission: 0.04, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1hvsdf03tl6w5pnfvfv5g8uphjd4wfw2h4gvnl7", votingPower: 100004083306 / (0.7202 * 254624972017526), name: "Atom.Bi23", commission: 0.5, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1hjct6q7npsspsg3dgvzk3sdf89spmlpfdn6m9d", votingPower: 4535001052736 / (0.7202 * 254624972017526), name: "Figment Networks", commission: 0.09, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1crqm3598z6qmyn2kkcl9dz7uqs4qdqnr6s8jdn", votingPower: 448224981318 / (0.7202 * 254624972017526), name: "Bison Trails", commission: 0.08, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1cgh5ksjwy2sd407lyre4l3uj2fdrqhpkzp06e6", votingPower: 930334262431 / (0.7202 * 254624972017526), name: "HashQuark", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1clpqr4nrk4khgkxj78fcwwh6dl3uw4epsluffn", votingPower: 6035834449351 / (0.7202 * 254624972017526), name: "Cosmostation", commission: 0.12, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ey69r37gfxvxg62sh4r0ktpuc46pzjrm873ae8", votingPower: 10331638721447 / (0.7202 * 254624972017526), name: "Sikka", commission: 0.03, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1et77usu8q2hargvyusl4qzryev8x8t9wwqkxfs", votingPower: 7171716873 / (0.7202 * 254624972017526), name: "\ud83d\udc7ereplicator.network", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1e0plfg475phrsvrlzw8gwppeva0zk5yg9fgg8c", votingPower: 331888417159 / (0.7202 * 254624972017526), name: "Easy 2 Stake", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1e0jnq2sun3dzjh8p2xq95kk0expwmd7sj6x59m", votingPower: 115925233338 / (0.7202 * 254624972017526), name: "Fission Labs", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1eh5mwu044gd5ntkkc2xgfg8247mgc56fz4sdg3", votingPower: 2149942889528 / (0.7202 * 254624972017526), name: "BouBouNode", commission: 0.061, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ehkfl7palwrh6w2hhr2yfrgrq8jetgucudztfe", votingPower: 1177445517966 / (0.7202 * 254624972017526), name: "KalpaTech", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ec3p6a75mqwkv33zt543n6cnxqwun37rr5xlqv", votingPower: 1016351149259 / (0.7202 * 254624972017526), name: "lunamint", commission: 0.15, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1emaa7mwgpnpmc7yptm728ytp9quamsvu837nc0", votingPower: 525674038457 / (0.7202 * 254624972017526), name: "kochacolaj", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1eup5t8pp8jq354heck53qtama7vss9l354kh6r", votingPower: 10418995367 / (0.7202 * 254624972017526), name: "IZ0", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper16qme5yxucnaj6snx35nmwze0wyxr8wfgqxsqfw", votingPower: 330184377372 / (0.7202 * 254624972017526), name: "KIRA Staking", commission: 0.01, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper16v3f95amtvpewuajjcdsvaekuuy4yyzups85ec", votingPower: 181360488389 / (0.7202 * 254624972017526), name: "BlockPool", commission: 0.02, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk542junl7rsvq6", votingPower: 969517688431 / (0.7202 * 254624972017526), name: "Any Labs", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper16k579jk6yt2cwmqx9dz5xvq9fug2tekvlu9qdv", votingPower: 928468619631 / (0.7202 * 254624972017526), name: "Cephalopod Equipment", commission: 0.0811, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1mykn77lkynl8fkwvl9tqg369u0zajzzcdhkptq", votingPower: 90061196918 / (0.7202 * 254624972017526), name: "Nodeasy.com", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1m83cwjucw9nt8xm66u8xavvy6v9m7xfspcszc5", votingPower: 159702133794 / (0.7202 * 254624972017526), name: "Fenbushi US - Staked", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ma02nlc7lchu7caufyrrqt4r6v2mpsj90y9wzd", votingPower: 3451941483054 / (0.7202 * 254624972017526), name: "hashtower", commission: 0.03, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1uxh465053nq3at4dn0jywgwq3s9sme3la3drx6", votingPower: 520172159921 / (0.7202 * 254624972017526), name: "Bison Trails", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1uhnsxv6m83jj3328mhrql7yax3nge5svrv6t6c", votingPower: 720793765456 / (0.7202 * 254624972017526), name: "Skystar Capital", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1u6ddcsjueax884l3tfrs66497c7g86skn7pa0u", votingPower: 809489102763 / (0.7202 * 254624972017526), name: "Sentinel", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1uutuwrwt3z2a5z8z3uasml3rftlpmu25aga5c6", votingPower: 895225796893 / (0.7202 * 254624972017526), name: "Delega Networks\u267e ", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1ul2me6vukg2vac2p6ltxmqlaa7jywdgt8q76ag", votingPower: 1214088124061 / (0.7202 * 254624972017526), name: "HyperblocksPro", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1a3yjj7d3qnx4spgvjcwjq9cw9snrrrhu5h6jll", votingPower: 10028697349 / (0.7202 * 254624972017526), name: "Coinbase Custody", commission: 0.2, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjkfg0gh", votingPower: 571902934315 / (0.7202 * 254624972017526), name: "FRESHATOMS", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq", votingPower: 1079608204356 / (0.7202 * 254624972017526), name: "01node", commission: 0.1, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1l9fwl9c77zx850htsr20pq3ltc379xt86ndelm", votingPower: 10182710273 / (0.7202 * 254624972017526), name: "CosmosLink", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1lktjhnzkpkz3ehrg8psvmwhafg56kfss3q3t8m", votingPower: 1658474619720 / (0.7202 * 254624972017526), name: "Umbrella \u2614", commission: 0.0704, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1lcwxu50rvvgf9v6jy6q5mrzyhlszwtjxhtscmp", votingPower: 10418082544 / (0.7202 * 254624972017526), name: "stake.zone", commission: 0.0, + estimatedYearlyRewardsRate: 0, }, { validatorAddress: "cosmosvaloper1l6udzyaz8xaxv4hpagwauacm95jlcec3xlht2u", votingPower: 11107365900 / (0.7202 * 254624972017526), name: "StakeHouse", commission: 0.01, + estimatedYearlyRewardsRate: 0, }, ], - rewardsState: { - targetBondedRatio: 0.67, - communityPoolCommission: 0.02, - assumedTimePerBlock: 6.5, - inflationRateChange: 0.13, - inflationMaxRate: 0.2, - inflationMinRate: 0.07, - actualBondedRatio: 0.7202, - averageTimePerBlock: 7.5, - totalSupply: 254624972.017526, - averageDailyFees: 20, - currentValueInflation: 0.07, - }, }; export default data; diff --git a/src/families/cosmos/serialization.js b/src/families/cosmos/serialization.js index 8e98589f73..907a311c9f 100644 --- a/src/families/cosmos/serialization.js +++ b/src/families/cosmos/serialization.js @@ -8,8 +8,10 @@ export function toCosmosResourcesRaw(r: CosmosResources): CosmosResourcesRaw { delegatedBalance, delegations, pendingRewardsBalance, - unboundingBalance, + unbondingBalance, withdrawAddress, + redelegations, + unbondings, } = r; return { delegations: delegations.map( @@ -20,9 +22,29 @@ export function toCosmosResourcesRaw(r: CosmosResources): CosmosResourcesRaw { validatorAddress, }) ), + redelegations: redelegations.map( + ({ + amount, + completionDate, + validatorSrcAddress, + validatorDstAddress, + }) => ({ + amount: amount.toString(), + completionDate: completionDate.toString(), + validatorSrcAddress, + validatorDstAddress, + }) + ), + unbondings: unbondings.map( + ({ amount, completionDate, validatorAddress }) => ({ + amount: amount.toString(), + completionDate: completionDate.toString(), + validatorAddress, + }) + ), delegatedBalance: delegatedBalance.toString(), pendingRewardsBalance: pendingRewardsBalance.toString(), - unboundingBalance: unboundingBalance.toString(), + unbondingBalance: unbondingBalance.toString(), withdrawAddress, }; } @@ -32,8 +54,10 @@ export function fromCosmosResourcesRaw(r: CosmosResourcesRaw): CosmosResources { delegatedBalance, delegations, pendingRewardsBalance, - unboundingBalance, + redelegations, + unbondingBalance, withdrawAddress, + unbondings, } = r; return { delegations: delegations.map( @@ -44,9 +68,29 @@ export function fromCosmosResourcesRaw(r: CosmosResourcesRaw): CosmosResources { validatorAddress, }) ), + redelegations: redelegations.map( + ({ + amount, + completionDate, + validatorSrcAddress, + validatorDstAddress, + }) => ({ + amount: BigNumber(amount), + completionDate: new Date(completionDate), + validatorSrcAddress, + validatorDstAddress, + }) + ), + unbondings: unbondings.map( + ({ amount, completionDate, validatorAddress }) => ({ + amount: BigNumber(amount), + completionDate: new Date(completionDate), + validatorAddress, + }) + ), delegatedBalance: BigNumber(delegatedBalance), pendingRewardsBalance: BigNumber(pendingRewardsBalance), - unboundingBalance: BigNumber(unboundingBalance), + unbondingBalance: BigNumber(unbondingBalance), withdrawAddress, }; } diff --git a/src/families/cosmos/test-dataset.js b/src/families/cosmos/test-dataset.js new file mode 100644 index 0000000000..fd09b0d4b3 --- /dev/null +++ b/src/families/cosmos/test-dataset.js @@ -0,0 +1,347 @@ +// @flow + +import { BigNumber } from "bignumber.js"; +import type { DatasetTest } from "../../__tests__/test-helpers/bridge"; +import { + InvalidAddress, + InvalidAddressBecauseDestinationIsAlsoSource, + NotEnoughBalance, +} from "@ledgerhq/errors"; +import invariant from "invariant"; +import type { Transaction } from "./types"; +import transactionTransformer from "./transaction"; +import { getEnv } from "../../env"; + +const dataset: DatasetTest = { + implementations: ["libcore"], + currencies: { + cosmos: { + FIXME_ignoreAccountFields: [ + "cosmosResources.pendingRewardsBalance", // They are always movings + "cosmosResources.delegations", // They are always movings because of pending Rewards + "cosmosResources.redelegations", // will change ince a redelegation it's done + "cosmosResources.unbondings", // will change once a unbonding it's done + ], + scanAccounts: [ + { + name: "cosmos seed 1", + apdus: ` + => 5500000000 + <= 0002010300330000049000 + => 550400001b06636f736d6f732c00008076000080000000800000000000000000 + <= 0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a636f736d6f73316738343933346a70753376356465357971756b6b6b68786d63767377337532616a787670646c9000 + => 5500000000 + <= 0002010300330000049000 + => 550400001b06636f736d6f732c00008076000080000000800000000000000000 + <= 0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a636f736d6f73316738343933346a70753376356465357971756b6b6b68786d63767377337532616a787670646c9000 + => 5500000000 + <= 0002010300330000049000 + => 550400001b06636f736d6f732c00008076000080010000800000000000000000 + <= 02624ac83690d5ef627927104767d679aef73d3d3c9544abe4206b1d0c463c94ff636f736d6f7331303875793571396a743539677775677135797264686b7a6364396a7279736c6d706373746b359000 + => 5500000000 + <= 0002010300330000049000 + => 550400001b06636f736d6f732c00008076000080020000800000000000000000 + <= 038ff98278402aa3e46ccfd020561dc9724ab63d7179ca507c8154b5257c7d5200636f736d6f733163676336393661793270673664346763656a656b3279386c6136366a376535793363376b79779000 + `, + }, + ], + accounts: [ + { + FIXME_tests: ["balance is sum of ops"], + raw: { + id: + "libcore:1:cosmos:cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4:", + seedIdentifier: + "0388459b2653519948b12492f1a0b464720110c147a8155d23d423a5cc3c21d89a", + xpub: + "cosmospub1addwnpepqwyytxex2dgejj93yjf0rg95v3eqzyxpg75p2hfr6s36tnpuy8vf5p6kez4", + derivationMode: "", + index: 0, + freshAddress: "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + freshAddressPath: "44'/118'/0'/0/0", + freshAddresses: [ + { + address: "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + derivationPath: "44'/118'/0'/0/0", + }, + ], + name: "Cosmos 1", + balance: "2180673", + spendableBalance: "2180673", + blockHeight: 1615299, + currencyId: "cosmos", + unit: { name: "Atom", code: "ATOM", magnitude: 6 }, + unitMagnitude: 6, + operationsCount: 85, + operations: [], + pendingOperations: [], + lastSyncDate: "", + }, + transactions: [ + { + name: "Same as Recipient", + transaction: (t) => ({ + ...t, + amount: BigNumber(100), + recipient: "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl", + }), + expectedStatus: { + errors: { + recipient: new InvalidAddressBecauseDestinationIsAlsoSource(), + }, + warnings: {}, + }, + }, + { + name: "Invalid Address", + transaction: (t) => ({ + ...t, + amount: BigNumber(100), + recipient: "dsadasdasdasdas", + }), + expectedStatus: { + errors: { + recipient: new InvalidAddress(), + }, + warnings: {}, + }, + }, + { + name: "GasLimit", + transaction: (t) => ({ + ...t, + amount: BigNumber(100), + recipient: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + gasLimit: BigNumber("10000"), + }), + expectedStatus: { + errors: {}, + warnings: {}, + estimatedFees: BigNumber("10000").multipliedBy( + getEnv("COSMOS_GAS_PRICE") + ), + }, + }, + { + name: "Fees", + transaction: (t) => ({ + ...t, + amount: BigNumber(100), + recipient: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + fees: BigNumber("10000"), + }), + expectedStatus: { + errors: {}, + warnings: {}, + estimatedFees: BigNumber("10000"), + }, + }, + { + name: "send max", + transaction: transactionTransformer.fromTransactionRaw({ + amount: "0", + recipient: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + useAllAmount: true, + family: "cosmos", + networkInfo: null, + validators: [], + cosmosSourceValidator: null, + fees: null, + gasLimit: "0", + memo: null, + mode: "send", + }), + expectedStatus: (account) => { + const { cosmosResources } = account; + invariant(cosmosResources, "Should exist because it's cosmos"); + const totalSpent = account.balance.minus( + cosmosResources.pendingRewardsBalance + .plus(cosmosResources.unbondingBalance) + .plus(cosmosResources.delegatedBalance) + ); + return { + errors: {}, + warnings: {}, + totalSpent, + }; + }, + }, + { + name: "send with memo", + transaction: transactionTransformer.fromTransactionRaw({ + amount: "0", + recipient: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + useAllAmount: true, + family: "cosmos", + networkInfo: null, + validators: [], + cosmosSourceValidator: null, + fees: null, + gasLimit: "0", + memo: "test", + mode: "send", + }), + expectedStatus: (account) => { + const { cosmosResources } = account; + invariant(cosmosResources, "Should exist because it's cosmos"); + const totalSpent = account.balance.minus( + cosmosResources.pendingRewardsBalance + .plus(cosmosResources.unbondingBalance) + .plus(cosmosResources.delegatedBalance) + ); + return { + errors: {}, + warnings: {}, + totalSpent, + }; + }, + }, + { + name: "Not Enough balance", + transaction: (t) => ({ + ...t, + amount: BigNumber("99999999999999999"), + recipient: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + }), + expectedStatus: { + errors: { + amount: new NotEnoughBalance(), + }, + warnings: {}, + }, + }, + { + name: "Redelegation - success", + transaction: (t) => ({ + ...t, + amount: BigNumber(100), + validators: [ + { + address: + "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7", + amount: BigNumber(100), + }, + ], + cosmosSourceValidator: + "cosmosvaloper1sd4tl9aljmmezzudugs7zlaya7pg2895ws8tfs", + mode: "redelegate", + }), + expectedStatus: { + errors: {}, + warnings: {}, + }, + }, + { + name: "Unbonding - success", + transaction: (t) => ({ + ...t, + mode: "undelegate", + validators: [ + { + address: + "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7", + amount: BigNumber(100), + }, + ], + }), + expectedStatus: { + errors: {}, + warnings: {}, + }, + }, + { + name: "Delegate - success", + transaction: (t) => ({ + ...t, + mode: "delegate", + validators: [ + { + address: + "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7", + amount: BigNumber(100), + }, + ], + }), + expectedStatus: { + errors: {}, + warnings: {}, + }, + }, + { + name: "Delegate - not a valid", + transaction: (t) => ({ + ...t, + mode: "delegate", + validators: [ + { + address: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + amount: BigNumber(100), + }, + ], + }), + expectedStatus: { + errors: { recipient: new InvalidAddress() }, + warnings: {}, + }, + }, + { + name: "ClaimReward - success", + transaction: (t) => ({ + ...t, + validators: [ + { + address: + "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7", + amount: BigNumber(0), + }, + ], + mode: "claimReward", + }), + expectedStatus: { + errors: {}, + warnings: {}, + }, + }, + { + name: "ClaimReward - not a cosmosvaloper", + transaction: (t) => ({ + ...t, + validators: [ + { + address: "cosmos108uy5q9jt59gwugq5yrdhkzcd9jryslmpcstk5", + amount: BigNumber(0), + }, + ], + mode: "claimReward", + }), + expectedStatus: { + errors: { recipient: new InvalidAddress() }, + warnings: {}, + }, + }, + { + name: "claimRewardCompound - success", + transaction: (t) => ({ + ...t, + validators: [ + { + address: + "cosmosvaloper1grgelyng2v6v3t8z87wu3sxgt9m5s03xfytvz7", + amount: BigNumber(100), + }, + ], + mode: "claimRewardCompound", + }), + expectedStatus: { + errors: {}, + warnings: {}, + }, + }, + ], + }, + ], + }, + }, +}; + +export default dataset; diff --git a/src/families/cosmos/test-specifics.js b/src/families/cosmos/test-specifics.js new file mode 100644 index 0000000000..451e945329 --- /dev/null +++ b/src/families/cosmos/test-specifics.js @@ -0,0 +1,194 @@ +// @flow + +import { BigNumber } from "bignumber.js"; +import { cosmosCreateMessage } from "./message"; +import { withLibcore } from "../../libcore/access"; + +export default () => { + describe("cosmosCreateMessage", () => { + const commonTransaction = { + family: "cosmos", + fees: null, + gasLimit: null, + recipient: "", + useAllAmount: false, + networkInfo: null, + memo: null, + cosmosSourceValidator: null, + validators: [], + }; + + const sourceAddresss = "cosmos1g84934jpu3v5de5yqukkkhxmcvsw3u2ajxvpdl"; + + test("create a message send", async () => { + const messages = await withLibcore(async (core) => { + return await cosmosCreateMessage( + sourceAddresss, + { + ...commonTransaction, + amount: BigNumber(3000), + mode: "send", + }, + core + ); + }); + + expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages.length).toBe(1); + }); + + test("create a message delegate that throw and error", async () => { + await withLibcore(async (core) => { + try { + return await cosmosCreateMessage( + sourceAddresss, + { + ...commonTransaction, + amount: BigNumber(3000), + mode: "delegate", + }, + core + ); + } catch (e) { + expect(e.message).toBe("no validators"); + } + }); + }); + + test("create a message delegate with multiples validators", async () => { + const messages = await withLibcore(async (core) => { + return await cosmosCreateMessage( + sourceAddresss, + { + ...commonTransaction, + amount: BigNumber(3000), + mode: "delegate", + validators: [ + { amount: BigNumber(3000), address: "" }, + { amount: BigNumber(3000), address: "" }, + { amount: BigNumber(3000), address: "" }, + ], + }, + core + ); + }); + + expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages[1].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages[2].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages.length).toBe(3); + }); + + test("create a message delegate", async () => { + const messages = await withLibcore(async (core) => { + return await cosmosCreateMessage( + sourceAddresss, + { + ...commonTransaction, + amount: BigNumber(3000), + mode: "delegate", + validators: [{ amount: BigNumber(3000), address: "" }], + }, + core + ); + }); + + expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages.length).toBe(1); + }); + + test("create a message undelegate", async () => { + const messages = await withLibcore(async (core) => { + return await cosmosCreateMessage( + sourceAddresss, + { + ...commonTransaction, + amount: BigNumber(3000), + mode: "undelegate", + validators: [{ amount: BigNumber(3000), address: "" }], + }, + core + ); + }); + + expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages.length).toBe(1); + }); + + test("create a message redelegate - without cosmosSourceValidator", async () => { + await withLibcore(async (core) => { + try { + return await cosmosCreateMessage( + sourceAddresss, + { + ...commonTransaction, + amount: BigNumber(0), + mode: "redelegate", + cosmosSourceValidator: null, + validators: [{ amount: BigNumber(3000), address: "" }], + }, + core + ); + } catch (e) { + expect(e.message).toBe("source validator is empty"); + } + }); + }); + + test("create a message redelegate", async () => { + const messages = await withLibcore(async (core) => { + return await cosmosCreateMessage( + sourceAddresss, + { + ...commonTransaction, + amount: BigNumber(3000), + mode: "redelegate", + cosmosSourceValidator: "source", + validators: [{ amount: BigNumber(3000), address: "" }], + }, + core + ); + }); + + expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages.length).toBe(1); + }); + + test("create a message claimReward", async () => { + const messages = await withLibcore(async (core) => { + return await cosmosCreateMessage( + sourceAddresss, + { + ...commonTransaction, + amount: BigNumber(0), + mode: "claimReward", + validators: [{ amount: BigNumber(0), address: "" }], + }, + core + ); + }); + + expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages.length).toBe(1); + }); + + test("create a message claimRewardCompound", async () => { + const messages = await withLibcore(async (core) => { + return await cosmosCreateMessage( + sourceAddresss, + { + ...commonTransaction, + amount: BigNumber(0), + mode: "claimRewardCompound", + validators: [{ amount: BigNumber(0), address: "" }], + }, + core + ); + }); + + expect(messages[0].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages[1].constructor.name).toBe("NJSCosmosLikeMessage"); + expect(messages.length).toBe(2); + }); + }); +}; diff --git a/src/families/cosmos/types.js b/src/families/cosmos/types.js index 4b8c5436ec..150973a11e 100644 --- a/src/families/cosmos/types.js +++ b/src/families/cosmos/types.js @@ -7,12 +7,25 @@ import type { } from "../../types/transaction"; import type { Operation, OperationRaw } from "../../types/operation"; +import type { CoreAmount, CoreBigInt, Spec } from "../../libcore/types"; -export type CoreStatics = {}; -export type CoreAccountSpecifics = {}; -export type CoreOperationSpecifics = {}; -export type CoreCurrencySpecifics = {}; +export type CoreStatics = { + CosmosLikeOperation: Class, + CosmosLikeAddress: Class, + CosmosLikeTransactionBuilder: Class, + CosmosLikeTransaction: Class, + CosmosLikeMessage: Class +}; + +export type CoreAccountSpecifics = { + asCosmosLikeAccount(): Promise +}; + +export type CoreOperationSpecifics = { + asCosmosLikeOperation(): Promise +}; +export type CoreCurrencySpecifics = {}; export type CosmosDelegationStatus = | "bonded" // in the active set that generates rewards | "unbonding" // doesn't generate rewards. means the validator has been removed from the active set, but has its voting power "frozen" in case they misbehaved (just like a delegator undelegating). This last 21 days @@ -25,11 +38,26 @@ export type CosmosDelegation = { status: CosmosDelegationStatus, }; +export type CosmosRedelegation = {| + validatorSrcAddress: string, + validatorDstAddress: string, + amount: BigNumber, + completionDate: Date +|}; + +export type CosmosUnbonding = {| + validatorAddress: string, + amount: BigNumber, + completionDate: Date +|}; + export type CosmosResources = {| delegations: CosmosDelegation[], + redelegations: CosmosRedelegation[], + unbondings: CosmosUnbonding[], delegatedBalance: BigNumber, pendingRewardsBalance: BigNumber, - unboundingBalance: BigNumber, + unbondingBalance: BigNumber, withdrawAddress: string, |}; @@ -40,11 +68,26 @@ export type CosmosDelegationRaw = {| status: CosmosDelegationStatus, |}; +export type CosmosUnbondingRaw = {| + validatorAddress: string, + amount: string, + completionDate: string +|}; + +export type CosmosRedelegationRaw = {| + validatorSrcAddress: string, + validatorDstAddress: string, + amount: string, + completionDate: string +|}; + export type CosmosResourcesRaw = {| delegations: CosmosDelegationRaw[], + redelegations: CosmosRedelegationRaw[], + unbondings: CosmosUnbondingRaw[], delegatedBalance: string, pendingRewardsBalance: string, - unboundingBalance: string, + unbondingBalance: string, withdrawAddress: string, |}; @@ -54,6 +97,7 @@ export type CosmosValidatorItem = {| name: string, votingPower: number, // value from 0.0 to 1.0 (normalized percentage) commission: number, // value from 0.0 to 1.0 (normalized percentage) + estimatedYearlyRewardsRate: number // value from 0.0 to 1.0 (normalized percentage) |}; export type CosmosRewardsState = {| @@ -72,8 +116,7 @@ export type CosmosRewardsState = {| // by convention preload would return a Promise of CosmosPreloadData export type CosmosPreloadData = { - validators: CosmosValidatorItem[], - rewardsState: CosmosRewardsState, + validators: CosmosValidatorItem[] }; export type CosmosOperationMode = @@ -131,11 +174,188 @@ export type CosmosDelegationInfo = { amount: BigNumber, }; -export type CosmosValidatorRaw = { +export type CosmosDelegationInfoRaw = { address: string, amount: string, }; +export type CosmosMessage = CoreCosmosLikeMessage; + +type CosmosMsgSend = { + fromAddress: string, + toAddress: string, + amount: Array +}; + +type CosmosMsgDelegate = { + delegatorAddress: string, + validatorAddress: string, + amount: CoreCosmosLikeAmount +}; + +type CosmosMsgUndelegate = CosmosMsgDelegate; + +type CosmosMsgRedelegate = { + delegatorAddress: string, + validatorSourceAddress: string, + validatorDestinationAddress: string, + amount: CoreCosmosLikeAmount +}; + +type CoreCosmosLikeAmount = { + amount: string, + denom: string +}; + +type CosmosMsgWithdrawDelegationReward = { + delegatorAddress: string, + validatorAddress: string +}; + +type CosmosLikeEntry = { + // Block height of the begin redelegate request + getCreationHeight(): Promise, + // Timestamp of the redelegation completion + getCompletionTime(): Date, + // Balance requested to redelegate + getInitialBalance(): Promise, + // Current amount being redelegated (i.e. less than initialBalance if slashed) + getBalance(): Promise +}; + +export type CosmosLikeRedelegation = { + getDelegatorAddress(): string, + getSrcValidatorAddress(): string, + getDstValidatorAddress(): string, + getEntries(): CosmosLikeEntry[] +}; + +export type CosmosLikeUnbonding = { + getDelegatorAddress(): string, + getValidatorAddress(): string, + getEntries(): CosmosLikeEntry[] +}; + +export type CosmosLikeDelegation = { + getDelegatorAddress(): string, + getValidatorAddress(): string, + getDelegatedAmount(): CoreAmount +}; + +declare class CoreCosmosLikeAddress { + toBech32(): Promise; +} + +declare class CoreCosmosLikeOperation { + getTransaction(): Promise; + getMessage(): Promise; +} + +declare class CoreCosmosLikeMsgType {} + +declare class CoreCosmosLikeMessage { + getIndex(): Promise; + getMessageType(): Promise; + getRawMessageType(): Promise; + static wrapMsgSend(message: CosmosMsgSend): Promise; + static wrapMsgDelegate( + message: CosmosMsgDelegate + ): Promise; + static wrapMsgUndelegate( + message: CosmosMsgUndelegate + ): Promise; + static wrapMsgBeginRedelegate( + message: CosmosMsgRedelegate + ): Promise; + static wrapMsgWithdrawDelegationReward( + message: CosmosMsgWithdrawDelegationReward + ): Promise; +} + +declare class CoreCosmosLikeTransactionBuilder { + setMemo(memo: string): Promise; + setSequence(sequence: string): Promise; + setAccountNumber( + accountNumber: string + ): Promise; + addMessage( + message: CoreCosmosLikeMessage + ): Promise; + setFee(fees: CoreAmount): Promise; + setGas(gas: CoreAmount): Promise; + build(): Promise; +} + +declare class CoreCosmosLikeTransaction { + toRawTransaction(): string; + toSignatureBase(): Promise; + getHash(): Promise; + getFee(): Promise; + getGas(): Promise; + serializeForSignature(): Promise; + serializeForBroadcast(type: "block" | "async" | "sync"): Promise; + setSignature(string, string): Promise; + setDERSignature(string): Promise; +} + +export type CoreCosmosGasLimitRequest = { + memo: string, + amplifier: number, + messages: CoreCosmosLikeMessage[] +}; + +declare class CosmosLikeReward { + getDelegatorAddress(): string; + getValidatorAddress(): string; + getRewardAmount(): CoreAmount; +} + +// It will be probably updated +// the API for our current version goes by 0 to 2 +// and it will be go to 1 to 3 when the explorer version will update +export type CosmosLikeValidator = { + activeStatus: 0 | 1 | 2; + getActiveStatus(): Promise<0 | 1 | 2> +} + +export type CosmosBroadcastResponse = { + code: number, + raw_log: string, + txhash: string, + raw_log: string +}; + +declare class CoreCosmosLikeAccount { + buildTransaction(): Promise; + broadcastRawTransaction(signed: string): Promise; + broadcastTransaction(signed: string): Promise; + getEstimatedGasLimit( + transaction: CoreCosmosLikeTransaction + ): Promise; + estimateGas(request: CoreCosmosGasLimitRequest): Promise; + getBaseReserve(): Promise; + isAddressActivated(address: string): Promise; + getFees(): Promise; + getSequence(): Promise; + + getAccountNumber(): Promise; + getPendingRewards(): Promise; + getRedelegations(): Promise; + getUnbondings(): Promise; + getDelegations(): Promise; + getValidatorInfo(validatorAddress: string): Promise; +} + +export type { + CoreCosmosLikeAccount, + CoreCosmosLikeAddress, + CoreCosmosLikeOperation, + CoreCosmosLikeTransaction, + CoreCosmosLikeTransactionBuilder +}; + + + export type Transaction = {| ...TransactionCommon, family: "cosmos", @@ -144,7 +364,7 @@ export type Transaction = {| fees: ?BigNumber, gasLimit: ?BigNumber, memo: ?string, - validators: ?(CosmosDelegationInfo[]), + validators: CosmosDelegationInfo[], cosmosSourceValidator: ?string, |}; @@ -156,12 +376,10 @@ export type TransactionRaw = {| fees: ?string, gasLimit: ?string, memo: ?string, - validators: ?(CosmosValidatorRaw[]), + validators: CosmosDelegationInfoRaw[], cosmosSourceValidator: ?string, |}; -export const reflect = (_declare: *) => {}; - export type CosmosMappedDelegation = CosmosDelegation & { formattedAmount: string, formattedPendingRewards: string, @@ -183,3 +401,204 @@ export type CosmosMappedValidator = { export type CosmosSearchFilter = ( query: string ) => (delegation: CosmosMappedDelegation | CosmosMappedValidator) => boolean; + +export const reflect = (declare: (string, Spec) => void) => { + declare("CosmosLikeTransactionBuilder", { + methods: { + addMessage: { + params: ["CosmosLikeMessage"] + }, + build: { + returns: "CosmosLikeTransaction" + }, + setMemo: {}, + setSequence: {}, + setAccountNumber: {}, + setFee: {}, + setGas: { + params: ["Amount"] + } + } + }); + + declare("CosmosLikeAccount", { + methods: { + estimateGas: { + params: ["CosmosGasLimitRequest"], + returns: "BigInt" + }, + buildTransaction: {}, + broadcastRawTransaction: {}, + broadcastTransaction: {}, + getEstimatedGasLimit: { + params: ["CosmosLikeTransaction"] + }, + estimateGas: { + params: ["CosmosGasLimitRequest"] + }, + getSequence: {}, + getAccountNumber: {}, + getPendingRewards: { + returns: ["CosmosLikeReward"] + }, + getRedelegations: { + returns: ["CosmosLikeRedelegation"] + }, + getUnbondings: { + returns: ["CosmosLikeUnbonding"] + }, + getDelegations: { + returns: ["CosmosLikeDelegation"] + }, + getValidatorInfo: { + returns: "CosmosLikeValidator" + } + } + }); + + declare("CosmosLikeValidator", { + njsUsesPlainObject: true, + methods: { + getActiveStatus: { + njsField: "activeStatus", + }, + } + }) + + declare("CosmosLikeReward", { + methods: { + getDelegatorAddress: {}, + getValidatorAddress: {}, + getRewardAmount: { + returns: "Amount" + } + } + }) + +declare("CosmosLikeUnbonding", { + methods: { + getDelegatorAddress: {}, + getValidatorAddress: {}, + getEntries: { + returns: ["CosmosLikeUnbondingEntry"] + } + } +}) + + declare("CosmosLikeTransaction", { + methods: { + getHash: {}, + setDERSignature: { + params: ["hex"] + }, + getFee: {}, + getGas: {}, + serializeForSignature: {}, + serializeForBroadcast: {} + } + }); + + declare("CosmosLikeOperation", { + methods: { + getTransaction: { + returns: "CosmosLikeTransaction" + }, + getMessage: { + returns: "CosmosLikeMessage" + } + } + }); + + declare("CosmosLikeRedelegationEntry", { + methods: { + getInitialBalance: { + returns: "BigInt" + }, + getCompletionTime: {} + } + }) + + declare("CosmosLikeUnbondingEntry", { + methods: { + getInitialBalance: { + returns: "BigInt" + }, + getCompletionTime: {} + } + }) + + declare("CosmosLikeRedelegation", { + methods: { + getDelegatorAddress: { + returns: "string" + }, + getSrcValidatorAddress: { + return: "string" + }, + getDstValidatorAddress: { + return: "string" + }, + getEntries: { + returns: ["CosmosLikeRedelegationEntry"] + } + } + }); + + declare("CosmosLikeDelegation", { + methods: { + getDelegatorAddress: {}, + getValidatorAddress: {}, + getDelegatedAmount: { + returns: "Amount" + } + } + }); + + declare("CosmosLikeMessage", { + statics: { + wrapMsgSend: { + params: ["CosmosLikeMsgSend"], + returns: "CosmosLikeMessage", + njsBuggyMethodIsNotStatic: true + }, + wrapMsgDelegate: { + params: ["CosmosLikeMsgDelegate"], + returns: "CosmosLikeMessage", + njsBuggyMethodIsNotStatic: true + }, + wrapMsgUndelegate: { + params: ["CosmosLikeMsgUndelegate"], + returns: "CosmosLikeMessage", + njsBuggyMethodIsNotStatic: true + }, + wrapMsgBeginRedelegate: { + params: ["CosmosLikeMsgBeginRelegate"], + returns: "CosmosLikeMessage", + njsBuggyMethodIsNotStatic: true + }, + wrapMsgWithdrawDelegationReward: { + params: ["CosmosLikeMsgWithdrawDelegationReward"], + returns: "CosmosLikeMessage", + njsBuggyMethodIsNotStatic: true + } + }, + methods: { + getMessageType: {}, + getRawMessageType: {}, + getIndex: {} + } + }); + + return { + OperationMethods: { + asCosmosLikeOperation: { + returns: "CosmosLikeOperation" + } + }, + AccountMethods: { + asCosmosLikeAccount: { + returns: "CosmosLikeAccount" + } + } + }; +}; diff --git a/src/families/cosmos/validators.js b/src/families/cosmos/validators.js new file mode 100644 index 0000000000..3fc5dd4ba2 --- /dev/null +++ b/src/families/cosmos/validators.js @@ -0,0 +1,193 @@ +// @flow +import network from "../../network"; +import { log } from "@ledgerhq/logs"; +import { getEnv } from "../../env"; +import { makeLRUCache } from "../../cache"; + +import type { CosmosValidatorItem, CosmosRewardsState } from "./types"; + +const getBaseApiUrl = () => + getEnv("API_COSMOS_BLOCKCHAIN_EXPLORER_API_ENDPOINT"); + +const cacheValidators = makeLRUCache( + async (rewardState: CosmosRewardsState) => { + const url = `${getBaseApiUrl()}/staking/validators`; + const { data } = await network({ url, method: "GET" }); + + const validators = data.result.map((validator) => { + const commission = parseFloat(validator.commission.commission_rates.rate); + return { + validatorAddress: validator.operator_address, + name: validator.description.moniker, + votingPower: + parseFloat(validator.tokens) / + (rewardState.actualBondedRatio * rewardState.totalSupply * 1000000), + commission, + estimatedYearlyRewardsRate: validatorEstimatedRate( + commission, + rewardState + ), + }; + }); + + return validators; + }, + () => "" +); + +export const getValidators = async () => { + const rewardsState = await getRewardsState(); + // validators need the rewardsState ONLY to compute voting power as percentage instead of raw uatoms amounts + return await cacheValidators(rewardsState); +}; + +const parseUatomStrAsAtomNumber = (uatoms: string) => { + return parseFloat(uatoms) / 1000000.0; +}; + +const getRewardsState = makeLRUCache( + async () => { + // All obtained values are strings ; so sometimes we will need to parse them as numbers + const inflationUrl = `${getBaseApiUrl()}/minting/inflation`; + const { data: inflationData } = await network({ + url: inflationUrl, + method: "GET", + }); + const currentValueInflation = parseFloat(inflationData.result); + + const inflationParametersUrl = `${getBaseApiUrl()}/minting/parameters`; + const { data: inflationParametersData } = await network({ + url: inflationParametersUrl, + method: "GET", + }); + const inflationRateChange = parseFloat( + inflationParametersData.result.inflation_rate_change + ); + const inflationMaxRate = parseFloat( + inflationParametersData.result.inflation_max + ); + const inflationMinRate = parseFloat( + inflationParametersData.result.inflation_min + ); + const targetBondedRatio = parseFloat( + inflationParametersData.result.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.result.blocks_per_year); + + const communityTaxUrl = `${getBaseApiUrl()}/distribution/parameters`; + const { data: communityTax } = await network({ + url: communityTaxUrl, + method: "GET", + }); + const communityPoolCommission = parseFloat( + communityTax.result.community_tax + ); + + const supplyUrl = `${getBaseApiUrl()}/supply/total`; + const { data: totalSupplyData } = await network({ + url: supplyUrl, + method: "GET", + }); + const totalSupply = parseUatomStrAsAtomNumber( + totalSupplyData.result[0].amount + ); + + const ratioUrl = `${getBaseApiUrl()}/staking/pool`; + const { data: ratioData } = await network({ url: ratioUrl, method: "GET" }); + const actualBondedRatio = + parseUatomStrAsAtomNumber(ratioData.result.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, + }; + }, + () => "" +); + +const computeAvgYearlyInflation = (rewardsState: CosmosRewardsState) => { + // Return invalid rewardsState if + // rewardsState.currentValueInflation is not between inflationMinRate and inflationMaxRate + const inflationSlope = + (1 - rewardsState.actualBondedRatio / rewardsState.targetBondedRatio) * + rewardsState.inflationRateChange; + const unrestrictedEndOfYearInflation = + rewardsState.currentValueInflation * (1 + inflationSlope); + + if ( + unrestrictedEndOfYearInflation <= rewardsState.inflationMaxRate && + unrestrictedEndOfYearInflation >= rewardsState.inflationMinRate + ) { + return ( + (rewardsState.currentValueInflation + unrestrictedEndOfYearInflation) / 2 + ); + } + + if (unrestrictedEndOfYearInflation > rewardsState.inflationMaxRate) { + const diffToMax = + rewardsState.inflationMaxRate - rewardsState.currentValueInflation; + const maxPoint = diffToMax / inflationSlope; + const averageInflation = + (1 - maxPoint / 2) * rewardsState.inflationMaxRate + + (maxPoint / 2) * rewardsState.currentValueInflation; + return averageInflation; + } + + if (unrestrictedEndOfYearInflation < rewardsState.inflationMinRate) { + const diffToMin = + rewardsState.currentValueInflation - rewardsState.inflationMinRate; + const minPoint = diffToMin / inflationSlope; + const averageInflation = + (1 - minPoint / 2) * rewardsState.inflationMinRate + + (minPoint / 2) * rewardsState.currentValueInflation; + return averageInflation; + } + + throw new Error("Unreachable code"); +}; + +export const validatorEstimatedRate = ( + validatorCommission: number, + rewardsState: CosmosRewardsState +) => { + // This correction changes how inflation is computed vs. the value the network advertises + const inexactBlockTimeCorrection = + rewardsState.assumedTimePerBlock / rewardsState.averageTimePerBlock; + // This correction assumes a constant bonded_ratio, this changes the yearly inflation + const yearlyInflation = computeAvgYearlyInflation(rewardsState); + + // This correction adds the fees to the rate computation + const yearlyFeeRate = + (rewardsState.averageDailyFees * 365.24) / rewardsState.totalSupply; + + return ( + inexactBlockTimeCorrection * + (yearlyInflation + yearlyFeeRate) * + (1 / rewardsState.actualBondedRatio) * + (1 - rewardsState.communityPoolCommission) * + (1 - validatorCommission) + ); +}; + +export const hydrateValidators = (validators: CosmosValidatorItem[]) => { + log("cosmos/validators", "hydrate " + validators.length + " validators"); + cacheValidators.hydrate("", validators); +}; diff --git a/src/generated/bridge/libcore.js b/src/generated/bridge/libcore.js index 7d36162c0a..e90fac0cda 100644 --- a/src/generated/bridge/libcore.js +++ b/src/generated/bridge/libcore.js @@ -1,5 +1,6 @@ // @flow import bitcoin from "../../families/bitcoin/bridge/libcore.js"; +import cosmos from "../../families/cosmos/bridge/libcore.js"; import ethereum from "../../families/ethereum/bridge/libcore.js"; import ripple from "../../families/ripple/bridge/libcore.js"; import stellar from "../../families/stellar/bridge/libcore.js"; @@ -7,6 +8,7 @@ import tezos from "../../families/tezos/bridge/libcore.js"; export default { bitcoin, + cosmos, ethereum, ripple, stellar, diff --git a/src/generated/cli-transaction.js b/src/generated/cli-transaction.js index 25c5486848..a9d556205e 100644 --- a/src/generated/cli-transaction.js +++ b/src/generated/cli-transaction.js @@ -1,5 +1,6 @@ // @flow import bitcoin from "../families/bitcoin/cli-transaction.js"; +import cosmos from "../families/cosmos/cli-transaction.js"; import ethereum from "../families/ethereum/cli-transaction.js"; import ripple from "../families/ripple/cli-transaction.js"; import stellar from "../families/stellar/cli-transaction.js"; @@ -8,6 +9,7 @@ import tron from "../families/tron/cli-transaction.js"; export default { bitcoin, + cosmos, ethereum, ripple, stellar, diff --git a/src/generated/hw-getAddress.js b/src/generated/hw-getAddress.js index 8516373588..07af8fe6db 100644 --- a/src/generated/hw-getAddress.js +++ b/src/generated/hw-getAddress.js @@ -1,5 +1,6 @@ // @flow import bitcoin from "../families/bitcoin/hw-getAddress.js"; +import cosmos from "../families/cosmos/hw-getAddress.js"; import ethereum from "../families/ethereum/hw-getAddress.js"; import neo from "../families/neo/hw-getAddress.js"; import ripple from "../families/ripple/hw-getAddress.js"; @@ -9,6 +10,7 @@ import tron from "../families/tron/hw-getAddress.js"; export default { bitcoin, + cosmos, ethereum, neo, ripple, diff --git a/src/generated/libcore-buildOperation.js b/src/generated/libcore-buildOperation.js index d61ae8642e..c86025832e 100644 --- a/src/generated/libcore-buildOperation.js +++ b/src/generated/libcore-buildOperation.js @@ -1,5 +1,6 @@ // @flow import bitcoin from "../families/bitcoin/libcore-buildOperation.js"; +import cosmos from "../families/cosmos/libcore-buildOperation.js"; import ethereum from "../families/ethereum/libcore-buildOperation.js"; import ripple from "../families/ripple/libcore-buildOperation.js"; import stellar from "../families/stellar/libcore-buildOperation.js"; @@ -7,6 +8,7 @@ import tezos from "../families/tezos/libcore-buildOperation.js"; export default { bitcoin, + cosmos, ethereum, ripple, stellar, diff --git a/src/generated/libcore-getFeesForTransaction.js b/src/generated/libcore-getFeesForTransaction.js index 23f6d739ba..e02cfb613f 100644 --- a/src/generated/libcore-getFeesForTransaction.js +++ b/src/generated/libcore-getFeesForTransaction.js @@ -1,10 +1,12 @@ // @flow import bitcoin from "../families/bitcoin/libcore-getFeesForTransaction.js"; +import cosmos from "../families/cosmos/libcore-getFeesForTransaction.js"; import ethereum from "../families/ethereum/libcore-getFeesForTransaction.js"; import tezos from "../families/tezos/libcore-getFeesForTransaction.js"; export default { bitcoin, + cosmos, ethereum, tezos, }; diff --git a/src/generated/libcore-postBuildAccount.js b/src/generated/libcore-postBuildAccount.js new file mode 100644 index 0000000000..b419bf1042 --- /dev/null +++ b/src/generated/libcore-postBuildAccount.js @@ -0,0 +1,6 @@ +// @flow +import cosmos from "../families/cosmos/libcore-postBuildAccount.js"; + +export default { + cosmos, +}; diff --git a/src/generated/test-dataset.js b/src/generated/test-dataset.js index fbbae5451e..278fc2a08b 100644 --- a/src/generated/test-dataset.js +++ b/src/generated/test-dataset.js @@ -1,5 +1,6 @@ // @flow import bitcoin from "../families/bitcoin/test-dataset.js"; +import cosmos from "../families/cosmos/test-dataset.js"; import ethereum from "../families/ethereum/test-dataset.js"; import ripple from "../families/ripple/test-dataset.js"; import stellar from "../families/stellar/test-dataset.js"; @@ -8,6 +9,7 @@ import tron from "../families/tron/test-dataset.js"; export default { bitcoin, + cosmos, ethereum, ripple, stellar, diff --git a/src/generated/test-specifics.js b/src/generated/test-specifics.js index 6f80bd7e98..57e402a490 100644 --- a/src/generated/test-specifics.js +++ b/src/generated/test-specifics.js @@ -1,9 +1,11 @@ // @flow +import cosmos from "../families/cosmos/test-specifics.js"; import stellar from "../families/stellar/test-specifics.js"; import tezos from "../families/tezos/test-specifics.js"; import tron from "../families/tron/test-specifics.js"; export default { + cosmos, stellar, tezos, tron, diff --git a/src/libcore/buildAccount/index.js b/src/libcore/buildAccount/index.js index 1dafc9c147..ef1ca76b5c 100644 --- a/src/libcore/buildAccount/index.js +++ b/src/libcore/buildAccount/index.js @@ -22,12 +22,15 @@ import { minimalOperationsBuilder } from "../../reconciliation"; import { getOperationsPageSize } from "../../pagination"; import getAccountBalanceHistory from "../getAccountBalanceHistory"; import { getRanges } from "../../portfolio"; +import byFamily from "../../generated/libcore-postBuildAccount"; // FIXME how to get that const OperationOrderKey = { date: 0, }; +type F = ({ account: Account, coreAccount: CoreAccount }) => Promise; + async function queryOps(coreAccount) { const query = await coreAccount.queryOperations(); await query.addOrder(OperationOrderKey.date, false); @@ -238,5 +241,10 @@ export async function buildAccount({ account.subAccounts = subAccounts; } + const f: F = byFamily[currency.family]; + if (f) { + return await f({ account, coreAccount }); + } + return account; } diff --git a/src/libcore/syncAccount.js b/src/libcore/syncAccount.js index bf0f966b99..cf70b62eff 100644 --- a/src/libcore/syncAccount.js +++ b/src/libcore/syncAccount.js @@ -130,6 +130,7 @@ export function sync( pendingOperations: initialAccount.pendingOperations.filter((op) => shouldRetainPendingOperation(syncedAccount, op) ), + cosmosResources: syncedAccount.cosmosResources, }) ) ); diff --git a/src/mock/account.js b/src/mock/account.js index 34215b28c8..011104ee4a 100644 --- a/src/mock/account.js +++ b/src/mock/account.js @@ -362,9 +362,11 @@ export function genAccount( account.cosmosResources = { // TODO variation in these delegations: [], + redelegations: [], + unbondings: [], delegatedBalance: BigNumber(0), pendingRewardsBalance: BigNumber(0), - unboundingBalance: BigNumber(0), + unbondingBalance: BigNumber(0), withdrawAddress: address, }; } diff --git a/src/types/operation.js b/src/types/operation.js index 84e09f3578..251252f0ac 100644 --- a/src/types/operation.js +++ b/src/types/operation.js @@ -14,7 +14,8 @@ export type OperationType = | "FREEZE" | "UNFREEZE" | "VOTE" - | "REWARD"; + | "REWARD" + | "FEES"; export type Operation = { // unique identifier (usually hash)