From c3b45ef9dcf3d0365aba4639c841f9b0b00eb84b Mon Sep 17 00:00:00 2001 From: "Sachin S. Kamath" Date: Mon, 7 Mar 2022 17:59:32 +0530 Subject: [PATCH 001/862] networks: add cheq and blockfend operator. --- src/networks.json | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index d79358edd..e8003d1ae 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1209,5 +1209,30 @@ } ], "authzSupport": false + }, + { + "name": "cheqd", + "prettyName": "cheqd", + "chainId": "cheqd-mainnet-1", + "prefix": "cheq", + "denom": "ncheq", + "restUrl": [ + "https://api.cheqd.net", + "https://api.cheqd.blockfend.com" + ], + "rpcUrl": [ + "https://rpc.cheqd.net", + "https://rpc.cheqd.blockfend.com" + ], + "image": "https://raw.githubusercontent.com/osmosis-labs/osmosis-frontend/master/public/assets/tokens/cheq.svg", + "operators": [ + { + "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", + "botAddress": "cheqd1ypntyymvjg90ntxwes3hxtmuvvh4fcr8w082sd", + "runTime": "21:00", + "minimumReward": 1000 + } + ], + "authzSupport": true } -] +] \ No newline at end of file From 37a648797817396aef400f4f1c7d61bab29b9be7 Mon Sep 17 00:00:00 2001 From: "Sachin S. Kamath" Date: Mon, 7 Mar 2022 18:01:27 +0530 Subject: [PATCH 002/862] networks: newline at end --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index e8003d1ae..6397287e2 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1235,4 +1235,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 7726e8b43131c6d4ad007264e7b4290c0dbd284b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 7 Mar 2022 20:32:50 +0000 Subject: [PATCH 003/862] Add decimal and handle n denoms --- src/components/Coins.js | 2 +- src/networks.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Coins.js b/src/components/Coins.js index c1c863566..8e0cbaf29 100644 --- a/src/components/Coins.js +++ b/src/components/Coins.js @@ -13,7 +13,7 @@ function Coins(props) { if(coins.denom.startsWith('base')){ return coins.denom.slice(4).toUpperCase() - }else if(['u', 'a'].includes(coins.denom[0])){ + }else if(['u', 'a', 'n'].includes(coins.denom[0])){ return coins.denom.slice(1).toUpperCase() } return coins.denom.toUpperCase() diff --git a/src/networks.json b/src/networks.json index 23aecf37d..00dab3ce8 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1225,6 +1225,7 @@ "chainId": "cheqd-mainnet-1", "prefix": "cheq", "denom": "ncheq", + "decimal": 9, "restUrl": [ "https://api.cheqd.net", "https://api.cheqd.blockfend.com" From 5d2f60e8095ea4c4c45fc64cbc9e38c8bd42cd7b Mon Sep 17 00:00:00 2001 From: stan-bl <72133698+stan-bl@users.noreply.github.com> Date: Tue, 8 Mar 2022 13:55:40 +0100 Subject: [PATCH 004/862] Add umee network --- src/networks.json | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/networks.json b/src/networks.json index 468ce5020..d0a5e5d99 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1232,5 +1232,38 @@ } ], "authzSupport": false + }, + { + "name": "umee", + "prettyName": "umee", + "chainId": "umee-1", + "prefix": "umee", + "denom": "uumee", + "restUrl": [ + "https://api.aphrodite.main.network.umee.cc", + "https://api.apollo.main.network.umee.cc", + "https://api.artemis.main.network.umee.cc", + "https://api.athena.main.network.umee.cc", + "https://api.beaked.main.network.umee.cc", + "https://api.bottlenose.main.network.umee.cc" + ], + "rpcUrl": [ + "https://rpc.aphrodite.main.network.umee.cc", + "https://rpc.apollo.main.network.umee.cc", + "https://rpc.artemis.main.network.umee.cc", + "https://rpc.athena.main.network.umee.cc", + "https://rpc.beaked.main.network.umee.cc", + "https://rpc.bottlenose.main.network.umee.cc" + ], + "image": "https://raw.githubusercontent.com/cosmos/chain-registry/master/umee/images/umee.png", + "operators": [ + { + "address": "umeevaloper14vmuhgm04ffvzts9jxm2wy5d538g96ru9fsg5v", + "botAddress": "umee1m5hgzum68rjf4c7zhezgkj8hlnmr0kghyhx5wz", + "runTime": "10:00", + "minimumReward": 10000 + } + ], + "authzSupport": true } ] From 8107e749b448eb1ffde67e5dbc70c702e10c00b1 Mon Sep 17 00:00:00 2001 From: Jean-Emrick Date: Tue, 8 Mar 2022 08:02:14 -0500 Subject: [PATCH 005/862] Added chain suggest, clean branch --- src/components/App.js | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index a696b10f8..b4057346a 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -91,10 +91,46 @@ class App extends React.Component { error: 'Could not connect to any available API servers' }) } - await window.keplr.enable(this.state.chainId); + try { + await window.keplr.enable(this.state.chainId); + } catch (e) { + console.log(e.message, e) + await window.keplr.experimentalSuggestChain({ + rpc: this.props.network.rpcUrl[0], + rest: this.props.network.restUrl, + chainId: this.state.chainId, + chainName: this.props.network.prettyName, + stakeCurrency: { + coinDenom: this.props.network.denom, // Implement basedenom in networks.json + coinMinimalDenom: this.props.network.denom, + coinDecimals: false ? this.props.network.decimals: 6, + //coinGeckoId: this.props.network.coinGeckoId + }, + bip44: { coinType: 118 },// implement coin type in networks.json + bech32Config: { + bech32PrefixAccAddr: this.props.network.prefix, + bech32PrefixAccPub: this.props.network.prefix + "pub", + bech32PrefixValAddr: this.props.network.prefix + "valoper", + bech32PrefixValPub: this.props.network.prefix + "valoperpub", + bech32PrefixConsAddr: this.props.network.prefix + "valcons", + bech32PrefixConsPub: this.props.network.prefix + "valconspub" + }, + currencies: [{ + coinDenom: this.props.network.denom, // Implement basedenom in networks.json + coinMinimalDenom: this.props.network.denom, + coinDecimals: false ? this.props.network.decimals: 6, + //coinGeckoId: this.props.network.coinGeckoId + }], + feeCurrencies:[{ + coinDenom: this.props.network.denom, // Implement basedenom in networks.json + coinMinimalDenom: this.props.network.denom, + coinDecimals: false ? this.props.network.decimals: 6, + //coinGeckoId: this.props.network.coinGeckoId + }], + }) + } if (window.getOfflineSigner){ const offlineSigner = await window.getOfflineSignerAuto(this.state.chainId) - const key = await window.keplr.getKey(this.state.chainId); const stargateClient = await this.props.network.signingClient(offlineSigner, key) if(!stargateClient.connected){ this.setState({ From 67ff053d92bc63af37f103a3274b230de262ae1d Mon Sep 17 00:00:00 2001 From: Jean-Emrick Date: Tue, 8 Mar 2022 08:05:34 -0500 Subject: [PATCH 006/862] Add keplr staking url --- src/components/App.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/App.js b/src/components/App.js index b4057346a..7d0600ebc 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -107,6 +107,7 @@ class App extends React.Component { //coinGeckoId: this.props.network.coinGeckoId }, bip44: { coinType: 118 },// implement coin type in networks.json + walletUrlForStaking: "https://restake.app/" + this.props.network.prettyName, bech32Config: { bech32PrefixAccAddr: this.props.network.prefix, bech32PrefixAccPub: this.props.network.prefix + "pub", From 419861656fc017aba2368a8a75f2b23271dbaaa4 Mon Sep 17 00:00:00 2001 From: Jean-Emrick Date: Tue, 8 Mar 2022 08:39:58 -0500 Subject: [PATCH 007/862] Query chain regsitry to have all the information for Keplr Suggest Chain --- src/components/App.js | 14 +++++++------- src/networks.json | 38 +++++++++++++++++++++++++++++++------- src/utils/Network.mjs | 17 +++++++++++++++++ 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 7d0600ebc..fcba30cb8 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -101,12 +101,12 @@ class App extends React.Component { chainId: this.state.chainId, chainName: this.props.network.prettyName, stakeCurrency: { - coinDenom: this.props.network.denom, // Implement basedenom in networks.json + coinDenom: this.props.network.tokenRegistry.assets[0].symbol, coinMinimalDenom: this.props.network.denom, coinDecimals: false ? this.props.network.decimals: 6, - //coinGeckoId: this.props.network.coinGeckoId + coinGeckoId: this.props.network.tokenRegistry.assets[0].coingecko_id }, - bip44: { coinType: 118 },// implement coin type in networks.json + bip44: { coinType: this.props.network.chainRegistry.slip44 }, walletUrlForStaking: "https://restake.app/" + this.props.network.prettyName, bech32Config: { bech32PrefixAccAddr: this.props.network.prefix, @@ -117,16 +117,16 @@ class App extends React.Component { bech32PrefixConsPub: this.props.network.prefix + "valconspub" }, currencies: [{ - coinDenom: this.props.network.denom, // Implement basedenom in networks.json + coinDenom: this.props.network.tokenRegistry.assets[0].symbol, coinMinimalDenom: this.props.network.denom, coinDecimals: false ? this.props.network.decimals: 6, - //coinGeckoId: this.props.network.coinGeckoId + coinGeckoId: this.props.network.tokenRegistry.assets[0].coingecko_id }], feeCurrencies:[{ - coinDenom: this.props.network.denom, // Implement basedenom in networks.json + coinDenom: this.props.network.tokenRegistry.assets[0].symbol, coinMinimalDenom: this.props.network.denom, coinDecimals: false ? this.props.network.decimals: 6, - //coinGeckoId: this.props.network.coinGeckoId + coinGeckoId: this.props.network.tokenRegistry.assets[0].coingecko_id }], }) } diff --git a/src/networks.json b/src/networks.json index 468ce5020..d4998cdaf 100644 --- a/src/networks.json +++ b/src/networks.json @@ -434,7 +434,6 @@ "runTime": "21:00", "minimumReward": 1000 }, - { "address": "chihuahuavaloper140l6y2gp3gxvay6qtn70re7z2s0gn57zum7h5l", "botAddress": "chihuahua12xpq68caw2aqdu70jm4k792g0v9prhrprkt2p6", @@ -964,11 +963,11 @@ "minimumReward": 1000 }, { - "address": "evmosvaloper1qp49y6vh8vvv5yf8ule8fwx6sss82ncz39tunl", - "botAddress": "evmos10g8dt086qsfw0s27us8uvhnnftf9aanx4afg3h", - "runTime": "21:05", - "minimumReward": 1000000000000000000 - }, + "address": "evmosvaloper1qp49y6vh8vvv5yf8ule8fwx6sss82ncz39tunl", + "botAddress": "evmos10g8dt086qsfw0s27us8uvhnnftf9aanx4afg3h", + "runTime": "21:05", + "minimumReward": 1000000000000000000 + }, { "address": "evmosvaloper1f6m9d94lkenw9fy5wmytatt76l849kx6ugdz70", "botAddress": "evmos175l97fdm2a6x5xp82psec52elzu8nsl7dmc7an", @@ -1232,5 +1231,30 @@ } ], "authzSupport": false + }, + { + "name": "cheqd", + "prettyName": "cheqd", + "chainId": "cheqd-mainnet-1", + "prefix": "cheq", + "denom": "ncheq", + "restUrl": [ + "https://api.cheqd.net", + "https://api.cheqd.blockfend.com" + ], + "rpcUrl": [ + "https://rpc.cheqd.net", + "https://rpc.cheqd.blockfend.com" + ], + "image": "https://raw.githubusercontent.com/osmosis-labs/osmosis-frontend/master/public/assets/tokens/cheq.svg", + "operators": [ + { + "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", + "botAddress": "cheqd1ypntyymvjg90ntxwes3hxtmuvvh4fcr8w082sd", + "runTime": "21:00", + "minimumReward": 1000 + } + ], + "authzSupport": true } -] +] \ No newline at end of file diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 207980ded..d4a154fc2 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -1,4 +1,5 @@ import _ from 'lodash' +import axios from 'axios' import RestClient from './RestClient.mjs' import SigningClient from './SigningClient.mjs' import Operator from './Operator.mjs' @@ -34,6 +35,20 @@ const Network = async (data) => { return restClient.getAllValidators(150) } + // Get Chain Registry to extract more information + const chainData = () => { + return axios.get('https://raw.githubusercontent.com/cosmos/chain-registry/master/' + data.name + '/chain.json') + .then(res => res.data) + } + + const tokenData = async () => { + return axios.get('https://raw.githubusercontent.com/cosmos/chain-registry/master/' + data.name + '/assetlist.json') + .then(res => res.data) + } + + const chainRegistry = await chainData(); + const tokenRegistry = await tokenData(); + return { connected: restClient.connected, name: data.name, @@ -46,6 +61,8 @@ const Network = async (data) => { rpcUrl: data.rpcUrl, operators: data.operators, authzSupport: data.authzSupport, + chainRegistry, + tokenRegistry, data, restClient, signingClient, From 1e21fdf95f2bec9a1fbc771a096e2f282398d52a Mon Sep 17 00:00:00 2001 From: Jean-Emrick Date: Tue, 8 Mar 2022 08:55:29 -0500 Subject: [PATCH 008/862] Replace chain registry with cosmos directory api --- src/utils/Network.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index d4a154fc2..301870ae5 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -37,12 +37,12 @@ const Network = async (data) => { // Get Chain Registry to extract more information const chainData = () => { - return axios.get('https://raw.githubusercontent.com/cosmos/chain-registry/master/' + data.name + '/chain.json') + return axios.get('https://registry.cosmos.directory/' + data.name) .then(res => res.data) } const tokenData = async () => { - return axios.get('https://raw.githubusercontent.com/cosmos/chain-registry/master/' + data.name + '/assetlist.json') + return axios.get('https://registry.cosmos.directory/' + data.name + '/assetlist') .then(res => res.data) } From 8e620a806daea89c0b008da5765004b183399979 Mon Sep 17 00:00:00 2001 From: Jacques Vella Critien <30493486+jacquesvcritien@users.noreply.github.com> Date: Wed, 9 Mar 2022 12:34:03 +0100 Subject: [PATCH 009/862] Added Simply Staking Validators + New Networks * Added Simply Staking Validators * Added Bitsong, Persistence and Agoric Networks --- src/networks.json | 141 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/src/networks.json b/src/networks.json index f8af88843..db13f5601 100644 --- a/src/networks.json +++ b/src/networks.json @@ -114,6 +114,12 @@ "botAddress": "osmo1uwqjtgjhjctjc45ugy7ev5prprhehc7w3xx7ph", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "osmovaloper1feh2keupglep6mvxf5c96eulh3puujjryj2h8v", + "botAddress": "osmo1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9h34kq8", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -249,6 +255,12 @@ "botAddress": "juno1xrrfek3qpz4ja8ccrjklfy064asyqq9k9q45nn", "runTime": "21:00", "minimumReward": 10000 + }, + { + "address": "junovaloper124maqmcqv8tquy764ktz7cu0gxnzfw54l2d073", + "botAddress": "juno1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9fc9a3f", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -307,6 +319,12 @@ "botAddress": "cosmos1dg8u5qll7fjm9nnwqns0ullhtc6g552ugd0eqj", "runTime": "9:00", "minimumReward": 10000 + }, + { + "address": "cosmosvaloper124maqmcqv8tquy764ktz7cu0gxnzfw54n3vww8", + "botAddress": "cosmos1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9l2xxk4", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -387,6 +405,12 @@ "botAddress": "akash1uwqjtgjhjctjc45ugy7ev5prprhehc7w5xcfwl", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "akashvaloper1epnrvkylqpk0f2d3us3cvx7x9tlycz42lwf0v6", + "botAddress": "akash1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9j3tp00", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": false @@ -587,6 +611,12 @@ "botAddress": "gravity1uwqjtgjhjctjc45ugy7ev5prprhehc7wad8kjd", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "gravityvaloper14vaak0zdjtnvh92hw9ypwxj76093yrgzmuguar", + "botAddress": "gravity1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9m657na", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -612,6 +642,12 @@ "botAddress": "regen1mjq48r6435aewerpruwc8up3tz3rzan2nm4th3", "runTime": "19:00", "minimumReward": 10000 + }, + { + "address": "regenvaloper1ceunjpth8nds7sfmfd9yjmh97vxmwqfyf7r2cn", + "botAddress": "regen1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9qgd6q3", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -770,6 +806,12 @@ "botAddress": "dig1e44rluarkdw56dy2turnwjtvtg4wqvs02lu047", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "digvaloper1uc8nz85axexc9svkxjy0ccm28cc0uctvs33ewg", + "botAddress": "dig1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9870d5w", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": false @@ -861,6 +903,12 @@ "botAddress": "emoney1m5hgzum68rjf4c7zhezgkj8hlnmr0kghezplad", "runTime": "10:00", "minimumReward": 1000 + }, + { + "address": "emoneyvaloper1sz7rp920hgupfl0p6c7kwkaes8q8tx3lmzhcrc", + "botAddress": "emoney1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9sfujpg", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": false @@ -955,6 +1003,12 @@ "botAddress": "desmos1az6huqur4g2u29fa6c5zzxmtljp0euqmnla6x5", "runTime": "10:00", "minimumReward": 10000 + }, + { + "address": "desmosvaloper1zm3l7p8n5dxqeadsfxy3rd0j3c2knnx3chg77a", + "botAddress": "desmos19n0raswpfd9f7779ah637h20ltae0g7ussa8at", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1064,6 +1118,12 @@ "botAddress": "evmos1uwqjtgjhjctjc45ugy7ev5prprhehc7wmuyqdd", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "evmosvaloper1a36r4jvcprvewxnr2qhfs0h4spec35238s2h7t", + "botAddress": "evmos1axp36fwjy2vzlaym52urh80wq9l7z2t0shrj2y", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1187,6 +1247,12 @@ "botAddress": "lum1305e92ng6a9pdh3ttc3ld459ckxupxt3zx820n", "runTime": "19:15", "minimumReward": 10000 + }, + { + "address": "lumvaloper1uqw4e63xwwjmxt6ympdl20dsxtjr7lzx2p20hu", + "botAddress": "lum1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr92qm0rp", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1244,6 +1310,12 @@ "botAddress": "stars1m5hgzum68rjf4c7zhezgkj8hlnmr0kghzavkpp", "runTime": "10:00", "minimumReward": 10000 + }, + { + "address": "starsvaloper13agg47uffkehwqpvqeqp86aamjmkvmt4hyt9z7", + "botAddress": "stars1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9tk3may", + "runTime": "20:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1322,5 +1394,74 @@ } ], "authzSupport": false + }, + { + "name": "bitsong", + "prettyName": "BitSong", + "chainId": "bitsong-2b", + "prefix": "bitsong", + "denom": "ubtsg", + "restUrl": [ + "https://lcd-bitsong.itastakers.com" + ], + "rpcUrl": [ + "https://rpc-bitsong.itastakers.com" + ], + "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/btsg.svg", + "operators": [ + { + "address": "bitsongvaloper129y6g55wz7mz9vs2nudfvfdaun80fuphnltfpf", + "botAddress": "bitsong14td30sayes36tm042gcrwpynky927x2c434c06", + "runTime": "20:00", + "minimumReward": 1000000 + } + ], + "authzSupport": true + }, + { + "name": "persistence", + "prettyName": "Persistence", + "chainId": "core-1", + "prefix": "persistence", + "denom": "uxprt", + "restUrl": [ + "https://rest.core.persistence.one" + ], + "rpcUrl": [ + "https://rpc.core.persistence.one" + ], + "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/xprt.png", + "operators": [ + { + "address": "persistencevaloper1pdse5rr5njkkka6qeu5m8u704h6z67w5r700vn", + "botAddress": "persistence1atgss2zpwl9m8fmm32gzrkjtqw3tdawguqw6tk", + "runTime": "20:00", + "minimumReward": 1000000 + } + ], + "authzSupport": true + }, + { + "name": "agoric", + "prettyName": "Agoric", + "chainId": "agoric-3", + "prefix": "agoric", + "denom": "ubld", + "restUrl": [ + "https://main.api.agoric.net:443" + ], + "rpcUrl": [ + "https://main.rpc.agoric.net:443" + ], + "image": "https://raw.githubusercontent.com/cosmos/chain-registry/master/agoric/images/bld.png", + "operators": [ + { + "address": "agoricvaloper1kh8nwuee2y353u6nmv8jffpvhtzs7xu8f4klam", + "botAddress": "agoric148kutx4cag3js995cesc0rpuwpe85hq7zk5ry3", + "runTime": "20:00", + "minimumReward": 1000000 + } + ], + "authzSupport": true } ] From b611fe923a5212db70f9b58cb78a1434b53b333c Mon Sep 17 00:00:00 2001 From: Jacques Vella Critien <30493486+jacquesvcritien@users.noreply.github.com> Date: Wed, 9 Mar 2022 13:59:58 +0100 Subject: [PATCH 010/862] Added more networks These networks have no support for Authz: * impacthub (ixo) * kichain * sommelier --- src/networks.json | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/networks.json b/src/networks.json index db13f5601..effc05a00 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1463,5 +1463,81 @@ } ], "authzSupport": true + }, + { + "name": "impacthub", + "prettyName": "Impact Hub", + "chainId": "impacthub-3", + "prefix": "ixo", + "denom": "uixo", + "restUrl": [ + "https://lcd-impacthub.keplr.app", + "https://proxies.sifchain.finance/api/impacthub-3/rest", + "http://18.220.101.192:1317/", + "https://impacthub.ixo.world/rest/" + ], + "rpcUrl": [ + "https://rpc-impacthub.keplr.app", + "https://proxies.sifchain.finance/api/impacthub-3/rpc", + "http://18.220.101.192:26657", + "https://impacthub.ixo.world/rpc" + ], + "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/ixo.png", + "gasPrice": "0.025uixo", + "operators": [ + { + "address": "ixovaloper1m3kz9q3hp50udyq5tt4rawrexnay4y7jpyvtr9", + "botAddress": "ixo1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9qlc5jx", + "runTime": "20:00", + "minimumReward": 1000000 + } + ], + "authzSupport": false + }, + { + "name": "kichain", + "prettyName": "Ki", + "chainId": "kichain-2", + "prefix": "ki", + "denom": "uxki", + "restUrl": [ + "https://api-mainnet.blockchain.ki" + ], + "rpcUrl": [ + "https://rpc-mainnet.blockchain.ki" + ], + "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/xki.svg", + "gasPrice": "0.025uxki", + "operators": [ + { + "address": "kivaloper1n5cpxhwm5rpgellky5njz67sx8sp0t6ejyxevq", + "botAddress": "ki1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9w8hfjp", + "runTime": "20:00", + "minimumReward": 1000000 + } + ], + "authzSupport": false + }, + { + "name": "sommelier", + "prettyName": "Sommelier", + "chainId": "sommelier-3", + "prefix": "somm", + "denom": "usomm", + "restUrl": [ + "https://lcd-sommelier.keplr.app" + ], + "rpcUrl": [ + "https://rpc-sommelier.keplr.app" + ], + "image": "https://github.com/osmosis-labs/assetlists/raw/main/images/somm.png", + "operators": [ + { + "address": "sommvaloper1cgdlryczzgrk7d4kkeawqg7t6ldz4x84yu305c", + "botAddress": "somm1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9nkf28l", + "runTime": "20:00", + "minimumReward": 1000000 + } + ] } ] From ebc6db6787c566b231ef7ddaa53ca53185ceb49c Mon Sep 17 00:00:00 2001 From: "Sachin S. Kamath" Date: Thu, 10 Mar 2022 03:49:32 +0530 Subject: [PATCH 011/862] networks: fix decimal typo --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 1174d38a7..05605baba 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1329,7 +1329,7 @@ "chainId": "cheqd-mainnet-1", "prefix": "cheq", "denom": "ncheq", - "decimal": 9, + "decimals": 9, "restUrl": [ "https://api.cheqd.net", "https://api.cheqd.blockfend.com" From 67f0b5d011b60b9842b80bbf583101ebe6c69b7b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 10 Mar 2022 18:27:01 +0000 Subject: [PATCH 012/862] Fix bad merge --- src/networks.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/networks.json b/src/networks.json index 21df3fc0b..216264d43 100644 --- a/src/networks.json +++ b/src/networks.json @@ -121,6 +121,7 @@ "runTime": "20:00", "minimumReward": 1000000 }, + { "address": "osmovaloper125kjmvuv5sv7qct2t6wc99h3mp7hpyxw37dcgg", "botAddress": "osmo1tah24yf5pndrwwc8hsdmjahgyf4s5s7nherfmw", "runTime": "1:00", From 8b7918e6ef030b5c8c644b261241047f20dc9278 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Fri, 11 Mar 2022 00:45:49 +0100 Subject: [PATCH 013/862] update kava restake address Signed-off-by: David Pierret --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index b35ec7a58..6c76793ca 100644 --- a/src/networks.json +++ b/src/networks.json @@ -944,7 +944,7 @@ }, { "address": "kavavaloper17jr6wyhm2twyph6d88l5ux945gwmf8vfkhckda", - "botAddress": "kava1avq4zm9cyrhc2cv9e4pn6p2l97y8aaucu0r733", + "botAddress": "kava1v9vae554q55xa4pcfdm0p3kv8nxn50jkxen0mw", "runTime": "23:00", "minimumReward": 1000 }, From d35f3f740e8438f1086fcab01f7438b391da7913 Mon Sep 17 00:00:00 2001 From: Ignacio Iglesias Date: Fri, 11 Mar 2022 01:31:34 +0100 Subject: [PATCH 014/862] fix: Sifchain prefix --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index b35ec7a58..98555214c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1144,7 +1144,7 @@ "name": "sifchain", "prettyName": "Sifchain", "chainId": "sifchain-1", - "prefix": "sifchain", + "prefix": "sif", "denom": "rowan", "decimals": 18, "restUrl": [ From cc376ad819a5840cd3fd024ace6662a7105ff959 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 01:43:48 +0000 Subject: [PATCH 015/862] Dynamic chain registry API via cosmos.directory --- src/components/App.js | 2 +- src/components/DelegateForm.js | 8 +-- src/components/Delegations.js | 6 +- src/components/NetworkFinder.js | 20 ++++-- src/components/NetworkSelect.js | 8 +-- src/components/Wallet.js | 2 +- src/networks.json | 104 -------------------------------- src/utils/Chain.mjs | 63 +++++++++++++++++++ src/utils/Network.mjs | 22 ++++--- 9 files changed, 106 insertions(+), 129 deletions(-) create mode 100644 src/utils/Chain.mjs diff --git a/src/components/App.js b/src/components/App.js index a696b10f8..ec9995544 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -222,7 +222,7 @@ class App extends React.Component { diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index 9d8fe40bb..8336b3340 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -63,7 +63,7 @@ class DelegateForm extends React.Component { const address = this.props.address const validatorAddress = this.props.selectedValidator.operator_address let messages = [] - const decimals = Math.pow(10, this.props.network.data.decimals || 6) + const decimals = Math.pow(10, this.props.network.decimals || 6) if(this.props.redelegate){ messages.push({ typeUrl: "/cosmos.staking.v1beta1.MsgBeginRedelegate", @@ -90,7 +90,7 @@ class DelegateForm extends React.Component { async setAvailableAmount(){ this.setState({error: undefined}) - const decimals = Math.pow(10, this.props.network.data.decimals || 6) + const decimals = Math.pow(10, this.props.network.decimals || 6) const messages = this.buildMessages(parseInt(this.props.availableBalance.amount * 0.95) / decimals) this.props.stargateClient.simulate(this.props.address, messages).then(gas => { const saveTxFeeNum = (this.props.redelegate || this.props.undelegate) ? 0 : 10 @@ -110,7 +110,7 @@ class DelegateForm extends React.Component { } denom(){ - return this.props.network.denom.slice(1).toUpperCase() + return this.props.network.symbol.toUpperCase() } render() { @@ -131,7 +131,7 @@ class DelegateForm extends React.Component { {this.props.availableBalance &&
this.setAvailableAmount()}> - Available: + Available:
} diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 18d0ee21b..e981a391e 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -121,7 +121,7 @@ class Delegations extends React.Component { } getTestGrant(){ - this.props.restClient.getGrants(this.props.network.data.testAddress, this.props.address) + this.props.restClient.getGrants(this.props.network.testAddress, this.props.address) .then( (result) => { }, (error) => { if (error.response && error.response.status === 501) { @@ -290,10 +290,10 @@ class Delegations extends React.Component { {validator.commission.commission_rates.rate * 100}% - + - {denomRewards && } + {denomRewards && }
diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 0607c9208..e58140543 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -1,3 +1,5 @@ +import _ from 'lodash' +import axios from 'axios' import React, { useEffect, useReducer } from 'react'; import { useParams, useNavigate } from "react-router-dom"; import Network from '../utils/Network.mjs' @@ -19,9 +21,16 @@ function NetworkFinder() { {loading: true, networks: [], operators: [], validators: []} ) - const getNetworks = () => { - let data = networksData - return data.filter(el => el.enabled !== false).reduce((a, v) => ({ ...a, [v.name]: v}), {}) + const getNetworks = async () => { + const registryNetworks = await axios.get('https://registry.cosmos.directory') + .then(res => res.data) + .then(data => data.reduce((a, v) => ({ ...a, [v.chain_name]: v}), {})) + + const networks = networksData.filter(el => el.enabled !== false).map(data => { + const registryData = registryNetworks[data.name] || {} + return {...data, ...registryData} + }) + return _.compact(networks).reduce((a, v) => ({ ...a, [v.name]: v}), {}) } const changeNetwork = (network, validators) => { @@ -38,8 +47,9 @@ function NetworkFinder() { useEffect(() => { if(!Object.keys(state.networks).length){ setState({loading: true}) - const networks = getNetworks() - setState({networks: networks}) + getNetworks().then(networks => { + setState({networks: networks}) + }) } }, [state.networks]) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index 9566e2bdd..1ab2324e7 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -55,13 +55,13 @@ function NetworkSelect(props) { const networks = Object.values(props.networks).sort((a, b) => a.name > b.name ? 1 : -1) setOptions({ networks: networks.map(el => { - return {value: el.name, label: el.prettyName, image: el.image, operators: el.operators, authz: el.authzSupport} + return {value: el.name, label: el.pretty_name, image: el.image, operators: el.operators, authz: el.authzSupport} }), network: selectedNetwork && { value: selectedNetwork.name, label: selectedNetwork.prettyName, - image: selectedNetwork.data.image, - operators: selectedNetwork.data.operators, + image: selectedNetwork.image, + operators: selectedNetwork.operators, authz: selectedNetwork.authzSupport } }) @@ -96,7 +96,7 @@ function NetworkSelect(props) { <>
diff --git a/src/networks.json b/src/networks.json index b35ec7a58..3775afc42 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1,10 +1,6 @@ [ { "name": "osmosis", - "prettyName": "Osmosis", - "chainId": "osmosis-1", - "prefix": "osmo", - "denom": "uosmo", "restUrl": [ "https://rest.cosmos.directory/osmosis", "https://lcd-osmosis.blockapsis.com" @@ -14,7 +10,6 @@ "https://osmosis.validator.network", "https://rpc-osmosis.blockapsis.com" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/osmo.svg", "gasPrice": "0uosmo", "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", @@ -138,10 +133,6 @@ }, { "name": "juno", - "prettyName": "Juno", - "chainId": "juno-1", - "prefix": "juno", - "denom": "ujuno", "restUrl": [ "https://rest.cosmos.directory/juno", "https://lcd-juno.itastakers.com" @@ -150,7 +141,6 @@ "https://rpc.cosmos.directory/juno", "https://rpc-juno.itastakers.com" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/juno.svg", "gasPrice": "0.0025ujuno", "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", "operators": [ @@ -279,10 +269,6 @@ }, { "name": "cosmoshub", - "prettyName": "Cosmos Hub", - "chainId": "cosmoshub-4", - "prefix": "cosmos", - "denom": "uatom", "restUrl": [ "https://rest.cosmos.directory/cosmoshub", "https://lcd-cosmoshub.blockapsis.com" @@ -292,7 +278,6 @@ "https://cosmoshub.validator.network", "https://rpc-cosmoshub.blockapsis.com" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/atom.svg", "gasPrice": "0.0025uatom", "testAddress": "cosmos1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz3k8ynk", "operators": [ @@ -343,10 +328,6 @@ }, { "name": "akash", - "prettyName": "Akash", - "chainId": "akashnet-2", - "prefix": "akash", - "denom": "uakt", "restUrl": [ "https://rest.cosmos.directory/akash" ], @@ -354,7 +335,6 @@ "https://rpc.cosmos.directory/akash", "https://akash.technofractal.com:443" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/akt.svg", "testAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", "operators": [ @@ -423,10 +403,6 @@ }, { "name": "chihuahua", - "prettyName": "Chihuahua", - "chainId": "chihuahua-1", - "prefix": "chihuahua", - "denom": "uhuahua", "restUrl": [ "https://rest.cosmos.directory/chihuahua", "https://api.chihuahua.wtf/" @@ -435,7 +411,6 @@ "https://rpc.cosmos.directory/chihuahua", "https://rpc.chihuahua.wtf" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/huahua.png", "gasPrice": "0.025uhuahua", "testAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", @@ -566,10 +541,6 @@ }, { "name": "gravitybridge", - "prettyName": "Gravity Bridge", - "chainId": "gravity-bridge-3", - "prefix": "gravity", - "denom": "ugraviton", "restUrl": [ "https://rest.cosmos.directory/gravitybridge", "https://gravitychain.io:1317" @@ -578,7 +549,6 @@ "https://rpc.cosmos.directory/gravitybridge", "https://gravitychain.io:26657" ], - "image": "https://raw.githubusercontent.com/Gravity-Bridge/Gravity-Docs/main/assets/Graviton-Blue.svg", "testAddress": "gravity1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz4x4uk7", "gasPrice": "0.025ugraviton", "operators": [ @@ -629,17 +599,12 @@ }, { "name": "regen", - "prettyName": "Regen Network", - "chainId": "regen-1", - "prefix": "regen", - "denom": "uregen", "restUrl": [ "https://rest.cosmos.directory/regen" ], "rpcUrl": [ "https://rpc.cosmos.directory/regen" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/regen.png", "gasPrice": "0.002uregen", "testAddress": "regen1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczw5vc9j", "operators": [ @@ -660,17 +625,12 @@ }, { "name": "terra", - "prettyName": "Terra", - "chainId": "columbus-5", - "prefix": "terra", - "denom": "uluna", "restUrl": [ "https://rest.cosmos.directory/terra" ], "rpcUrl": [ "https://rpc.cosmos.directory/terra" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/luna.svg", "testAddress": "terra1rnszu7kumz5lmgdk3fv2pmzt3s8vhcddqep7d4", "gasPrice": "0.015uluna", "operators": [ @@ -697,10 +657,6 @@ }, { "name": "sentinel", - "prettyName": "Sentinel", - "chainId": "sentinelhub-2", - "prefix": "sent", - "denom": "udvpn", "restUrl": [ "https://rest.cosmos.directory/sentinel", "https://lcd-sentinel.keplr.app" @@ -709,7 +665,6 @@ "https://rpc.cosmos.directory/sentinel", "https://rpc-sentinel.keplr.app" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/dvpn.png", "gasPrice": "0.02udvpn", "testAddress": "sent1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz2d3ahe", "operators": [ @@ -760,10 +715,6 @@ }, { "name": "dig", - "prettyName": "Dig Chain", - "chainId": "dig-1", - "prefix": "dig", - "denom": "udig", "restUrl": [ "https://rest.cosmos.directory/dig", "https://api-1-dig.notional.ventures" @@ -772,7 +723,6 @@ "https://rpc.cosmos.directory/dig", "https://rpc-1-dig.notional.ventures" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/dig.png", "gasPrice": "0.0025udig", "testAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", "ownerAddress": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", @@ -824,10 +774,6 @@ }, { "name": "bitcanna", - "prettyName": "BitCanna", - "chainId": "bitcanna-1", - "prefix": "bcna", - "denom": "ubcna", "restUrl": [ "https://rest.cosmos.directory/bitcanna", "https://lcd.bitcanna.io" @@ -837,7 +783,6 @@ "https://rpc.bitcanna.io", "https://rpc-bitcanna.itastakers.com" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/bcna.svg", "gasPrice": "0.001ubcna", "operators": [ { @@ -863,10 +808,6 @@ }, { "name": "emoney", - "prettyName": "e-Money", - "chainId": "emoney-3", - "prefix": "emoney", - "denom": "ungm", "restUrl": [ "https://rest.cosmos.directory/emoney", "https://lcd-emoney.keplr.app", @@ -877,7 +818,6 @@ "https://rpc-emoney.keplr.app", "https://emoney.validator.network" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/ngm.svg", "gasPrice": "0.08ungm", "operators": [ { @@ -921,10 +861,6 @@ }, { "name": "kava", - "prettyName": "Kava", - "chainId": "kava-9", - "prefix": "kava", - "denom": "ukava", "restUrl": [ "https://rest.cosmos.directory/kava", "https://api.data.kava.io/" @@ -933,7 +869,6 @@ "https://rpc.cosmos.directory/kava", "https://rpc.kava.io" ], - "image": "https://raw.githubusercontent.com/Kava-Labs/kava/master/kava-logo.svg", "gasPrice": "0.00008ukava", "operators": [ { @@ -959,10 +894,6 @@ }, { "name": "desmos", - "prettyName": "Desmos", - "chainId": "desmos-mainnet", - "prefix": "desmos", - "denom": "udsm", "restUrl": [ "https://rest.cosmos.directory/desmos", "https://api.mainnet.desmos.network" @@ -971,7 +902,6 @@ "https://rpc.cosmos.directory/desmos", "https://rpc.mainnet.desmos.network" ], - "image": "https://raw.githubusercontent.com/cosmos/chain-registry/master/desmos/images/dsm.png", "gasPrice": "0.001udsm", "operators": [ { @@ -1027,11 +957,6 @@ }, { "name": "cryptoorgchain", - "prettyName": "Crypto.org", - "chainId": "crypto-org-chain-mainnet-1", - "prefix": "cro", - "denom": "basecro", - "decimals": 8, "restUrl": [ "https://rest.cosmos.directory/cryptoorgchain", "https://lcd-crypto-org.keplr.app/" @@ -1041,7 +966,6 @@ "https://mainnet.crypto.org", "https://rpc-crypto-org.keplr.app" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/cro.png", "gasPrice": "0.025basecro", "operators": [ { @@ -1061,12 +985,6 @@ }, { "name": "evmos", - "enabled": false, - "prettyName": "Evmos", - "chainId": "evmos_9001-1", - "prefix": "evmos", - "denom": "aevmos", - "decimals": 18, "restUrl": [ "https://rest.cosmos.directory/evmos", "https://rest.bd.evmos.org:1317" @@ -1075,7 +993,6 @@ "https://rpc.cosmos.directory/evmos", "https://tendermint.bd.evmos.org:26657" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/evmos.png", "operators": [ { "address": "evmosvaloper1shvcjzhcxau6qtlz9w82a646ecwp4hq7ayqt0w", @@ -1142,11 +1059,6 @@ }, { "name": "sifchain", - "prettyName": "Sifchain", - "chainId": "sifchain-1", - "prefix": "sifchain", - "denom": "rowan", - "decimals": 18, "restUrl": [ "https://rest.cosmos.directory/sifchain", "https://api.sifchain.finance" @@ -1155,7 +1067,6 @@ "https://rpc.cosmos.directory/sifchain", "https://rpc.sifchain.finance" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/rowan.png", "operators": [ { "address": "sifvaloper1lnhxf6war6qlldemkqzp0t3g57hpe9a6nh3tyv", @@ -1186,10 +1097,6 @@ }, { "name": "lumnetwork", - "prettyName": "Lum Network", - "chainId": "lum-network-1", - "prefix": "lum", - "denom": "ulum", "restUrl": [ "https://rest.cosmos.directory/lumnetwork", "https://node0.mainnet.lum.network/rest" @@ -1198,7 +1105,6 @@ "https://rpc.cosmos.directory/lumnetwork", "https://node0.mainnet.lum.network/rpc" ], - "image": "https://raw.githubusercontent.com/lum-network/mainnet/master/assets/lum.png", "operators": [ { "address": "lumvaloper1u6jr0pztvsjpvx77rfzmtw49xwzu9kash9slqs", @@ -1271,10 +1177,6 @@ }, { "name": "stargaze", - "prettyName": "Stargaze", - "chainId": "stargaze-1", - "prefix": "stars", - "denom": "ustars", "restUrl": [ "https://rest.cosmos.directory/stargaze", "https://rest.stargaze-apis.com", @@ -1285,7 +1187,6 @@ "https://rpc.stargaze-apis.com", "https://rpc.stargaze.ezstaking.io" ], - "image": "https://raw.githubusercontent.com/public-awesome/assets/main/images/stars.png", "operators": [ { "address": "starsvaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qd0jluj0", @@ -1334,10 +1235,6 @@ }, { "name": "comdex", - "prettyName": "Comdex", - "chainId": "comdex-1", - "prefix": "comdex", - "denom": "ucmdx", "restUrl": [ "https://rest.cosmos.directory/comdex", "https://api.comdex.audit.one/rest", @@ -1348,7 +1245,6 @@ "https://api.comdex.audit.one/rpc/", "https://rpc.comdex.one" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/cmdx.png", "operators": [ { "address": "comdexvaloper195re7mhwh9urewm3rvaj9r7vm6j63c4sd78njd", diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs new file mode 100644 index 000000000..dccebf0c9 --- /dev/null +++ b/src/utils/Chain.mjs @@ -0,0 +1,63 @@ +import axios from 'axios' + +const Chain = async (data) => { + const getChainData = () => { + return axios.get('https://registry.cosmos.directory/' + data.name + '/chain') + .then(res => res.data) + } + + const getTokenData = async () => { + return axios.get('https://registry.cosmos.directory/' + data.name + '/assetlist') + .then(res => res.data) + } + + const chainData = await getChainData(); + const tokenData = await getTokenData(); + + const getChainInfo = () => { + return { + prettyName: data.prettyName || chainData.pretty_name, + chainId: data.chainId || chainData.chain_id, + prefix: data.prefix || chainData.bech32_prefix + } + } + + const getTokenInfo = () => { + const asset = tokenData.assets[0] + const base = asset.denom_units.find(el => el.denom === asset.base) + const token = asset.denom_units.find(el => el.denom === asset.display) + return { + denom: data.denom || base.denom, + symbol: data.symbol || token.denom, + decimals: data.decimals || token.exponent, + image: data.image || (asset.logo_URIs && (asset.logo_URIs.png || asset.logo_URIs.svg)) + } + } + + const { + prettyName, + chainId, + prefix + } = getChainInfo() + + const { + denom, + symbol, + decimals, + image + } = getTokenInfo() + + return { + prettyName, + chainId, + prefix, + denom, + symbol, + decimals, + image, + chainData, + tokenData + } +} + +export default Chain; diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 207980ded..4a1ef8ff6 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -2,13 +2,16 @@ import _ from 'lodash' import RestClient from './RestClient.mjs' import SigningClient from './SigningClient.mjs' import Operator from './Operator.mjs' +import Chain from './Chain.mjs' const Network = async (data) => { - const restClient = await RestClient(data.chainId, data.restUrl) + + const chain = await Chain(data) + const restClient = await RestClient(chain.chainId, data.restUrl) const signingClient = (wallet, key) => { - const gasPrice = data.gasPrice || '0.0025' + data.denom - return SigningClient(data.rpcUrl, data.chainId, gasPrice, wallet, key) + const gasPrice = data.gasPrice || '0.0025' + chain.denom + return SigningClient(data.rpcUrl, chain.chainId, gasPrice, wallet, key) } const getOperator = (operators, operatorAddress) => { @@ -37,16 +40,21 @@ const Network = async (data) => { return { connected: restClient.connected, name: data.name, - prettyName: data.prettyName, - chainId: data.chainId, - prefix: data.prefix, + prettyName: chain.prettyName, + chainId: chain.chainId, + prefix: chain.prefix, gasPrice: data.gasPrice, - denom: data.denom, + denom: chain.denom, + symbol: chain.symbol, + decimals: chain.decimals, + image: chain.image, + testAddress: data.testAddress, restUrl: restClient.restUrl, rpcUrl: data.rpcUrl, operators: data.operators, authzSupport: data.authzSupport, data, + chain, restClient, signingClient, getValidators, From ff72b3a2a9cf1a0e56d2d5f387c267687afdca07 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 01:47:45 +0000 Subject: [PATCH 016/862] Remove registryConfig script --- package.json | 3 +- scripts/registryConfig.mjs | 107 ------------------------------------- 2 files changed, 1 insertion(+), 109 deletions(-) delete mode 100644 scripts/registryConfig.mjs diff --git a/package.json b/package.json index 3a12168d6..629687205 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,7 @@ "start": "npx parcel src/index.html", "prebuild": "rm -rf dist/", "build": "npx parcel build src/index.html", - "autostake": "node scripts/autostake.mjs", - "registryConfig": "node scripts/registryConfig.mjs" + "autostake": "node scripts/autostake.mjs" }, "eslintConfig": { "extends": [ diff --git a/scripts/registryConfig.mjs b/scripts/registryConfig.mjs deleted file mode 100644 index 2dd1f790b..000000000 --- a/scripts/registryConfig.mjs +++ /dev/null @@ -1,107 +0,0 @@ -import axios from 'axios' -import _ from 'lodash' -import fs from 'fs' - -import RestClient from '../src/utils/RestClient.mjs' -import {findAsync} from '../src/utils/Helpers.mjs' - -const registryConfig = (name) => { - const generateConfig = (chain, tokens, existingConfig) => { - if(!existingConfig) existingConfig = {} - - const restApis = chain.apis && chain.apis.rest - const rpcApis = chain.apis && chain.apis.rpc - if(_.isEmpty(restApis) && _.isEmpty(existingConfig.restUrl)){ - console.log('No REST URL found') - return - } - - if(_.isEmpty(rpcApis) && _.isEmpty(existingConfig.rpcUrl)){ - console.log('No RPC URL found') - return - } - - const fees = chain.fees && chain.fees.fee_tokens && chain.fees.fee_tokens[0] || {} - let gasPrice - if(fees && fees.fixed_min_gas_price && fees.denom){ - gasPrice = fees.fixed_min_gas_price.toString() + fees.denom - } - let denom, image - if(tokens){ - const asset = tokens.assets[0] - if(asset){ - denom = asset.denom_units.find(el => el.exponent === 0) - denom = denom && denom.denom - const logos = asset.logo_URIs - image = logos && (logos.svg || logos.png) - } - } - if(!denom) denom = fees.denom - return { - "name": chain.chain_name, - "prettyName": chain.pretty_name, - "chainId": chain.chain_id, - "prefix": chain.bech32_prefix, - "denom": denom || existingConfig.denom, - "restUrl": _.uniq(chain.apis.rest.map(el => el.address), existingConfig.restUrl), - "rpcUrl": _.uniq(chain.apis.rpc.map(el => el.address.replace(/\/$/, '')), existingConfig.rpcUrl), - "image": existingConfig.image || image, - "gasPrice": existingConfig.gasPrice || gasPrice, - "testAddress": existingConfig.testAddress, - "operators": existingConfig.operators || [] - } - } - - const chainData = () => { - return axios.get('https://raw.githubusercontent.com/cosmos/chain-registry/master/' + name + '/chain.json') - .then(res => res.data) - } - - const tokenData = () => { - return axios.get('https://raw.githubusercontent.com/cosmos/chain-registry/master/' + name + '/assetlist.json') - .then(res => res.data) - } - - const testGrants = (restUrl) => { - return axios.get(restUrl + "/cosmos/authz/v1beta1/grants") - .then(res => res.data) - .then(data => data).catch(error => { - return error.response && error.response.status === 400 // expect bad request - }) - } - - const getNetworksData = () => { - let response = fs.readFileSync('src/networks.json'); - return JSON.parse(response); - } - - return { - generateConfig, - chainData, - tokenData, - testGrants, - getNetworksData - } -} - -const chainName = process.argv[2] -const registry = registryConfig(chainName) -const networks = registry.getNetworksData().reduce((a, v) => ({ ...a, [v.name]: v}), {}) -const existingConfig = networks[chainName] -const chain = await registry.chainData() -const tokens = await registry.tokenData() -const config = registry.generateConfig(chain, tokens, existingConfig) -let authzEnabled = false - -if(config.restUrl.length){ - const restClient = await RestClient(config.chainId, config.restUrl) - if(!restClient.restUrl) console.log('No API responses. Unable to check authz status') - authzEnabled = await registry.testGrants(restClient.restUrl) - if(restClient.restUrl && !authzEnabled) console.log('No authz support') -} -_.merge(config, {authzSupport: authzEnabled}) -_.set(networks, chainName, config) -const newConfig = JSON.stringify(Object.values(networks), null, ' ') - -fs.writeFileSync('src/networks.json', newConfig) -console.log('New config written, use git to rollback if needed') From 2a04a34224e3e22ee8a97f2b059b30dcca825352 Mon Sep 17 00:00:00 2001 From: "Sachin S. Kamath" Date: Fri, 11 Mar 2022 12:41:18 +0530 Subject: [PATCH 017/862] networks: fix cheqd prefix --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 05605baba..6f5b67f60 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1327,7 +1327,7 @@ "name": "cheqd", "prettyName": "cheqd", "chainId": "cheqd-mainnet-1", - "prefix": "cheq", + "prefix": "cheqd", "denom": "ncheq", "decimals": 9, "restUrl": [ From 2067782c53fa6ff44fb87c096123705b943c6506 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 10:47:37 +0000 Subject: [PATCH 018/862] Reduce delegation batch size for cosmoshub etc --- scripts/autostake.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 6f8e83a0b..205164bef 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -126,7 +126,7 @@ class Autostake { } getDelegations(client) { - return client.restClient.getAllValidatorDelegations(client.operator.address, 250, (pages) => { + return client.restClient.getAllValidatorDelegations(client.operator.address, 100, (pages) => { console.log("...batch", pages.length) }).catch(error => { console.log("ERROR:", error.message || error) From d1d97123f6350cc66d7141f3831c8d65fa7b327e Mon Sep 17 00:00:00 2001 From: JiuKelo Date: Fri, 11 Mar 2022 13:29:29 +0200 Subject: [PATCH 019/862] Add 01node operator Add 01node operator for Cosmos, Osmosis and Juno --- src/networks.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/networks.json b/src/networks.json index b35ec7a58..ddda95f73 100644 --- a/src/networks.json +++ b/src/networks.json @@ -19,6 +19,12 @@ "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "operators": [ + { + "address": "osmovaloper17mggn4znyeyg25wd7498qxl7r2jhgue8td054x", + "botAddress": "osmo1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5p5wwvc", + "runTime": "18:00", + "minimumReward": 1000 + }, { "address": "osmovaloper1pxphtfhqnx9ny27d53z4052e3r76e7qq495ehm", "botAddress": "osmo194ytn50yhh67rdha8akhs7c6zulnz4n2pv6n63", @@ -154,6 +160,12 @@ "gasPrice": "0.0025ujuno", "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", "operators": [ + { + "address": "junovaloper17mggn4znyeyg25wd7498qxl7r2jhgue8swpngk", + "botAddress": "juno1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5la79ak", + "runTime": "18:00", + "minimumReward": 1000 + }, { "address": "junovaloper1zxx8j75ngm8m38v9l5wreaavwnsuun7gcq5cu8", "botAddress": "juno1n3m6rj0w9lgzg7leppnaru2xguraxhqcn4eyv6", @@ -296,6 +308,12 @@ "gasPrice": "0.0025uatom", "testAddress": "cosmos1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz3k8ynk", "operators": [ + { + "address": "cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq", + "botAddress": "cosmos1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5f0a762", + "runTime": "18:00", + "minimumReward": 1000 + }, { "address": "cosmosvaloper1vvwtk805lxehwle9l4yudmq6mn0g32px9xtkhc", "botAddress": "cosmos1eml7agwd5x084ennlg6apvskufzrlvnrks6h4e", From 56f70d76570ef3b04be4781948b597815bdfdedf Mon Sep 17 00:00:00 2001 From: LPX Date: Sat, 12 Mar 2022 02:45:42 +0900 Subject: [PATCH 020/862] Add ShapeShift Validators for Osmosis and Cosmos --- src/networks.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/networks.json b/src/networks.json index b35ec7a58..57609de12 100644 --- a/src/networks.json +++ b/src/networks.json @@ -132,6 +132,12 @@ "botAddress": "osmo14gra89zm8753pfuhvujjulmtkf2qq70f3l9gtx", "runTime": "21:28", "minimumReward": 1000 + }, + { + "address": "osmovaloper1xf9zpq5kpxks49cg606tzd8qstaykxgt2vs0d5", + "botAddress": "osmo19pqrxrl6n0g0mky4y79hlfzchprmsp5jmu2t6g", + "runTime": "19:00", + "minimumReward": 1000 } ], "authzSupport": true @@ -337,6 +343,12 @@ "botAddress": "cosmos1dg8u5qll7fjm9nnwqns0ullhtc6g552ugd0eqj", "runTime": "9:00", "minimumReward": 10000 + }, + { + "address": "cosmosvaloper199mlc7fr6ll5t54w7tts7f4s0cvnqgc59nmuxf", + "botAddress": "cosmos19pqrxrl6n0g0mky4y79hlfzchprmsp5jn8emv6", + "runTime": "19:30", + "minimumReward": 10000 } ], "authzSupport": true From 6fe29a77f244423439be29179568b5596953d8b3 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 18:19:24 +0000 Subject: [PATCH 021/862] Remove superseded cosmos.directory integration --- src/utils/Network.mjs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 355e155a2..4a1ef8ff6 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -1,5 +1,4 @@ import _ from 'lodash' -import axios from 'axios' import RestClient from './RestClient.mjs' import SigningClient from './SigningClient.mjs' import Operator from './Operator.mjs' @@ -38,20 +37,6 @@ const Network = async (data) => { return restClient.getAllValidators(150) } - // Get Chain Registry to extract more information - const chainData = () => { - return axios.get('https://registry.cosmos.directory/' + data.name) - .then(res => res.data) - } - - const tokenData = async () => { - return axios.get('https://registry.cosmos.directory/' + data.name + '/assetlist') - .then(res => res.data) - } - - const chainRegistry = await chainData(); - const tokenRegistry = await tokenData(); - return { connected: restClient.connected, name: data.name, @@ -68,8 +53,6 @@ const Network = async (data) => { rpcUrl: data.rpcUrl, operators: data.operators, authzSupport: data.authzSupport, - chainRegistry, - tokenRegistry, data, chain, restClient, From 7816b1928fe6fd2cc112be5a21ddff9f7e2cbf59 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 18:35:47 +0000 Subject: [PATCH 022/862] Update with chain registry integration --- src/components/App.js | 95 ++++++++++++++++++++----------------------- src/networks.json | 9 ++-- src/utils/Chain.mjs | 16 +++++--- src/utils/Network.mjs | 2 + 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 80c320a76..1a253d6eb 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -75,8 +75,6 @@ class App extends React.Component { return this.setState({ error: false, - chainId: network.chainId, - denom: network.denom, restClient: network.restClient }) } @@ -91,47 +89,16 @@ class App extends React.Component { error: 'Could not connect to any available API servers' }) } + const chainId = this.props.network.chainId try { - await window.keplr.enable(this.state.chainId); - } catch (e) { + await window.keplr.enable(chainId); + } catch (e) { console.log(e.message, e) - await window.keplr.experimentalSuggestChain({ - rpc: this.props.network.rpcUrl[0], - rest: this.props.network.restUrl, - chainId: this.state.chainId, - chainName: this.props.network.prettyName, - stakeCurrency: { - coinDenom: this.props.network.tokenRegistry.assets[0].symbol, - coinMinimalDenom: this.props.network.denom, - coinDecimals: false ? this.props.network.decimals: 6, - coinGeckoId: this.props.network.tokenRegistry.assets[0].coingecko_id - }, - bip44: { coinType: this.props.network.chainRegistry.slip44 }, - walletUrlForStaking: "https://restake.app/" + this.props.network.prettyName, - bech32Config: { - bech32PrefixAccAddr: this.props.network.prefix, - bech32PrefixAccPub: this.props.network.prefix + "pub", - bech32PrefixValAddr: this.props.network.prefix + "valoper", - bech32PrefixValPub: this.props.network.prefix + "valoperpub", - bech32PrefixConsAddr: this.props.network.prefix + "valcons", - bech32PrefixConsPub: this.props.network.prefix + "valconspub" - }, - currencies: [{ - coinDenom: this.props.network.tokenRegistry.assets[0].symbol, - coinMinimalDenom: this.props.network.denom, - coinDecimals: false ? this.props.network.decimals: 6, - coinGeckoId: this.props.network.tokenRegistry.assets[0].coingecko_id - }], - feeCurrencies:[{ - coinDenom: this.props.network.tokenRegistry.assets[0].symbol, - coinMinimalDenom: this.props.network.denom, - coinDecimals: false ? this.props.network.decimals: 6, - coinGeckoId: this.props.network.tokenRegistry.assets[0].coingecko_id - }], - }) + await this.suggestChain(this.props.network) } if (window.getOfflineSigner){ - const offlineSigner = await window.getOfflineSignerAuto(this.state.chainId) + const offlineSigner = await window.getOfflineSignerAuto(chainId) + const key = await window.keplr.getKey(chainId); const stargateClient = await this.props.network.signingClient(offlineSigner, key) if(!stargateClient.connected){ this.setState({ @@ -160,6 +127,34 @@ class App extends React.Component { }) } + suggestChain(network){ + const currency = { + coinDenom: network.symbol, + coinMinimalDenom: network.denom, + coinDecimals: network.decimals, + coinGeckoId: network.coinGeckoId + } + return window.keplr.experimentalSuggestChain({ + rpc: network.rpcUrl[0], + rest: network.restUrl, + chainId: network.chainId, + chainName: network.prettyName, + stakeCurrency: currency, + bip44: { coinType: network.slip44 }, + walletUrlForStaking: "https://restake.app/" + network.name, + bech32Config: { + bech32PrefixAccAddr: network.prefix, + bech32PrefixAccPub: network.prefix + "pub", + bech32PrefixValAddr: network.prefix + "valoper", + bech32PrefixValPub: network.prefix + "valoperpub", + bech32PrefixConsAddr: network.prefix + "valcons", + bech32PrefixConsPub: network.prefix + "valconspub" + }, + currencies: [currency], + feeCurrencies:[currency] + }) + } + getValidatorImage(network, validatorAddress, expireCache){ const images = this.state.validatorImages[network.name] || {} if(images[validatorAddress]){ @@ -296,17 +291,17 @@ class App extends React.Component { ) )} {this.state.address && - <> - - + <> + + }

diff --git a/src/networks.json b/src/networks.json index 68874c8e7..7fab92992 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1304,19 +1304,16 @@ }, { "name": "cheqd", - "prettyName": "cheqd", - "chainId": "cheqd-mainnet-1", - "prefix": "cheq", - "denom": "ncheq", "restUrl": [ + "https://rest.cosmos.directory/cheqd", "https://api.cheqd.net", "https://api.cheqd.blockfend.com" ], "rpcUrl": [ + "https://rpc.cosmos.directory/cheqd", "https://rpc.cheqd.net", "https://rpc.cheqd.blockfend.com" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/osmosis-frontend/master/public/assets/tokens/cheq.svg", "operators": [ { "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", @@ -1327,4 +1324,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index dccebf0c9..d94d4cf11 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -18,7 +18,8 @@ const Chain = async (data) => { return { prettyName: data.prettyName || chainData.pretty_name, chainId: data.chainId || chainData.chain_id, - prefix: data.prefix || chainData.bech32_prefix + prefix: data.prefix || chainData.bech32_prefix, + slip44: data.slip44 || chainData.slip44 || 118 } } @@ -29,32 +30,37 @@ const Chain = async (data) => { return { denom: data.denom || base.denom, symbol: data.symbol || token.denom, - decimals: data.decimals || token.exponent, - image: data.image || (asset.logo_URIs && (asset.logo_URIs.png || asset.logo_URIs.svg)) + decimals: data.decimals || token.exponent || 6, + image: data.image || (asset.logo_URIs && (asset.logo_URIs.png || asset.logo_URIs.svg)), + coinGeckoId: asset.coingecko_id } } const { prettyName, chainId, - prefix + prefix, + slip44 } = getChainInfo() const { denom, symbol, decimals, - image + image, + coinGeckoId } = getTokenInfo() return { prettyName, chainId, prefix, + slip44, denom, symbol, decimals, image, + coinGeckoId, chainData, tokenData } diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 4a1ef8ff6..8929cfe36 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -43,11 +43,13 @@ const Network = async (data) => { prettyName: chain.prettyName, chainId: chain.chainId, prefix: chain.prefix, + slip44: chain.slip44, gasPrice: data.gasPrice, denom: chain.denom, symbol: chain.symbol, decimals: chain.decimals, image: chain.image, + coinGeckoId: chain.coinGeckoId, testAddress: data.testAddress, restUrl: restClient.restUrl, rpcUrl: data.rpcUrl, From deebb82e4b7b81300e4794dc2e1efac03f95ff92 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 18:44:32 +0000 Subject: [PATCH 023/862] Remove cheqd in favour of dedicated PR --- src/networks.json | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/networks.json b/src/networks.json index 7fab92992..9fa7f19bb 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1067,7 +1067,7 @@ "https://rpc.sifchain.finance" ], "operators": [ - { + { "address": "sifvaloper1lnhxf6war6qlldemkqzp0t3g57hpe9a6nh3tyv", "botAddress": "sifchain194ytn50yhh67rdha8akhs7c6zulnz4n2zjs8hn", "runTime": "21:00", @@ -1301,27 +1301,5 @@ } ], "authzSupport": false - }, - { - "name": "cheqd", - "restUrl": [ - "https://rest.cosmos.directory/cheqd", - "https://api.cheqd.net", - "https://api.cheqd.blockfend.com" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/cheqd", - "https://rpc.cheqd.net", - "https://rpc.cheqd.blockfend.com" - ], - "operators": [ - { - "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", - "botAddress": "cheqd1ypntyymvjg90ntxwes3hxtmuvvh4fcr8w082sd", - "runTime": "21:00", - "minimumReward": 1000 - } - ], - "authzSupport": true } ] From 2083706eca6caf4e0bdddca87527c1ed983fc3f9 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 20:29:16 +0000 Subject: [PATCH 024/862] Add cosmos.directory URLs for cheqd and umee --- src/networks.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/networks.json b/src/networks.json index d540a244b..d23b0e6c1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1310,10 +1310,12 @@ "denom": "ncheq", "decimals": 9, "restUrl": [ + "https://rest.cosmos.directory/cheqd", "https://api.cheqd.net", "https://api.cheqd.blockfend.com" ], "rpcUrl": [ + "https://rpc.cosmos.directory/cheqd", "https://rpc.cheqd.net", "https://rpc.cheqd.blockfend.com" ], @@ -1335,6 +1337,7 @@ "prefix": "umee", "denom": "uumee", "restUrl": [ + "https://rest.cosmos.directory/umee", "https://api.aphrodite.main.network.umee.cc", "https://api.apollo.main.network.umee.cc", "https://api.artemis.main.network.umee.cc", @@ -1343,6 +1346,7 @@ "https://api.bottlenose.main.network.umee.cc" ], "rpcUrl": [ + "https://rpc.cosmos.directory/umee", "https://rpc.aphrodite.main.network.umee.cc", "https://rpc.apollo.main.network.umee.cc", "https://rpc.artemis.main.network.umee.cc", From e8a82813488f68de7f010221a9d364bd82d93cfb Mon Sep 17 00:00:00 2001 From: EB Date: Fri, 11 Mar 2022 13:19:39 -0800 Subject: [PATCH 025/862] Add FreshJUNO, FreshLUNA, FreshATOMS --- src/networks.json | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 235d7a44e..23bc3db35 100644 --- a/src/networks.json +++ b/src/networks.json @@ -130,8 +130,8 @@ { "address": "osmovaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjp38f93", "botAddress": "osmo14gra89zm8753pfuhvujjulmtkf2qq70f3l9gtx", - "runTime": "21:28", - "minimumReward": 1000 + "runTime": "17:28", + "minimumReward": 10000 } ], "authzSupport": true @@ -273,6 +273,12 @@ "botAddress": "juno1hl0jgxre5z0nkyjj07c5vfdy44yk44at0dphgp", "runTime": "22:30", "minimumReward": 1000 + }, + { + "address": "junovaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgj6jfwcp", + "botAddress": "juno14gra89zm8753pfuhvujjulmtkf2qq70f0k4r6g", + "runTime": "17:28", + "minimumReward": 10000 } ], "authzSupport": true @@ -337,6 +343,12 @@ "botAddress": "cosmos1dg8u5qll7fjm9nnwqns0ullhtc6g552ugd0eqj", "runTime": "9:00", "minimumReward": 10000 + }, + { + "address": "cosmosvaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjkfg0gh", + "botAddress": "cosmos14gra89zm8753pfuhvujjulmtkf2qq70feykca5", + "runTime": "17:28", + "minimumReward": 10000 } ], "authzSupport": true @@ -685,6 +697,12 @@ "botAddress": "terra1hl0jgxre5z0nkyjj07c5vfdy44yk44atlmcvda", "runTime": "22:30", "minimumReward": 1000 + }, + { + "address": "terravaloper1audgfvmgt0js54p3s8kj3r40uwej6vy2tv6rrw", + "botAddress": "terra1hz2dutqm7e0t7q3h5cm9d8lchztskm8pekadus", + "runTime": "17:28", + "minimumReward": 5000 } ], "authzSupport": true @@ -1310,6 +1328,12 @@ "botAddress": "stars1m5hgzum68rjf4c7zhezgkj8hlnmr0kghzavkpp", "runTime": "10:00", "minimumReward": 10000 + }, + { + "address": "starsvaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjvlln5d", + "botAddress": "stars14gra89zm8753pfuhvujjulmtkf2qq70fdcp9k9", + "runTime": "17:28", + "minimumReward": 10000 } ], "authzSupport": true From 5f4cd320c56a8268ea343038ee774d2170ac5a2e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 21:21:34 +0000 Subject: [PATCH 026/862] Update with chain registry integration --- src/networks.json | 67 +++++++++++------------------------------------ 1 file changed, 15 insertions(+), 52 deletions(-) diff --git a/src/networks.json b/src/networks.json index 06b9c4c07..145d86e0a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1376,22 +1376,14 @@ }, { "name": "cheqd", - "prettyName": "cheqd", - "chainId": "cheqd-mainnet-1", - "prefix": "cheqd", - "denom": "ncheq", - "decimals": 9, "restUrl": [ "https://rest.cosmos.directory/cheqd", - "https://api.cheqd.net", - "https://api.cheqd.blockfend.com" + "https://api.cheqd.net" ], "rpcUrl": [ "https://rpc.cosmos.directory/cheqd", - "https://rpc.cheqd.net", - "https://rpc.cheqd.blockfend.com" + "https://rpc.cheqd.net" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/osmosis-frontend/master/public/assets/tokens/cheq.svg", "operators": [ { "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", @@ -1404,10 +1396,6 @@ }, { "name": "umee", - "prettyName": "umee", - "chainId": "umee-1", - "prefix": "umee", - "denom": "uumee", "restUrl": [ "https://rest.cosmos.directory/umee", "https://api.aphrodite.main.network.umee.cc", @@ -1426,7 +1414,6 @@ "https://rpc.beaked.main.network.umee.cc", "https://rpc.bottlenose.main.network.umee.cc" ], - "image": "https://raw.githubusercontent.com/cosmos/chain-registry/master/umee/images/umee.png", "operators": [ { "address": "umeevaloper14vmuhgm04ffvzts9jxm2wy5d538g96ru9fsg5v", @@ -1439,17 +1426,14 @@ }, { "name": "bitsong", - "prettyName": "BitSong", - "chainId": "bitsong-2b", - "prefix": "bitsong", - "denom": "ubtsg", "restUrl": [ + "https://rest.cosmos.directory/bitsong", "https://lcd-bitsong.itastakers.com" ], "rpcUrl": [ + "https://rpc.cosmos.directory/bitsong", "https://rpc-bitsong.itastakers.com" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/btsg.svg", "operators": [ { "address": "bitsongvaloper129y6g55wz7mz9vs2nudfvfdaun80fuphnltfpf", @@ -1462,17 +1446,14 @@ }, { "name": "persistence", - "prettyName": "Persistence", - "chainId": "core-1", - "prefix": "persistence", - "denom": "uxprt", "restUrl": [ + "https://rest.cosmos.directory/persistence", "https://rest.core.persistence.one" ], "rpcUrl": [ + "https://rpc.cosmos.directory/persistence", "https://rpc.core.persistence.one" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/xprt.png", "operators": [ { "address": "persistencevaloper1pdse5rr5njkkka6qeu5m8u704h6z67w5r700vn", @@ -1485,17 +1466,12 @@ }, { "name": "agoric", - "prettyName": "Agoric", - "chainId": "agoric-3", - "prefix": "agoric", - "denom": "ubld", "restUrl": [ - "https://main.api.agoric.net:443" + "https://rest.cosmos.directory/agoric" ], "rpcUrl": [ - "https://main.rpc.agoric.net:443" + "https://rpc.cosmos.directory/agoric" ], - "image": "https://raw.githubusercontent.com/cosmos/chain-registry/master/agoric/images/bld.png", "operators": [ { "address": "agoricvaloper1kh8nwuee2y353u6nmv8jffpvhtzs7xu8f4klam", @@ -1508,23 +1484,17 @@ }, { "name": "impacthub", - "prettyName": "Impact Hub", - "chainId": "impacthub-3", - "prefix": "ixo", - "denom": "uixo", "restUrl": [ + "https://rest.cosmos.directory/impacthub", "https://lcd-impacthub.keplr.app", "https://proxies.sifchain.finance/api/impacthub-3/rest", - "http://18.220.101.192:1317/", "https://impacthub.ixo.world/rest/" ], "rpcUrl": [ + "https://rpc.cosmos.directory/impacthub", "https://rpc-impacthub.keplr.app", - "https://proxies.sifchain.finance/api/impacthub-3/rpc", - "http://18.220.101.192:26657", - "https://impacthub.ixo.world/rpc" + "https://proxies.sifchain.finance/api/impacthub-3/rpc" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/ixo.png", "gasPrice": "0.025uixo", "operators": [ { @@ -1538,17 +1508,13 @@ }, { "name": "kichain", - "prettyName": "Ki", - "chainId": "kichain-2", - "prefix": "ki", - "denom": "uxki", "restUrl": [ + "https://rest.cosmos.directory/kichain", "https://api-mainnet.blockchain.ki" ], "rpcUrl": [ - "https://rpc-mainnet.blockchain.ki" + "https://rpc.cosmos.directory/kichain" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/xki.svg", "gasPrice": "0.025uxki", "operators": [ { @@ -1562,17 +1528,14 @@ }, { "name": "sommelier", - "prettyName": "Sommelier", - "chainId": "sommelier-3", - "prefix": "somm", - "denom": "usomm", "restUrl": [ + "https://rest.cosmos.directory/sommelier", "https://lcd-sommelier.keplr.app" ], "rpcUrl": [ + "https://rpc.cosmos.directory/sommelier", "https://rpc-sommelier.keplr.app" ], - "image": "https://github.com/osmosis-labs/assetlists/raw/main/images/somm.png", "operators": [ { "address": "sommvaloper1cgdlryczzgrk7d4kkeawqg7t6ldz4x84yu305c", From 8ced2966eed642020d6483ff4563ff9edd7affd2 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 21:34:59 +0000 Subject: [PATCH 027/862] Increase reward timeout slightly --- src/components/Delegations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index e981a391e..0903ff7d1 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -163,7 +163,7 @@ class Delegations extends React.Component { setTimeout(() => { this.props.getDelegations() this.getRewards() - }, 5_000) + }, 6_000) } setClaimLoading(value){ From 750a1234067291d8039070fe380fd9a946188c1b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 11 Mar 2022 21:38:47 +0000 Subject: [PATCH 028/862] Fix autostake log bug --- scripts/autostake.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 205164bef..4a1a803bb 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -86,7 +86,7 @@ class Autostake { const accounts = await wallet.getAccounts() const botAddress = accounts[0].address - console.log(data.prettyName, 'bot address is', botAddress) + console.log(network.prettyName, 'bot address is', botAddress) const client = await network.signingClient(wallet) if(client.connected){ @@ -94,7 +94,7 @@ class Autostake { } let validators = {} - if(data.operators.find(el => el.botAddress === botAddress)){ + if(network.operators.find(el => el.botAddress === botAddress)){ validators = await network.getValidators() } const operators = network.getOperators(validators) From a582310c7eb6b14fe4c555fa383de43e907dc26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Iglesias=20Castre=C3=B1o?= Date: Sat, 12 Mar 2022 00:44:47 +0100 Subject: [PATCH 029/862] fix: increase regen gas price --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 98555214c..2003bb405 100644 --- a/src/networks.json +++ b/src/networks.json @@ -640,7 +640,7 @@ "https://rpc.cosmos.directory/regen" ], "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/regen.png", - "gasPrice": "0.002uregen", + "gasPrice": "0.03uregen", "testAddress": "regen1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczw5vc9j", "operators": [ { From d1ad6110cf291267470406c246dc15bb759b7b10 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 12 Mar 2022 01:20:16 +0000 Subject: [PATCH 030/862] Update and tidy up README --- README.md | 85 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 31048353a..e674109df 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Becoming an operator is extremely easy. You need to do three things: ### Setup a bot wallet -Generate a new hot wallet you will use to automatically carry out the staking transactions. The mnemonic will need to be provided to the script so **use a dedicated wallet and only keep enough funds for transaction fees**. +Generate a new hot wallet you will use to automatically carry out the staking transactions. The mnemonic will need to be provided to the script so **use a dedicated wallet and only keep enough funds for transaction fees**. The ONLY menmonic required here is for the hot wallet, do not put your validator operator mnemonic anywhere. You only need a single mnemonic for multiple Cosmos chains, and the script will check each network in the [networks.json](./src/networks.json) file for a matching bot address. @@ -50,7 +50,7 @@ Docker Compose: [docs.docker.com/compose/install](https://docs.docker.com/compos Clone the repository and copy the sample `.env` file ready for your mnemonic. -``` +```bash git clone https://github.com/eco-stake/restake cd restake cp .env.sample .env @@ -64,13 +64,13 @@ Running the autostake script manually is then simple. Note you might need `sudo` depending on your docker install. -``` +```bash docker-compose run app npm run autostake ``` Pass a network name to run the script for a single network at a time. -``` +```bash docker-compose run app npm run autostake osmosis ``` @@ -80,7 +80,7 @@ REStake is MVP. Very MVP. Updates are happening all the time and there are bugs Update your local repository and pre-build your Docker containers with the following commands: -``` +```bash git pull docker-compose build ``` @@ -88,30 +88,33 @@ docker-compose build ### Setting up Cron to make sure the script runs daily You should setup your script to run at the same time each day. -2 methodes are described bellow, The `crontab` one, and the `systemd-timer` one. +2 methods are described below; using `crontab` or using `systemd-timer`. -#### **using `crontab` (deprecated)** +In both cases, ensure your system time is correct and you know what time the script will run in UTC, as that will be required later. Both examples below are for 21:00. -``` +Don't forget to [update often](#updating-your-local-version)! + +#### Using `crontab` + +```bash crontab -e 0 21 * * * /bin/bash -c "cd restake && docker-compose run app npm run autostake" > ./restake.log 2>&1 ``` -#### **using `systemd-timer` (prefered)** +#### Using `systemd-timer` -Systemd-timer allow to run a onshot service with specified rules. +Systemd-timer allow to run a one-off service with specified rules. - * Create a systemd unit file: +##### Create a systemd unit file: -The unit file describe the application to run. -We define a dependency with the timer with the `Wants` statement. +The unit file describe the application to run. We define a dependency with the timer with the `Wants` statement. ```bash $ sudo vim /etc/systemd/system/restake.service ``` -``` +```bash [Unit] Description=stakebot service with docker compose Requires=docker.service @@ -127,7 +130,7 @@ ExecStart=/usr/bin/docker-compose run --rm app npm run autostake WantedBy=multi-user.target ``` - * Create a systemd timer file: +##### Create a systemd timer file: The timer file defines the rules for running the restake service every day. All rules are described in the [systemd documentation](https://www.freedesktop.org/software/systemd/man/systemd.timer.html). @@ -135,7 +138,7 @@ The timer file defines the rules for running the restake service every day. All $ sudo vim /etc/systemd/system/restake.timer ``` -``` +```bash [Unit] Description=Restake bot timer @@ -147,13 +150,15 @@ OnCalendar=*-*-* 21:00:00 WantedBy=timers.target ``` - * Enable and start everything +##### Enable and start everything + ```bash $ systemctl enable restake.service $ systemctl enable restake.timer $ systemctl start restake.timer ``` - * Check your timer + +##### Check your timer `$ systemctl status restake.timer`

 restake.timer - Restake bot timer
@@ -171,17 +176,15 @@ TriggeredBy:  restake.timer
    Main PID: 86925 (code=exited, status=0/SUCCESS)
 
- -Don't forget to [update often](#updating-your-local-version)! - - -### Overriding networks config locally +### Overriding networks config locally/use your own node You will likely want to customise your networks config, e.g. to set your own node URLs to ensure your autocompounding script completes successfully. +Note that REStake requires a node with indexing enabled and minimum gas prices matching the networks.json gas price (or your local override). + Create a `src/networks.local.json` file with JSON in the following format: -``` +```json { "osmosis": { "prettyName": "Foobar", @@ -203,7 +206,7 @@ Any values you specify will override the `networks.json` file. Arrays will not b You now need to update the [networks.json](./src/networks.json) file at `./src/networks.json` to add your operator to any networks you want to auto-compound for. Check the existing file for examples, but the operators array is simple: -``` +```json "operators": [{ "address": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "botAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", @@ -212,7 +215,9 @@ You now need to update the [networks.json](./src/networks.json) file at `./src/n }, ``` -`address` is your validator's address, and `botAddress` is the address from your new hot wallet you generated earlier. Repeat this for all networks you want to REStake for. +`address` is your validator's address, and `botAddress` is the address from your new hot wallet you generated earlier. `runTime` is the time in UTC that your bot will run (multiple planned in the future). `minimumReward` is the minimum reward to trigger autostaking, otherwise the address be skipped. + +Repeat this config for all networks you want to REStake for. Note that the `botAddress` is the address which will be granted by the delegator to carry out their restaking transactions if it wasn't clear. @@ -222,19 +227,35 @@ You can now submit your `networks.json` update to the repository in a pull reque ## Adding/updating a network -A script is included to generate the config needed to add a new chain from the [Chain Registry](https://github.com/cosmos/chain-registry). It will update the config file in-place, retaining the important information like operators etc. It can be used to update and add a chain, just check the changes with Git. +Network information is sourced from the [Chain Registry](https://github.com/cosmos/chain-registry) via the [registry.cosmos.directory](https://registry.cosmos.directory) API. The `networks.json` defines which chains appear in REStake; so long as the chain name matches the directory name from the Chain Registry, all chain information will be sourced automatically. -Make sure you match the directory name from Chain Registry. +To add a network to REStake, add the required information to `networks.json` as follows: +```json +{ + "name": "osmosis", + "restUrl": [ + "https://rest.cosmos.directory/osmosis", + ], + "rpcUrl": [ + "https://rpc.cosmos.directory/osmosis", + ], + "gasPrice": "0.025uosmo", + "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", + "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", + "operators": [ + ], + "authzSupport": true +} ``` -docker-compose run app npm run registryConfig cosmoshub -``` + +Note that most attributes from Chain Registry can be overriden by defining the camelCase version in networks.json. ## Running the UI Run the UI using docker with one line: -``` +```bash docker run -p 80:80 -t ghcr.io/eco-stake/restake ``` @@ -244,7 +265,7 @@ Alternative run from source using `docker-compose up` or `npm start`. The REStake UI is both validator and network agnostic. Any validator can be added as an operator and run this tool to provide an auto-compounding service to their delegators, but they can also run their own UI if they choose and adjust the branding to suit themselves. -For this to work, we need a common source of chain information, and a common source of 'operator' information. Currently this the [networks.json](./src/networks.json) file in this repository. A helper script is included to add a chain from the [Chain Registry](https://github.com/cosmos/chain-registry), and this integration will be taken further soon. +For this to work, we need a common source of chain information, and a common source of 'operator' information. Chain information is sourced from the [Chain Registry](https://github.com/cosmos/chain-registry), via an API provided by [cosmos.directory](https://registry.cosmos.directory). Operator information currently lives in the [networks.json](./src/networks.json) file in this repository. If you fork this repository to provide your own UI, please keep up to date with the upstream to ensure you have the latest [networks.json](./src/networks.json) to include all operators. Some honesty is needed until we have a more decentralised solution. From f9b34e6d57db080e12efa4fab1037fc50ac15384 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 12 Mar 2022 03:04:45 +0000 Subject: [PATCH 031/862] Add rm flag to docker-compose run examples --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e674109df..e1979eb70 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,13 @@ Running the autostake script manually is then simple. Note you might need `sudo` depending on your docker install. ```bash -docker-compose run app npm run autostake +docker-compose run --rm app npm run autostake ``` Pass a network name to run the script for a single network at a time. ```bash -docker-compose run app npm run autostake osmosis +docker-compose run --rm app npm run autostake osmosis ``` ### Updating your local version @@ -99,7 +99,7 @@ Don't forget to [update often](#updating-your-local-version)! ```bash crontab -e -0 21 * * * /bin/bash -c "cd restake && docker-compose run app npm run autostake" > ./restake.log 2>&1 +0 21 * * * /bin/bash -c "cd restake && docker-compose run --rm app npm run autostake" > ./restake.log 2>&1 ``` #### Using `systemd-timer` From 353808bfdc45134cbc0c1713f98051679d4ad668 Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Fri, 11 Mar 2022 23:26:54 -0400 Subject: [PATCH 032/862] Added Cheqd + Evmos for WhisperNode --- src/networks.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/networks.json b/src/networks.json index 4a1962d8a..3f658248e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1106,6 +1106,12 @@ "botAddress": "evmos16e67hq4eqgnfw6pxu8v33mz259z5r30d9v2uq2", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "evmosvaloper1chx7v975g72xuw8kdpjt94dh35daqqfyc37kys", + "botAddress": "evmos1k0fpumkxl35p3se0n52e6dc3mh8vy24kn0uqsg", + "runTime": "21:00", + "minimumReward": 1000 }, { "address": "evmosvaloper14zatq4jagqtm9ejgvglnv0t364d88u80futp65", @@ -1444,6 +1450,12 @@ "botAddress": "cheqd1ypntyymvjg90ntxwes3hxtmuvvh4fcr8w082sd", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "cheqdvaloper1qsp3a2qd6km9g0hczsac8279wcwmzmvzgvre8w", + "botAddress": "cheqd1e44rluarkdw56dy2turnwjtvtg4wqvs0ufeyu5", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": true From 86b175c286d400e77a8e050536e488d053917e99 Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Fri, 11 Mar 2022 23:47:48 -0400 Subject: [PATCH 033/862] fixed indent --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 3f658248e..4925ed97b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1107,7 +1107,7 @@ "runTime": "21:00", "minimumReward": 1000 }, - { + { "address": "evmosvaloper1chx7v975g72xuw8kdpjt94dh35daqqfyc37kys", "botAddress": "evmos1k0fpumkxl35p3se0n52e6dc3mh8vy24kn0uqsg", "runTime": "21:00", From 0449dae317fe3ce40b05e865aa22bed6f9c0b7b3 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Sat, 12 Mar 2022 14:35:50 +0100 Subject: [PATCH 034/862] add Fetch, Konstellation Signed-off-by: David Pierret --- src/networks.json | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/networks.json b/src/networks.json index 4925ed97b..46d7c89dd 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1526,6 +1526,12 @@ "botAddress": "persistence1atgss2zpwl9m8fmm32gzrkjtqw3tdawguqw6tk", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "persistencevaloper1nchnrey36nrvzjslscu0c3l8j0r4z92hlsz3gk", + "botAddress": "persistence1f204r62y7l3ye7s3z05pvwufjawaf6vefpjmk0", + "runTime": "23:00", + "minimumReward": 10000 } ], "authzSupport": true @@ -1588,6 +1594,12 @@ "botAddress": "ki1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9w8hfjp", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "kivaloper1lykm0h574w0r3jm545hhlfnd8937a70022xlwl", + "botAddress": "ki1v9vae554q55xa4pcfdm0p3kv8nxn50jktpkafa", + "runTime": "23:00", + "minimumReward": 100000 } ], "authzSupport": false @@ -1610,5 +1622,51 @@ "minimumReward": 1000000 } ] + }, + { + "name": "darchub", + "prettyName": "Konstellation", + "chainId": "darchub", + "prefix": "darc", + "denom": "knstld", + "restUrl": [ + "https://node1.konstellation.tech:1318" + ], + "rpcUrl": [ + "https://node1.konstellation.tech:26657" + ], + "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/darc.png", + "operators": [ + { + "address": "darcvaloper1cmtxucuhf0hysjrx02p9fvzjr5kkcgcj9nxwhc", + "botAddress": "darc1v9vae554q55xa4pcfdm0p3kv8nxn50jk9dnrq7", + "runTime": "23:00", + "minimumReward": 100000 + } + ], + "authzSupport": false + }, + { + "name": "fetchai", + "prettyName": "Fetch.ai", + "chainId": "fetchhub-3", + "prefix": "fetch", + "denom": "afet", + "restUrl": [ + "https://rest-fetchhub.fetch.ai" + ], + "rpcUrl": [ + "https://rpc-fetchhub.fetch.ai:443" + ], + "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/fet.png", + "operators": [ + { + "address": "fetchvaloper1a7l5xar7vyymvahfelar6vtkdar5x9a54l7zvc", + "botAddress": "fetch1v9vae554q55xa4pcfdm0p3kv8nxn50jkf3wk07", + "runTime": "23:00", + "minimumReward": 10000000000000000 + } + ], + "authzSupport": false } ] From 25d4b6363588a368e1e5348ab6e1c20a178d695c Mon Sep 17 00:00:00 2001 From: David Pierret Date: Sat, 12 Mar 2022 14:46:30 +0100 Subject: [PATCH 035/862] remove not needed info Signed-off-by: David Pierret --- src/networks.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/networks.json b/src/networks.json index 46d7c89dd..7cfff2e4f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1625,17 +1625,12 @@ }, { "name": "darchub", - "prettyName": "Konstellation", - "chainId": "darchub", - "prefix": "darc", - "denom": "knstld", "restUrl": [ "https://node1.konstellation.tech:1318" ], "rpcUrl": [ "https://node1.konstellation.tech:26657" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/darc.png", "operators": [ { "address": "darcvaloper1cmtxucuhf0hysjrx02p9fvzjr5kkcgcj9nxwhc", @@ -1648,17 +1643,12 @@ }, { "name": "fetchai", - "prettyName": "Fetch.ai", - "chainId": "fetchhub-3", - "prefix": "fetch", - "denom": "afet", "restUrl": [ "https://rest-fetchhub.fetch.ai" ], "rpcUrl": [ "https://rpc-fetchhub.fetch.ai:443" ], - "image": "https://raw.githubusercontent.com/osmosis-labs/assetlists/main/images/fet.png", "operators": [ { "address": "fetchvaloper1a7l5xar7vyymvahfelar6vtkdar5x9a54l7zvc", From a99c1bf72f55b79bac8b60b8cb6ecca6f9809a71 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Sat, 12 Mar 2022 14:50:11 +0100 Subject: [PATCH 036/862] rename darchub to konstellation Signed-off-by: David Pierret --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 7cfff2e4f..2f83dfad3 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1624,7 +1624,7 @@ ] }, { - "name": "darchub", + "name": "konstellation", "restUrl": [ "https://node1.konstellation.tech:1318" ], From 645f8fc35a3c01d35d130410707b32a3ad6c1c49 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Sat, 12 Mar 2022 14:51:15 +0100 Subject: [PATCH 037/862] rename fetchai to fetchhub Signed-off-by: David Pierret --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 2f83dfad3..e02bd3f60 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1642,7 +1642,7 @@ "authzSupport": false }, { - "name": "fetchai", + "name": "fetchhub", "restUrl": [ "https://rest-fetchhub.fetch.ai" ], From 40f65b733cf1e6c645dc4b0ae6941f2bfa3ef284 Mon Sep 17 00:00:00 2001 From: David Pierret Date: Sat, 12 Mar 2022 15:06:46 +0100 Subject: [PATCH 038/862] Image for Darc token is not correct in chain-registry Signed-off-by: David Pierret --- src/networks.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/networks.json b/src/networks.json index e02bd3f60..e99ad11e5 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1631,6 +1631,7 @@ "rpcUrl": [ "https://node1.konstellation.tech:26657" ], + "image": "https://raw.githubusercontent.com/Konstellation/DARC_token/main/darctoken.svg", "operators": [ { "address": "darcvaloper1cmtxucuhf0hysjrx02p9fvzjr5kkcgcj9nxwhc", From b8fca6aaeb01c0d47e70afe36afa7be16ad1c05b Mon Sep 17 00:00:00 2001 From: "QuantumNode.Io" <98349274+quantumnodeio@users.noreply.github.com> Date: Sat, 12 Mar 2022 15:31:30 +0100 Subject: [PATCH 039/862] Update time for QN --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 4925ed97b..5f60f809c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -261,7 +261,7 @@ { "address": "junovaloper1297950qqq6yxhgsms6t5dks66gyk6sctplnyd7", "botAddress": "juno1q200ut9chx7t0nnde4h526vdxwudktgkfhxfw7", - "runTime": "19:00", + "runTime": "21:00", "minimumReward": 10000 }, { @@ -592,7 +592,7 @@ { "address": "chihuahuavaloper1jdlfdu2gaj0tlchzjhdnh5r7lfzt6l0zxlhkr6", "botAddress": "chihuahua1q200ut9chx7t0nnde4h526vdxwudktgkusgugq", - "runTime": "19:00", + "runTime": "21:00", "minimumReward": 1000 }, { From 79d8ba0a1f005aaed90cbfab0f9bc6430e9333b8 Mon Sep 17 00:00:00 2001 From: "Sachin S. Kamath" Date: Sat, 12 Mar 2022 21:22:57 +0530 Subject: [PATCH 040/862] networks: add gasPrice for cheq --- src/networks.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/networks.json b/src/networks.json index 4925ed97b..d3efddf4a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1444,6 +1444,7 @@ "https://rpc.cosmos.directory/cheqd", "https://rpc.cheqd.net" ], + "gasPrice": "25ncheq", "operators": [ { "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", From 5c0accc7bc8564760f3209b3461f2279625df827 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 12 Mar 2022 18:55:43 +0000 Subject: [PATCH 041/862] Refactor QueryClient and improve status checking --- scripts/autostake.mjs | 10 +++++----- src/components/App.js | 15 ++++----------- src/components/Delegations.js | 6 +++--- src/components/NetworkFinder.js | 2 +- src/components/Wallet.js | 4 ++-- src/utils/Network.mjs | 16 ++++++++-------- src/utils/{RestClient.mjs => QueryClient.mjs} | 18 +++++++++++------- src/utils/SigningClient.mjs | 15 --------------- 8 files changed, 34 insertions(+), 52 deletions(-) rename src/utils/{RestClient.mjs => QueryClient.mjs} (89%) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 4a1a803bb..44bea003e 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -104,12 +104,12 @@ class Autostake { network: network, operator: operator, signingClient: client, - restClient: network.restClient + queryClient: network.queryClient } } checkBalance(client) { - return client.restClient.getBalance(client.operator.botAddress, client.network.denom) + return client.queryClient.getBalance(client.operator.botAddress, client.network.denom) .then( (balance) => { console.log("Bot balance is", balance.amount, balance.denom) @@ -126,7 +126,7 @@ class Autostake { } getDelegations(client) { - return client.restClient.getAllValidatorDelegations(client.operator.address, 100, (pages) => { + return client.queryClient.getAllValidatorDelegations(client.operator.address, 100, (pages) => { console.log("...batch", pages.length) }).catch(error => { console.log("ERROR:", error.message || error) @@ -152,7 +152,7 @@ class Autostake { } getGrantValidators(client, delegatorAddress) { - return client.restClient.getGrants(client.operator.botAddress, delegatorAddress) + return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress) .then( (result) => { if(result.claimGrant && result.stakeGrant){ @@ -227,7 +227,7 @@ class Autostake { } totalRewards(client, address, validators){ - return client.restClient.getRewards(address) + return client.queryClient.getRewards(address) .then( (rewards) => { const total = Object.values(rewards).reduce((sum, item) => { diff --git a/src/components/App.js b/src/components/App.js index 1a253d6eb..f1d9bf611 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -2,7 +2,6 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import './App.css'; import React from 'react' import _ from 'lodash' -import SigningClient from '../utils/SigningClient.mjs' import AlertMessage from './AlertMessage' import NetworkSelect from './NetworkSelect' import Wallet from './Wallet' @@ -75,7 +74,7 @@ class App extends React.Component { return this.setState({ error: false, - restClient: network.restClient + queryClient: network.queryClient }) } @@ -100,12 +99,6 @@ class App extends React.Component { const offlineSigner = await window.getOfflineSignerAuto(chainId) const key = await window.keplr.getKey(chainId); const stargateClient = await this.props.network.signingClient(offlineSigner, key) - if(!stargateClient.connected){ - this.setState({ - error: 'Could not connect to any available RPC servers' - }) - return - } const address = await stargateClient.getAddress() @@ -135,7 +128,7 @@ class App extends React.Component { coinGeckoId: network.coinGeckoId } return window.keplr.experimentalSuggestChain({ - rpc: network.rpcUrl[0], + rpc: network.rpcUrl, rest: network.restUrl, chainId: network.chainId, chainName: network.prettyName, @@ -215,7 +208,7 @@ class App extends React.Component { } async getBalance() { - this.state.restClient.getBalance(this.state.address, this.props.network.denom) + this.state.queryClient.getBalance(this.state.address, this.props.network.denom) .then( (balance) => { this.setState({ @@ -299,7 +292,7 @@ class App extends React.Component { validators={this.props.validators} balance={this.state.balance} getValidatorImage={this.getValidatorImage} - restClient={this.state.restClient} + queryClient={this.state.queryClient} stargateClient={this.state.stargateClient} /> } diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 0903ff7d1..5efa0f1f1 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -75,7 +75,7 @@ class Delegations extends React.Component { } getRewards(hideError) { - this.props.restClient.getRewards(this.props.address, this.props.network.denom) + this.props.queryClient.getRewards(this.props.address, this.props.network.denom) .then( (rewards) => { this.setState({ rewards: rewards }); @@ -93,7 +93,7 @@ class Delegations extends React.Component { getGrants() { this.props.operators.forEach(operator => { const {botAddress, address} = operator - this.props.restClient.getGrants(botAddress, this.props.address) + this.props.queryClient.getGrants(botAddress, this.props.address) .then( (result) => { let grantValidators @@ -121,7 +121,7 @@ class Delegations extends React.Component { } getTestGrant(){ - this.props.restClient.getGrants(this.props.network.testAddress, this.props.address) + this.props.queryClient.getGrants(this.props.network.testAddress, this.props.address) .then( (result) => { }, (error) => { if (error.response && error.response.status === 501) { diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index e58140543..5fc68246c 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -77,7 +77,7 @@ function NetworkFinder() { if(state.error) return if(state.network && (!Object.keys(state.validators).length)){ - if(!state.network.restClient.connected){ + if(!state.network.connected){ return setState({ loading: false }) diff --git a/src/components/Wallet.js b/src/components/Wallet.js index bb00cf9a3..9c4348f9b 100644 --- a/src/components/Wallet.js +++ b/src/components/Wallet.js @@ -52,7 +52,7 @@ class Wallet extends React.Component { } async getDelegations(hideError) { - this.props.restClient.getDelegations(this.props.address) + this.props.queryClient.getDelegations(this.props.address) .then( (delegations) => { this.setState({ @@ -145,7 +145,7 @@ class Wallet extends React.Component { validators={this.props.validators} getValidatorImage={this.props.getValidatorImage} delegations={this.state.delegations} - restClient={this.props.restClient} + queryClient={this.props.queryClient} stargateClient={this.props.stargateClient} getDelegations={this.getDelegations} onAddValidator={this.onAddValidator} /> diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 8929cfe36..1086035d3 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -1,5 +1,5 @@ import _ from 'lodash' -import RestClient from './RestClient.mjs' +import QueryClient from './QueryClient.mjs' import SigningClient from './SigningClient.mjs' import Operator from './Operator.mjs' import Chain from './Chain.mjs' @@ -7,11 +7,11 @@ import Chain from './Chain.mjs' const Network = async (data) => { const chain = await Chain(data) - const restClient = await RestClient(chain.chainId, data.restUrl) + const queryClient = await QueryClient(chain.chainId, data.rpcUrl, data.restUrl) const signingClient = (wallet, key) => { const gasPrice = data.gasPrice || '0.0025' + chain.denom - return SigningClient(data.rpcUrl, chain.chainId, gasPrice, wallet, key) + return SigningClient(queryClient.rpcUrl, chain.chainId, gasPrice, wallet, key) } const getOperator = (operators, operatorAddress) => { @@ -34,11 +34,11 @@ const Network = async (data) => { } const getValidators = () => { - return restClient.getAllValidators(150) + return queryClient.getAllValidators(150) } return { - connected: restClient.connected, + connected: queryClient.connected, name: data.name, prettyName: chain.prettyName, chainId: chain.chainId, @@ -51,13 +51,13 @@ const Network = async (data) => { image: chain.image, coinGeckoId: chain.coinGeckoId, testAddress: data.testAddress, - restUrl: restClient.restUrl, - rpcUrl: data.rpcUrl, + restUrl: queryClient.restUrl, + rpcUrl: queryClient.rpcUrl, operators: data.operators, authzSupport: data.authzSupport, data, chain, - restClient, + queryClient, signingClient, getValidators, getOperators, diff --git a/src/utils/RestClient.mjs b/src/utils/QueryClient.mjs similarity index 89% rename from src/utils/RestClient.mjs rename to src/utils/QueryClient.mjs index 0b5ef4f0f..483fd3c9b 100644 --- a/src/utils/RestClient.mjs +++ b/src/utils/QueryClient.mjs @@ -2,9 +2,10 @@ import axios from 'axios' import _ from 'lodash' import {findAsync} from './Helpers.mjs' -const RestClient = async (chainId, restUrls) => { +const QueryClient = async (chainId, rpcUrls, restUrls) => { - const restUrl = await findAvailableUrl(Array.isArray(restUrls) ? restUrls : [restUrls]) + const rpcUrl = await findAvailableUrl(Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls], 'rpc') + const restUrl = await findAvailableUrl(Array.isArray(restUrls) ? restUrls : [restUrls], 'rest') const getAllValidators = (pageSize, pageCallback) => { return getAllPages((nextKey) => { @@ -118,12 +119,14 @@ const RestClient = async (chainId, restUrls) => { return pages } - function findAvailableUrl(urls){ + function findAvailableUrl(urls, type){ + const path = type === 'rest' ? '/blocks/latest' : '/block' return findAsync(urls, (url) => { - return axios.get(url + '/node_info', {timeout: 2000}) + return axios.get(url + path, {timeout: 2000}) .then(res => res.data) .then(data => { - return data.node_info.network === chainId + if(type === 'rpc') data = data.result + return data.block.header.chain_id === chainId }).catch(error => { return false }) @@ -131,7 +134,8 @@ const RestClient = async (chainId, restUrls) => { } return { - connected: !!restUrl, + connected: !!rpcUrl && !!restUrl, + rpcUrl, restUrl, getAllValidators, getValidators, @@ -144,4 +148,4 @@ const RestClient = async (chainId, restUrls) => { } } -export default RestClient; +export default QueryClient; diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index eb858c5e0..b7b3cb00f 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -12,9 +12,6 @@ import axios from 'axios' const SigningClient = async (rpcUrl, chainId, defaultGasPrice, signer, key) => { - const rpcUrls = Array.isArray(rpcUrl) ? rpcUrl : [rpcUrl] - rpcUrl = await findAvailableUrl(rpcUrls) - const client = rpcUrl && await SigningStargateClient.connectWithSigner( rpcUrl, signer @@ -73,18 +70,6 @@ const SigningClient = async (rpcUrl, chainId, defaultGasPrice, signer, key) => { return (parseInt(estimate * (modifier || 1.5))) } - function findAvailableUrl(urls){ - return findAsync(urls, (url) => { - return axios.get(url + '/status?', {timeout: 1000}) - .then(res => res.data) - .then(data => { - return data.result.node_info.network === chainId - }).catch(error => { - return false - }) - }) - } - return { connected: !!rpcUrl, registry: client && client.registry, From 237c3c1d644b7cd4d3b3740e7ef88421d841d437 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 12 Mar 2022 19:21:04 +0000 Subject: [PATCH 042/862] Speed up autostake script and handle enable --- scripts/autostake.mjs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 44bea003e..f7dd10079 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -27,22 +27,21 @@ class Autostake { const calls = this.getNetworksData().map(data => { return async () => { if(networkName && data.name !== networkName) return + if(data.enabled === false) return let client try { client = await this.getClient(data) } catch (error) { - return console.log('Failed to connect') + return console.log('Failed to connect', error.message) } - if(!client.operator) return console.log('Not an operator') - if(!client.network.authzSupport) return console.log('No Authz support') - if(!data.overriden) console.log('You are using public nodes, script may fail with many delegations. Check the README to use your own') - if(!client.network.connected) return console.log('Could not connect to REST API') - if(!client.signingClient.connected) return console.log('Could not connect to RPC API') + if(!client) return console.log('Skipping') console.log('Using REST URL', client.network.restUrl) - console.log('Using RPC URL', client.signingClient.rpcUrl) + console.log('Using RPC URL', client.network.rpcUrl) + + if(!data.overriden) console.log('You are using public nodes, script may fail with many delegations. Check the README to use your own') console.log('Running autostake') await this.checkBalance(client) @@ -88,17 +87,19 @@ class Autostake { console.log(network.prettyName, 'bot address is', botAddress) + const operatorData = data.operators.find(el => el.botAddress === botAddress) + + if(!operatorData) return console.log('Not an operator') + if(!network.authzSupport) return console.log('No Authz support') + if(!network.rpcUrl) return console.log('Could not connect to RPC API') + if(!network.restUrl) return console.log('Could not connect to REST API') + const client = await network.signingClient(wallet) - if(client.connected){ - client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) - } + client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) - let validators = {} - if(network.operators.find(el => el.botAddress === botAddress)){ - validators = await network.getValidators() - } + const validators = await network.getValidators() const operators = network.getOperators(validators) - const operator = operators.find(el => el.botAddress === botAddress) + const operator = network.getOperator(operators, botAddress) return{ network: network, From 227a598638b7192beeedeb18cbab5cda2683f9a4 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 12 Mar 2022 19:42:52 +0000 Subject: [PATCH 043/862] Handle directory not matching chain_name --- src/components/NetworkFinder.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index e58140543..760dec416 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -24,11 +24,11 @@ function NetworkFinder() { const getNetworks = async () => { const registryNetworks = await axios.get('https://registry.cosmos.directory') .then(res => res.data) - .then(data => data.reduce((a, v) => ({ ...a, [v.chain_name]: v}), {})) + .then(data => data.reduce((a, v) => ({ ...a, [v.directory]: v}), {})) const networks = networksData.filter(el => el.enabled !== false).map(data => { const registryData = registryNetworks[data.name] || {} - return {...data, ...registryData} + return {...registryData, ...data} }) return _.compact(networks).reduce((a, v) => ({ ...a, [v.name]: v}), {}) } From 576db582c5ab51cd7617021c6e24b4f5ad11badb Mon Sep 17 00:00:00 2001 From: LPX Date: Sun, 13 Mar 2022 07:08:11 +0900 Subject: [PATCH 044/862] Add ShapeShift Umee Validator --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 9bcf2e69e..7fc978daa 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1487,6 +1487,12 @@ "botAddress": "umee1m5hgzum68rjf4c7zhezgkj8hlnmr0kghyhx5wz", "runTime": "10:00", "minimumReward": 10000 + }, + { + "address": "umeevaloper1gvt9l5tshr0fp8ksl2tuem584z69eyvt9m8yh6", + "botAddress": "umee19pqrxrl6n0g0mky4y79hlfzchprmsp5jp3yygg", + "runTime": "17:00", + "minimumReward": 10000, } ], "authzSupport": true From 0436e61551e496b3bd1bb1252561d0934933cd6a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 12 Mar 2022 22:11:32 +0000 Subject: [PATCH 045/862] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..b410eaa35 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 ECO Stake + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From a14326101c05cb63aa4705496ea0b553dafd9657 Mon Sep 17 00:00:00 2001 From: LPX Date: Sun, 13 Mar 2022 07:13:33 +0900 Subject: [PATCH 046/862] Update Osmosis Run Time --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 7fc978daa..7cbd49a02 100644 --- a/src/networks.json +++ b/src/networks.json @@ -143,7 +143,7 @@ { "address": "osmovaloper1xf9zpq5kpxks49cg606tzd8qstaykxgt2vs0d5", "botAddress": "osmo19pqrxrl6n0g0mky4y79hlfzchprmsp5jmu2t6g", - "runTime": "19:00", + "runTime": "19:31", "minimumReward": 1000 } ], From 95d3dc7cc232ae6825b506ad1951c8650756163c Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 13 Mar 2022 02:23:31 +0000 Subject: [PATCH 047/862] Show countdown and minimum rewards, improve UI --- README.md | 2 +- src/components/About.js | 4 +- src/components/App.js | 6 ++- src/components/CountdownRestake.js | 68 ++++++++++++++++++++++++++++++ src/components/Delegations.js | 31 +++++++++----- src/components/GrantRestake.js | 28 ++++++++++-- src/components/RevokeRestake.js | 24 +++-------- src/components/TooltipIcon.js | 4 +- src/components/Validators.js | 2 +- src/components/Wallet.js | 58 ------------------------- 10 files changed, 130 insertions(+), 97 deletions(-) create mode 100644 src/components/CountdownRestake.js diff --git a/README.md b/README.md index e1979eb70..4b5bbe1ab 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Try it out at [restake.app](https://restake.app). Authz is a new feature for Tendermint chains which lets you grant permission to another wallet to carry out certain transactions for you. These transactions are sent by the grantee on behalf of the granter, meaning the validator will send and pay for the TX, but actions will affect your wallet (such as claiming rewards). -REStake specifically lets you grant a validator permission to send `WithdrawDelegatorReward` and `Delegate` transactions for their validator only (note `WithdrawDelegatorReward` is technically not restricted to a single validator). The validator cannot send any other transaction types, and has no other access to your wallet. You authorise this using Keplr as normal. +REStake specifically lets you grant a validator permission to send `WithdrawDelegatorReward` and `Delegate` transactions for their validator only (note `WithdrawDelegatorReward` is technically not restricted to a single validator). The validator cannot send any other transaction types, and has no other access to your wallet. You authorize this using Keplr as normal. A script is also provided which allows a validator to automatically search their delegators, check each for the required grants, and if applicable carry out the claim and delegate transactions on their behalf in a single transaction. This script should be run daily, and the time you will run it can be specified when you [add your operator](#become-an-operator). diff --git a/src/components/About.js b/src/components/About.js index 28b283ffc..7fe62653a 100644 --- a/src/components/About.js +++ b/src/components/About.js @@ -11,8 +11,8 @@ function About(props) {
How REStake works
-

REStake makes use of a new feature in Cosmos blockchains called Authz. This allows a validator (or any other wallet) to send certain pre-authorised transactions on your behalf.

-

When you enable REStake you authorise the validator to send WithdrawDelegatorReward for any address, and Delegate for their own validator address. The validator cannot delegate to any other validators, and the authorisation expires automatically after one year and you can revoke at any time.

+

REStake makes use of a new feature in Cosmos blockchains called Authz. This allows a validator (or any other wallet) to send certain pre-authorized transactions on your behalf.

+

When you enable REStake you authorize the validator to send WithdrawDelegatorReward for any address, and Delegate for their own validator address. The validator cannot delegate to any other validators, and the authorisation expires automatically after one year and you can revoke at any time.

How to use REStake
  1. Choose a network. Some don't support Authz yet but many do.
  2. diff --git a/src/components/App.js b/src/components/App.js index f1d9bf611..ee9c09756 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -267,7 +267,9 @@ class App extends React.Component {
    -

    REStake allows validators to auto-compound your {this.props.network.prettyName} staking rewards for you

    +

    + REStake allows validators to this.setState({showAbout: true})} className="text-decoration-underline" role="button">auto-compound your {this.props.network.prettyName} staking rewards for you +

    {!this.state.address && ( !this.state.keplr @@ -299,7 +301,7 @@ class App extends React.Component {

    Enabling REStake will authorize the validator to send WithdrawDelegatorReward and Delegate transactions on your behalf for 1 year using Authz.
    - They will only be authorised to delegate to their own validator. You can revoke the authorization at any time and everything is open source. + They will only be authorized to delegate to their own validator. You can revoke the authorization at any time and everything is open source.

    The validators will pay the transaction fees for you. diff --git a/src/components/CountdownRestake.js b/src/components/CountdownRestake.js new file mode 100644 index 000000000..ab9d2ce92 --- /dev/null +++ b/src/components/CountdownRestake.js @@ -0,0 +1,68 @@ +import React from 'react'; + +import TooltipIcon from './TooltipIcon'; +import Coins from './Coins'; + +import Countdown from 'react-countdown'; +import { + ClockHistory +} from 'react-bootstrap-icons'; + +function RevokeRestake(props) { + const { operator } = props + + const minimumReward = () => { + return { amount: operator.data.minimumReward, denom: props.network.denom } + } + + const nextRun = (delayHour) => { + const now = new Date() + if(!operator || !operator.data.runTime){ + return null + } + const runTime = operator.data.runTime.split(':') + let day + if(delayHour){ + day = now.getHours() > runTime[0] ? now.getDate() + 1 : now.getDate() + }else{ + day = now.getHours() >= runTime[0] ? now.getDate() + 1 : now.getDate() + } + + return new Date( + now.getFullYear(), + now.getMonth(), + day, + runTime[0], + runTime[1], + runTime[2] || 0 + ) + } + + const countdownRenderer = ({ hours, minutes, seconds, completed }) => { + if (completed) { + return

    Running now. The next run will be at {operator.data.runTime} tomorrow

    + } else { + let string = '' + if(hours > 0) string = string.concat(hours + 'h ') + if(minutes > 0) string = string.concat(minutes + 'm ') + if(props.showSeconds) string = string.concat(seconds + 's') + return ( +

    Validator will REStake in
    {string}

    + ) + } + } + return ( + } identifier={operator.address}> +
    + +

    Minimum reward is

    +
    +
    + ) +} + +export default RevokeRestake; + diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 5efa0f1f1..be4db2682 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -5,6 +5,7 @@ import Coins from './Coins' import ClaimRewards from './ClaimRewards' import RevokeRestake from './RevokeRestake' import GrantRestake from './GrantRestake' +import CountdownRestake from './CountdownRestake' import Delegate from './Delegate' import ValidatorImage from './ValidatorImage' import TooltipIcon from './TooltipIcon' @@ -242,7 +243,9 @@ class Delegations extends React.Component { const rewards = this.state.rewards && this.state.rewards[validatorAddress] const denomRewards = rewards && this.denomRewards(rewards) const operator = this.operatorForValidator(validatorAddress) - let rowVariant = operator ? 'table-warning' : undefined + let rowVariant = operator && delegation ? ( + this.grantsValid(operator) ? 'table-success' : 'table-warning' + ) : undefined const delegationBalance = (delegation && delegation.balance) || { amount: 0, @@ -265,16 +268,12 @@ class Delegations extends React.Component { {operator ? this.restakePossible() && delegation ? ( this.grantsValid(operator) ? ( - + ) : ( ) ) : ( - } identifier={validatorAddress} tooltip="This validator can auto-compound your rewards" /> + } identifier={validatorAddress} tooltip="This validator can REStake your rewards" /> ) : ( } identifier={validatorAddress} tooltip="This validator is not a REStake operator" /> )} @@ -305,6 +304,18 @@ class Delegations extends React.Component { + {operator && this.restakePossible() && this.grantsValid(operator) && ( + <> + this.setValidatorLoading(validatorAddress, loading)} + setError={this.setError} /> +
    + + )} {alerts} {!this.noDelegations() && ( - +
    diff --git a/src/components/GrantRestake.js b/src/components/GrantRestake.js index 22ac7b408..b7fcfb252 100644 --- a/src/components/GrantRestake.js +++ b/src/components/GrantRestake.js @@ -5,7 +5,9 @@ import { StakeAuthorization } from "cosmjs-types/cosmos/staking/v1beta1/authz"; import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; import { - Button + Button, + OverlayTrigger, + Tooltip } from 'react-bootstrap' function GrantRestake(props) { @@ -66,13 +68,31 @@ function GrantRestake(props) { } } + function button(){ + return ( + + ) + } + return ( <> {!loading ? ( - + <> + {props.tooltip ? ( + + {props.tooltip} + + } + >{button()} + ) : button()} + ) : ( - ) : ( - - ) - } - + revoke()} > + Disable REStake + ) } diff --git a/src/components/TooltipIcon.js b/src/components/TooltipIcon.js index bb71d2ce9..88b05aa89 100644 --- a/src/components/TooltipIcon.js +++ b/src/components/TooltipIcon.js @@ -7,13 +7,13 @@ function TooltipIcon(props) { return ( <> - {props.tooltip ? ( + {(props.children || props.tooltip) ? ( - {props.tooltip} + {props.children || props.tooltip} } >{props.icon} diff --git a/src/components/Validators.js b/src/components/Validators.js index f6885a1a3..5a9fbbef0 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -64,7 +64,7 @@ function Validators(props) { diff --git a/src/components/Delegations.js b/src/components/Delegations.js index c997813c7..a129ae432 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -14,7 +14,7 @@ import { Table, Button, Dropdown, - Spinner, + Spinner } from 'react-bootstrap' import { @@ -252,6 +252,11 @@ class Delegations extends React.Component { denom: this.props.network.denom } + const minimumReward = operator && { + amount: operator.data.minimumReward, + denom: this.props.network.denom + } + return ( @@ -269,7 +274,7 @@ class Delegations extends React.Component { - - + + - - + + diff --git a/src/networks.json b/src/networks.json index d2bc7cee1..8fc51db7a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -29,7 +29,7 @@ { "address": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "botAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", - "runTime": "21:00", + "runTime": ["09:00", "21:00"], "minimumReward": 1000 }, { diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index e7817929b..9e64ced79 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -1,14 +1,64 @@ -// import axios from 'axios' +import moment from 'moment' +import parse from 'parse-duration' const Operator = (data, validatorData) => { + function runTimes() { + if(Array.isArray(data.runTime)) return data.runTime + return [data.runTime] + } + + function frequency() { + if(Array.isArray(data.runTime)){ + return data.runTime.length + 'x per day' + }else{ + if(data.runTime.startsWith('every')){ + return data.runTime.replace('every ', '') + } + return 'daily' + } + } + + function nextRun() { + if (!data.runTime) return + + if(Array.isArray(data.runTime)){ + const runTimes = data.runTime.map(el => nextRunFromRuntime(el)) + return runTimes.sort().find(el => el.isAfter()); + }else{ + if(data.runTime.startsWith('every')){ + return nextRunFromInterval(data.runTime) + } + return nextRunFromRuntime(data.runTime) + } + } + + function nextRunFromInterval(runTime){ + const interval = parse(runTime.replace('every ', '')) + const date = moment().startOf('day') + do { + date.add(interval, 'ms') + } while (date.isBefore()) + return date + } + + function nextRunFromRuntime(runTime) { + const date = moment(runTime, 'HH:mm:ss') + if(date.isAfter()) return date + + return date.add(1, 'day') + } + return { address: data.address, botAddress: data.botAddress, moniker: validatorData && validatorData.description.moniker, description: validatorData && validatorData.description, - validatorData: validatorData, - data: data + validatorData, + data, + runTimes, + nextRun, + frequency } } From e99d26502fda1ed353ad9ce885bdcb0aae61e722 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 00:18:03 +0000 Subject: [PATCH 085/862] Update operators --- src/networks.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/networks.json b/src/networks.json index f9ec9c1b4..77c8ef1ea 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1706,8 +1706,8 @@ { "address": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "botAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", - "runTime": "21:00", - "minimumReward": 1000000000 + "runTime": "every 15 minutes", + "minimumReward": 100000000 }, { "address": "cerberusvaloper1uvl2g9nd8qttjjyxjs30x7fj878d3wt4lt4juq", @@ -1748,8 +1748,14 @@ { "address": "cerberusvaloper1y3thykrje2fmdcf8wva8l8kphmwpx89ur4erq3", "botAddress": "cerberus1v9vae554q55xa4pcfdm0p3kv8nxn50jkxt8mcz", - "runTime": "23:00", + "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "cerberusvaloper1evv5y2ake002n9l27t5qhqcwhgwd6up2m2yhcy", + "botAddress": "cerberus1m8d834ykcy3dgrdve4xhxky3u3c2tl9dwln0sv", + "runTime": "every 1 hour", + "minimumReward": 100000000 } ], "authzSupport": true From 8a2f1ae6461900bc9eeb78c8720fc2d7512ba430 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 00:39:58 +0000 Subject: [PATCH 086/862] Standardise eco runtimes and rewards --- src/networks.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/networks.json b/src/networks.json index 8fc51db7a..f6c1f77a2 100644 --- a/src/networks.json +++ b/src/networks.json @@ -413,8 +413,8 @@ { "address": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", "botAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", - "runTime": "21:00", - "minimumReward": 10000 + "runTime": ["09:00", "21:00"], + "minimumReward": 1000 }, { "address": "akashvaloper1qwpnhmdlfj6gfyh0e29fjudh0ytfe5l7tjttul", @@ -520,7 +520,7 @@ { "address": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", "botAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", - "runTime": "21:00", + "runTime": ["09:00", "21:00"], "minimumReward": 1000 }, { @@ -819,7 +819,7 @@ { "address": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", "botAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", - "runTime": "21:00", + "runTime": ["09:00", "21:00"], "minimumReward": 1000 }, { From fdb7e35104deb881493d641a321d7dee1ca82202 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 00:55:36 +0000 Subject: [PATCH 087/862] Improve tooltip info --- src/components/Delegations.js | 1 + src/utils/Operator.mjs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index a129ae432..d2aab3077 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -296,6 +296,7 @@ class Delegations extends React.Component { {operator && ( {operator.frequency()}} identifier={operator.address}>
    +

    REStakes {operator.runTimesString()}

    Minimum reward is

    diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index 9e64ced79..d5a648d90 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -8,6 +8,14 @@ const Operator = (data, validatorData) => { return [data.runTime] } + function runTimesString(){ + let string = '' + if (runTimes().length > 1 || !runTimes()[0].startsWith('every')) { + string = 'at ' + } + return string + runTimes().join(', ') + } + function frequency() { if(Array.isArray(data.runTime)){ return data.runTime.length + 'x per day' @@ -56,9 +64,10 @@ const Operator = (data, validatorData) => { description: validatorData && validatorData.description, validatorData, data, - runTimes, nextRun, - frequency + frequency, + runTimes, + runTimesString } } From 87f7eeebefc173edf9e82879f75f6638d2620dee Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 00:56:53 +0000 Subject: [PATCH 088/862] Match in delegate form --- src/components/Delegate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Delegate.js b/src/components/Delegate.js index 9d5b8d5b3..54a9b27ba 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -181,7 +181,7 @@ function Delegate(props) {
    - + + {this.props.network.data.apyEnabled !== false ? ( + + ) : ( + "" + )} - + - ) - }else{ - return null + ); + } else { + return null; } } @@ -418,32 +563,47 @@ class Delegations extends React.Component { Loading... - ) + ); } const alerts = ( <> - {this.state.authzMissing && - - {this.props.network.prettyName} doesn't support Authz just yet. You can manually restake for now and REStake is ready when support is enabled - - } - {!this.state.authzMissing && !this.props.operators.length && - - } - {!this.state.authzMissing && this.props.operators.length > 0 && this.state.isNanoLedger && - - } + {this.state.authzMissing && ( + + {this.props.network.prettyName} doesn't support Authz just yet. You + can manually restake for now and REStake is ready when support is + enabled + + )} + {!this.state.authzMissing && !this.props.operators.length && ( + + )} + {!this.state.authzMissing && + this.props.operators.length > 0 && + this.state.isNanoLedger && ( + + )} - ) + ); - if (this.noDelegations()){ + if (this.noDelegations()) { return ( <> {alerts}
    -

    There are no REStake operators for this network yet. You can delegate to other validators in the meantime.

    +

    + There are no REStake operators for this network yet. You can + delegate to other validators in the meantime. +

    + onDelegate={this.props.onAddValidator} + />
    - ) + ); } return ( @@ -469,26 +630,38 @@ class Delegations extends React.Component {
    + {this.props.network.data.apyEnabled !== false ? ( + + ) : ( + "" + )} - - + + - {this.orderedOperators().length > 0 && ( - this.orderedOperators().map(operator => { - const delegation = this.props.delegations && this.props.delegations[operator.address] - return this.renderValidator(operator.address, delegation) - }) - )} - {this.regularDelegations().length > 0 && ( - this.regularDelegations().map(delegation => { - return this.renderValidator(delegation.delegation.validator_address, delegation) - }) - )} + {this.orderedOperators().length > 0 && + this.orderedOperators().map((operator) => { + const delegation = + this.props.delegations && + this.props.delegations[operator.address]; + return this.renderValidator(operator.address, delegation); + })} + {this.regularDelegations().length > 0 && + this.regularDelegations().map((delegation) => { + return this.renderValidator( + delegation.delegation.validator_address, + delegation + ); + })}
    Validator {isOperator ? } identifier={item.operator_address} - tooltip="This validator can auto-compound your rewards" /> + tooltip="This validator can REStake your rewards" /> : } identifier={item.operator_address} tooltip="This validator is not a REStake operator" /> } diff --git a/src/components/Wallet.js b/src/components/Wallet.js index 9c4348f9b..710b81a9a 100644 --- a/src/components/Wallet.js +++ b/src/components/Wallet.js @@ -1,8 +1,5 @@ import React from 'react' import Delegations from './Delegations' -import Coins from './Coins' - -import Countdown from 'react-countdown'; import { Spinner @@ -14,7 +11,6 @@ class Wallet extends React.Component { this.state = {} this.getDelegations = this.getDelegations.bind(this); this.onAddValidator = this.onAddValidator.bind(this); - this.countdownRenderer = this.countdownRenderer.bind(this); } componentDidMount() { @@ -75,51 +71,6 @@ class Wallet extends React.Component { ) } - minimumReward(){ - const operator = this.props.operator - - if(operator){ - return {amount: operator.data.minimumReward, denom: this.props.network.denom} - } - } - - nextRun(operator, delayHour){ - const now = new Date() - if(!operator || !operator.data.runTime){ - return null - } - const runTime = operator.data.runTime.split(':') - let day - if(delayHour){ - day = now.getHours() > runTime[0] ? now.getDate() + 1 : now.getDate() - }else{ - day = now.getHours() >= runTime[0] ? now.getDate() + 1 : now.getDate() - } - - return new Date( - now.getFullYear(), - now.getMonth(), - day, - runTime[0], - runTime[1], - runTime[2] || 0 - ) - } - - countdownRenderer({ hours, minutes, seconds, completed }){ - if (completed) { - return

    Auto REStake is running right now. The next run will be at {this.state.runTime} tomorrow

    - } else { - let string = '' - if(hours > 0) string = string.concat(hours + ' hours, ') - if(minutes > 0) string = string.concat(minutes + ' minutes and ') - string = string.concat(seconds + ' seconds') - return ( -

    Auto REStake will run in {string}

    - ) - } - } - render() { if (!this.state.isLoaded) { return ( @@ -149,15 +100,6 @@ class Wallet extends React.Component { stargateClient={this.props.stargateClient} getDelegations={this.getDelegations} onAddValidator={this.onAddValidator} /> - {false && this.props.operator && Object.values(this.state.delegations).length > 0 && -
    - -

    The minimum reward is

    -
    - } ) } From b091169d0402c358ab1fea847a5c46a57b308ef3 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 13 Mar 2022 03:27:57 +0000 Subject: [PATCH 048/862] Add more validator info to modal --- src/components/CountdownRestake.js | 2 +- src/components/Delegate.js | 111 +++++++++++++++++++++++------ src/components/Delegations.js | 3 + 3 files changed, 94 insertions(+), 22 deletions(-) diff --git a/src/components/CountdownRestake.js b/src/components/CountdownRestake.js index ab9d2ce92..e72b37ae7 100644 --- a/src/components/CountdownRestake.js +++ b/src/components/CountdownRestake.js @@ -52,7 +52,7 @@ function RevokeRestake(props) { } } return ( - } identifier={operator.address}> + } identifier={operator.address}>
    { setShow(true) - if(!props.validator || props.redelegate){ + if (!props.validator || props.redelegate) { setSelectedValidator(null) } } @@ -31,32 +39,57 @@ function Delegate(props) { } const excludeValidators = () => { - if(props.redelegate){ + if (props.redelegate) { return [props.validator.operator_address] - }else if(props.delegations){ + } else if (props.delegations) { return Object.keys(props.delegations) } } + const operator = () => { + if (!props.operators || !selectedValidator) return + + return props.network.getOperator(props.operators, selectedValidator.operator_address) + } + + const website = () => { + if (!selectedValidator) return + + return selectedValidator.description && selectedValidator.description.website + } + + const securityContact = () => { + if (!selectedValidator) return + + return selectedValidator.description && selectedValidator.description.security_contact + } + + const bondedTokens = () => { + if (!selectedValidator) return + + const amount = parseInt(selectedValidator.tokens) + return + } + const actionText = () => { - if(props.redelegate) return 'Redelegate' - if(props.undelegate) return 'Undelegate' - if(props.validator){ + if (props.redelegate) return 'Redelegate' + if (props.undelegate) return 'Undelegate' + if (props.validator) { return 'Delegate' - }else{ + } else { return 'Add Validator' } } const button = () => { - if(props.children){ + if (props.children) { return ( {props.children} ) - }else{ - if(props.button){ + } else { + if (props.button) { const button = ( +

    @@ -323,7 +325,7 @@ class App extends React.Component { Star

    - this.setState({showAbout: false})} /> + this.setState({ showAbout: false })} /> ) } diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 2d8f81c9f..3693d1ed9 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -18,7 +18,7 @@ function NetworkFinder() { const [state, setState] = useReducer( (state, newState) => ({...state, ...newState}), - {loading: true, networks: [], operators: [], validators: []} + {loading: true, networks: [], operators: [], validators: {}} ) const getNetworks = async () => { @@ -69,27 +69,27 @@ function NetworkFinder() { } Network(data).then(network => { setState({network: network}) + }, (error) => { + Network(data, true).then(network => { + setState({ network: network, loading: false }) + }) }) } }, [state.networks, state.network, params.network, navigate]) useEffect(() => { if(state.error) return - + if(!state.network || !state.network.connected) return if(state.network && (!Object.keys(state.validators).length)){ - if(!state.network.connected){ - return setState({ - loading: false - }) - } - state.network.getValidators().then(validators => { setState({ validators, operators: state.network.getOperators(validators), loading: false }) - }, error => setState({loading: false, error: 'Unable to connect right now, try again'})) + }, error => { + setState({validators: {}, loading: false}) + }) } }, [state.network]) @@ -103,16 +103,6 @@ function NetworkFinder() { ) } - if (state.error) { - return ( -

    Loading failed

    - ) - } - - if(!state.network){ - return

    Page not found

    - } - return changeNetwork(network, validators)} />; } diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index 1ab2324e7..43d57a839 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -72,22 +72,23 @@ function NetworkSelect(props) { if(data){ setLoading(true) setError(false) + Network(data, true).then(network => { + setSelectedNetwork(network) + }) Network(data).then(network => { setSelectedNetwork(network) setValidators({}) - if(network.connected){ - network.getValidators().then(data => { - setValidators(data) - loadValidatorImages(network, data) - setLoading(false) - }).catch(error => { - setError('Unable to connect to this network currently. Try again later.') - setLoading(false) - }) - }else{ + network.getValidators().then(data => { + setValidators(data) + loadValidatorImages(network, data) + setLoading(false) + }, (error) => { setError('Unable to connect to this network currently. Try again later.') setLoading(false) - } + }) + }, (error) => { + setError('Unable to connect to this network currently. Try again later.') + setLoading(false) }) } } diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 3f6441abb..c5ba81d31 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -4,12 +4,17 @@ import SigningClient from './SigningClient.mjs' import Operator from './Operator.mjs' import Chain from './Chain.mjs' -const Network = async (data) => { +const Network = async (data, withoutQueryClient) => { const chain = await Chain(data) - const queryClient = await QueryClient(chain.chainId, data.rpcUrl, data.restUrl) + let queryClient + if(!withoutQueryClient){ + queryClient = await QueryClient(chain.chainId, data.rpcUrl, data.restUrl) + } const signingClient = (wallet, key) => { + if(!queryClient) return + const gasPrice = data.gasPrice || '0.0025' + chain.denom return SigningClient(queryClient.rpcUrl, chain.chainId, gasPrice, wallet, key) } @@ -42,7 +47,7 @@ const Network = async (data) => { } return { - connected: queryClient.connected, + connected: queryClient && queryClient.connected, name: data.name, prettyName: chain.prettyName, chainId: chain.chainId, @@ -55,8 +60,8 @@ const Network = async (data) => { image: chain.image, coinGeckoId: chain.coinGeckoId, testAddress: data.testAddress, - restUrl: queryClient.restUrl, - rpcUrl: queryClient.rpcUrl, + restUrl: queryClient && queryClient.restUrl, + rpcUrl: queryClient && queryClient.rpcUrl, operators: data.operators, authzSupport: data.authzSupport, data, diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index b7b3cb00f..9124eacbe 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -8,8 +8,6 @@ import { GasPrice } from '@cosmjs/stargate' -import axios from 'axios' - const SigningClient = async (rpcUrl, chainId, defaultGasPrice, signer, key) => { const client = rpcUrl && await SigningStargateClient.connectWithSigner( From cfab2a446dd9f5b99c7b76705483bdbc42c42c09 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 13 Mar 2022 21:33:26 +0000 Subject: [PATCH 058/862] Don't load network fully until needed --- scripts/autostake.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index fb96cf841..b97c503b4 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -1,6 +1,5 @@ import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import Network from '../src/utils/Network.mjs' -import Operator from '../src/utils/Operator.mjs' import {mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' import { @@ -76,7 +75,7 @@ class Autostake { } async getClient(data){ - const network = await Network(data) + let network = await Network(data, true) const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.mnemonic, { prefix: network.prefix @@ -91,6 +90,8 @@ class Autostake { if(!operatorData) return console.log('Not an operator') if(!network.authzSupport) return console.log('No Authz support') + + network = await Network(data) if(!network.rpcUrl) return console.log('Could not connect to RPC API') if(!network.restUrl) return console.log('Could not connect to REST API') From fe9909cbd3dfc2415ccfef2537acc5efd116ef2c Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 13 Mar 2022 22:43:44 +0000 Subject: [PATCH 059/862] Handle signing client error (flakey RPC) --- src/components/App.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 72b7c6bd5..5a671cfda 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -98,20 +98,26 @@ class App extends React.Component { await this.suggestChain(this.props.network) } if (window.getOfflineSigner){ - const offlineSigner = await window.getOfflineSignerAuto(chainId) - const key = await window.keplr.getKey(chainId); - const stargateClient = await this.props.network.signingClient(offlineSigner, key) + try { + const offlineSigner = await window.getOfflineSignerAuto(chainId) + const key = await window.keplr.getKey(chainId); + const stargateClient = await this.props.network.signingClient(offlineSigner, key) - const address = await stargateClient.getAddress() + const address = await stargateClient.getAddress() - stargateClient.registry.register("/cosmos.authz.v1beta1.MsgGrant", MsgGrant) - stargateClient.registry.register("/cosmos.authz.v1beta1.MsgRevoke", MsgRevoke) - this.setState({ - address: address, - stargateClient: stargateClient, - error: false - }) - this.getBalance() + stargateClient.registry.register("/cosmos.authz.v1beta1.MsgGrant", MsgGrant) + stargateClient.registry.register("/cosmos.authz.v1beta1.MsgRevoke", MsgRevoke) + this.setState({ + address: address, + stargateClient: stargateClient, + error: false + }) + this.getBalance() + } catch (e) { + return this.setState({ + error: 'Failed to connect to signing client. API may be down' + }) + } } } From c5a1925e55502397afca197dbd826cc04d9f8e5b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 13 Mar 2022 23:16:09 +0000 Subject: [PATCH 060/862] Add Cerberus to networks.json --- src/networks.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/networks.json b/src/networks.json index 58c5ef47d..57d00df39 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1684,5 +1684,26 @@ } ], "authzSupport": false + }, + { + "name": "cerberus", + "restUrl": [ + "https://rest.cosmos.directory/cerberus" + ], + "rpcUrl": [ + "https://rpc.cosmos.directory/cerberus" + ], + "gasPrice": "0.025ucrbrus", + "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", + "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", + "operators": [ + { + "address": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", + "botAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", + "runTime": "21:00", + "minimumReward": 100000000 + } + ], + "authzSupport": true } ] From 05c244575b819b8414e285570d3ecf9b9dae174a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 14 Mar 2022 00:43:49 +0000 Subject: [PATCH 061/862] Adjust minimum reward --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 57d00df39..4420cdfe5 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1701,7 +1701,7 @@ "address": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "botAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "runTime": "21:00", - "minimumReward": 100000000 + "minimumReward": 1000000000 } ], "authzSupport": true From 640e542d695f3dd1c3fe9cc4b9ef5fc2f08d4c75 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Mon, 14 Mar 2022 13:37:10 +0200 Subject: [PATCH 062/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 58c5ef47d..d2bc7cee1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -382,6 +382,12 @@ "botAddress": "cosmos14gra89zm8753pfuhvujjulmtkf2qq70feykca5", "runTime": "17:28", "minimumReward": 10000 + }, + { + "address": "cosmosvaloper157v7tczs40axfgejp2m43kwuzqe0wsy0rv8puv", + "botAddress": "cosmos1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjqmxay4", + "runTime": "18:00", + "minimumReward": 10000 } ], "authzSupport": true From 1a30f1fe444ef637838a99365e4ae1cfc60f5e02 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 14 Mar 2022 11:47:38 +0000 Subject: [PATCH 063/862] Add operators --- src/networks.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/networks.json b/src/networks.json index 4420cdfe5..205875360 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1702,6 +1702,30 @@ "botAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "runTime": "21:00", "minimumReward": 1000000000 + }, + { + "address": "cerberusvaloper1uvl2g9nd8qttjjyxjs30x7fj878d3wt4lt4juq", + "botAddress": "cerberus1e7tcnlaen39yafqxcj5zhnd0m8r8xlzyq92vrv", + "runTime": "21:00", + "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", + "botAddress": "cerberus1h43hjfxlz8t6y5wewma63jz7melv46gjcaue9a", + "runTime": "21:00", + "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1g3d36rfxfqtlnz3hzd05cs2wrjgykcz2g649a8", + "botAddress": "cerberus1e44rluarkdw56dy2turnwjtvtg4wqvs0wv4dzw", + "runTime": "21:00", + "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1td92z5qwl39f407gxf6lu2x80enat7t3jfefnf", + "botAddress": "cerberus1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjuux537", + "runTime": "18:00", + "minimumReward": 100000000 } ], "authzSupport": true From 5e9477bf4b155cddf67d84cdcf260297ed0f0a70 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 14 Mar 2022 12:04:10 +0000 Subject: [PATCH 064/862] Increase gas price for cerberus --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 205875360..8570baf8c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1693,7 +1693,7 @@ "rpcUrl": [ "https://rpc.cosmos.directory/cerberus" ], - "gasPrice": "0.025ucrbrus", + "gasPrice": "0.1ucrbrus", "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ From 88130a26a603191c80fe8449c0a6016a3e2d1e49 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 14 Mar 2022 12:15:28 +0000 Subject: [PATCH 065/862] Handle network not found properly --- src/components/App.js | 4 ++-- src/components/NetworkFinder.js | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 5a671cfda..015f3f4ef 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -276,8 +276,8 @@ class App extends React.Component {

    - REStake allows validators to this.setState({showAbout: true})} className="text-decoration-underline" role="button">auto-compound your {this.props.network.prettyName} staking rewards for you -

    + REStake allows validators to this.setState({ showAbout: true })} className="text-decoration-underline" role="button">auto-compound your {this.props.network.prettyName} staking rewards for you +

    {!this.state.address && ( !this.state.keplr diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 3693d1ed9..cce4d0002 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -55,16 +55,17 @@ function NetworkFinder() { useEffect(() => { if(Object.keys(state.networks).length && !state.network){ - const networkName = params.network || Object.keys(state.networks)[0] - const data = state.networks[networkName] + let networkName = params.network || Object.keys(state.networks)[0] + let data = state.networks[networkName] if(params.network && !data){ - navigate("/" + Object.keys(state.networks)[0]); + networkName = Object.keys(state.networks)[0] + data = state.networks[networkName] } if(!data){ setState({loading: false}) return } - if(!params.network){ + if(params.network != networkName){ navigate("/" + networkName); } Network(data).then(network => { From f613108247b3244f3619dce988fe4f348601c854 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 14 Mar 2022 12:24:10 +0000 Subject: [PATCH 066/862] Revert "Increase gas price for cerberus" 5e9477bf4b155cddf67d84cdcf260297ed0f0a70 --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 8570baf8c..205875360 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1693,7 +1693,7 @@ "rpcUrl": [ "https://rpc.cosmos.directory/cerberus" ], - "gasPrice": "0.1ucrbrus", + "gasPrice": "0.025ucrbrus", "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ From 209cad4f857b1bf23cf4ba1f5dd1a660635243e8 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Mon, 14 Mar 2022 14:42:00 +0200 Subject: [PATCH 067/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index d2bc7cee1..f604096ed 100644 --- a/src/networks.json +++ b/src/networks.json @@ -606,6 +606,12 @@ "botAddress": "chihuahua1uwqjtgjhjctjc45ugy7ev5prprhehc7w6gcqk8", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "chihuahuavaloper1fm68jvjpk0g7dvdq75czjynyszeaduxt5lc0a8", + "botAddress": "chihuahua1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjrwtn9h", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": false From 61ac07e1c39442a82a06ff10de9abfb0c4de1073 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Mon, 14 Mar 2022 14:42:53 +0200 Subject: [PATCH 068/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index f604096ed..114f931ba 100644 --- a/src/networks.json +++ b/src/networks.json @@ -610,7 +610,7 @@ { "address": "chihuahuavaloper1fm68jvjpk0g7dvdq75czjynyszeaduxt5lc0a8", "botAddress": "chihuahua1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjrwtn9h", - "runTime": "21:00", + "runTime": "18:00", "minimumReward": 1000 } ], From 8265a7995c8ae62dbbfa403109bf0ad2734cfe76 Mon Sep 17 00:00:00 2001 From: stan-bl <72133698+stan-bl@users.noreply.github.com> Date: Mon, 14 Mar 2022 14:15:31 +0100 Subject: [PATCH 069/862] Add BlueStake --- src/networks.json | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index d2bc7cee1..ab8306a33 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1037,7 +1037,7 @@ }, { "address": "desmosvaloper19c6nnp6afxj82ehxtlejyvg6zh58050jcgpdj6", - "botAddress": "desmos1az6huqur4g2u29fa6c5zzxmtljp0euqmnla6x5", + "botAddress": "desmos1m5hgzum68rjf4c7zhezgkj8hlnmr0kghzekmag", "runTime": "10:00", "minimumReward": 10000 }, @@ -1531,6 +1531,12 @@ "botAddress": "bitsong14td30sayes36tm042gcrwpynky927x2c434c06", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "bitsongvaloper1mceksy7l3xenuyx4dzl64erf3fh29a3r2ercnz", + "botAddress": "bitsong1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh6gfrgl", + "runTime": "10:00", + "minimumReward": 100000 } ], "authzSupport": true @@ -1557,6 +1563,12 @@ "botAddress": "persistence1f204r62y7l3ye7s3z05pvwufjawaf6vefpjmk0", "runTime": "23:00", "minimumReward": 10000 + }, + { + "address": "persistencevaloper1f9p23ru4sw8p2044237ckfhwdpklrn0ahdaujg", + "botAddress": "persistence1m5hgzum68rjf4c7zhezgkj8hlnmr0kghcdacy5", + "runTime": "10:00", + "minimumReward": 100000 } ], "authzSupport": true @@ -1599,6 +1611,12 @@ "botAddress": "ixo1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9qlc5jx", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "ixovaloper1a3qpp5ff9cgnusxwufdkmnvn83446lf9vq88ga", + "botAddress": "ixo1m5hgzum68rjf4c7zhezgkj8hlnmr0kghf59ewr", + "runTime": "10:00", + "minimumReward": 100000 } ], "authzSupport": false @@ -1687,6 +1705,12 @@ "botAddress": "fetch1v9vae554q55xa4pcfdm0p3kv8nxn50jkf3wk07", "runTime": "23:00", "minimumReward": 10000000000000000 + }, + { + "address": "fetchvaloper13uwrjldm8m45cuarjdvgk9qwnysf835rgrugc6", + "botAddress": "fetch1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh9uj0g8", + "runTime": "10:00", + "minimumReward": 10000000000000000 } ], "authzSupport": false From 9d2f71f5fdd225a1beebfedf69ed59fb06d95a00 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Mon, 14 Mar 2022 15:40:58 +0200 Subject: [PATCH 070/862] Add persistence network validator configuration --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 114f931ba..6ff40843e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1563,6 +1563,12 @@ "botAddress": "persistence1f204r62y7l3ye7s3z05pvwufjawaf6vefpjmk0", "runTime": "23:00", "minimumReward": 10000 + }, + { + "address": "persistencevaloper10sc98vt6saux8asexnsp2hgvkgmjmful8w5cuw", + "botAddress": "persistence1n8htrjks32y59u3fxwuy6fnz868n667k8ltezs", + "runTime": "18:00", + "minimumReward": 10000 } ], "authzSupport": true From a1756a39503229072b84e74e5be61a100bc5e8dd Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Mon, 14 Mar 2022 15:53:40 +0200 Subject: [PATCH 071/862] Add Osmosis blockchain validator support --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 6ff40843e..d9ea67575 100644 --- a/src/networks.json +++ b/src/networks.json @@ -145,6 +145,12 @@ "botAddress": "osmo19pqrxrl6n0g0mky4y79hlfzchprmsp5jmu2t6g", "runTime": "19:31", "minimumReward": 1000 + }, + { + "address": "osmovaloper1e8238v24qccht9mqc2w0r4luq462yxttfpaeam", + "botAddress": "osmo1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjgq4dj8", + "runTime": "18:00", + "minimumReward": 10000 } ], "authzSupport": true From a08209251bed3dcd296ce11361715bdfff6e0d07 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Mon, 14 Mar 2022 16:00:51 +0200 Subject: [PATCH 072/862] Add Juno blockchain POSTHUMAN validator details --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index d9ea67575..230ea9613 100644 --- a/src/networks.json +++ b/src/networks.json @@ -305,6 +305,12 @@ "botAddress": "juno14gra89zm8753pfuhvujjulmtkf2qq70f0k4r6g", "runTime": "17:28", "minimumReward": 10000 + }, + { + "address": "junovaloper1e8238v24qccht9mqc2w0r4luq462yxttjzn7qt", + "botAddress": "juno1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjkf9xrf", + "runTime": "18:00", + "minimumReward": 10000 } ], "authzSupport": true From 9d6a998442f7ed56dc936f0e4d7744783e7f9b4e Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Mon, 14 Mar 2022 16:06:38 +0200 Subject: [PATCH 073/862] Add Gravity Bridge blockchain POSTHUMAN validator settings --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 230ea9613..1a5dfa9a5 100644 --- a/src/networks.json +++ b/src/networks.json @@ -688,6 +688,12 @@ "botAddress": "gravity1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9m657na", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "gravityvaloper1epfpvqsc34sfserdx8x4t3aszdkar3w684fwr6", + "botAddress": "gravity1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjyt59pa", + "runTime": "18:00", + "minimumReward": 100000 } ], "authzSupport": true From 12ebb80be7b851192f1e4b6be80a8467123ecdd4 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Mon, 14 Mar 2022 16:12:58 +0200 Subject: [PATCH 074/862] Add LUM Network blockchain POSTHUMAN validator settings --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 1a5dfa9a5..6ce3cb6b4 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1326,6 +1326,12 @@ "botAddress": "lum1327kewr0m28edp6ee022myyxzvlf3g5exxpvr7", "runTime": "14:30", "minimumReward": 10000 + }, + { + "address": "lumvaloper1z7ss5slnpjfkceahl323ag9tkf3a6yrs88h4ng", + "botAddress": "lum1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgj43m53p", + "runTime": "18:00", + "minimumReward": 10000 } ], "authzSupport": true From ebf86aaa90545beb23e6a6de6e7b847ebcb01995 Mon Sep 17 00:00:00 2001 From: FarmerL <88308388+chleeab@users.noreply.github.com> Date: Mon, 14 Mar 2022 23:06:02 +0800 Subject: [PATCH 075/862] Add Chihuahua Operator SF RSE Land --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index d2bc7cee1..70aa5aa77 100644 --- a/src/networks.json +++ b/src/networks.json @@ -606,6 +606,12 @@ "botAddress": "chihuahua1uwqjtgjhjctjc45ugy7ev5prprhehc7w6gcqk8", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "chihuahuavaloper1x8drn3260ezg3se6j7w7wqhy090u4wsjt9raj8", + "botAddress": "chihuahua1x9e5qkjzzmfhw4dt05aludazwvd5x634ssjj76", + "runTime": "16:00", + "minimumReward": 10000000 } ], "authzSupport": false From a58b70912c2b40c661d8b46f976d43e04df23100 Mon Sep 17 00:00:00 2001 From: sryps Date: Mon, 14 Mar 2022 17:22:35 +0000 Subject: [PATCH 076/862] Add Sunshine Val --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index d2bc7cee1..93bfb15b3 100644 --- a/src/networks.json +++ b/src/networks.json @@ -145,6 +145,12 @@ "botAddress": "osmo19pqrxrl6n0g0mky4y79hlfzchprmsp5jmu2t6g", "runTime": "19:31", "minimumReward": 1000 + }, + { + "address": "osmovaloper1vmkt6ysppk8m4rhlq78tpqyh429lhlsah4mn3y", + "botAddress": "osmo1syc66updwkat9nhmfplzzvflh5789phc3z4kgk", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": true From d7fa50c17913bca394b0c0b8c05b87e53e5493dc Mon Sep 17 00:00:00 2001 From: altbro <89856346+altbro@users.noreply.github.com> Date: Mon, 14 Mar 2022 18:48:09 +0100 Subject: [PATCH 077/862] Add Bro_n_Bro validator --- src/networks.json | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/networks.json b/src/networks.json index d2bc7cee1..6c4827cbd 100644 --- a/src/networks.json +++ b/src/networks.json @@ -145,6 +145,12 @@ "botAddress": "osmo19pqrxrl6n0g0mky4y79hlfzchprmsp5jmu2t6g", "runTime": "19:31", "minimumReward": 1000 + }, + { + "address": "osmovaloper13tk45jkxgf7w0nxquup3suwaz2tx483xe832ge", + "botAddress": "osmo1grfk7t0k42yxpwqhr7kt4te7j73m2a3v27wqh5", + "runTime": "13:37", + "minimumReward": 10000 } ], "authzSupport": true @@ -299,6 +305,12 @@ "botAddress": "juno14gra89zm8753pfuhvujjulmtkf2qq70f0k4r6g", "runTime": "17:28", "minimumReward": 10000 + }, + { + "address": "junovaloper1quqxfrxkycr0uzt4yk0d57tcq3zk7srm7sm6r8", + "botAddress": "juno1grfk7t0k42yxpwqhr7kt4te7j73m2a3v5h7tx6", + "runTime": "13:37", + "minimumReward": 10000 } ], "authzSupport": true @@ -670,6 +682,12 @@ "botAddress": "gravity1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9m657na", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "gravityvaloper1vyd4k5j636erx5y5kdqghdu3rfjtwc48vdc7r6", + "botAddress": "gravity1grfk7t0k42yxpwqhr7kt4te7j73m2a3vx40gyw", + "runTime": "13:37", + "minimumReward": 10000 } ], "authzSupport": true @@ -956,6 +974,12 @@ "botAddress": "emoney1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9sfujpg", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "emoneyvaloper149vyxd36kxpg46rralaw6eejv4d9daqc3nv642", + "botAddress": "emoney1grfk7t0k42yxpwqhr7kt4te7j73m2a3vdx8ykm", + "runTime": "13:37", + "minimumReward": 100000 } ], "authzSupport": false @@ -1172,6 +1196,12 @@ "botAddress": "evmos1axp36fwjy2vzlaym52urh80wq9l7z2t0shrj2y", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "evmosvaloper1ce4vh0e5kanlgc7z0rhcemvd8erjnfzcyfecl7", + "botAddress": "evmos1grfk7t0k42yxpwqhr7kt4te7j73m2a3vqyv7mw", + "runTime": "13:37", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1372,6 +1402,12 @@ "botAddress": "stars14gra89zm8753pfuhvujjulmtkf2qq70fdcp9k9", "runTime": "17:28", "minimumReward": 10000 + }, + { + "address": "starsvaloper1y58hfnm90r4efhlydx0gavz57lvm7k6uulkg3h", + "botAddress": "stars1grfk7t0k42yxpwqhr7kt4te7j73m2a3vke2d2h", + "runTime": "13:37", + "minimumReward": 10000 } ], "authzSupport": true From 61e9603f2eb05373e6698b86aff74ea3a6050618 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 14 Mar 2022 18:58:00 +0000 Subject: [PATCH 078/862] Add operators --- src/networks.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/networks.json b/src/networks.json index 205875360..ba1f37c65 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1726,6 +1726,18 @@ "botAddress": "cerberus1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjuux537", "runTime": "18:00", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1r34mlqewsxrde38vp3tzwh3rk5vt6ez0slzuvu", + "botAddress": "cerberus16r56awgxe7mkrwpsnurjexfdg0n3f4f3dgf090", + "runTime": "21:00", + "minimumReward": 10000 + }, + { + "address": "cerberusvaloper1lxz6ucd5qydhpwtpatn9wu83fc002v08tek6zh", + "botAddress": "cerberus1305e92ng6a9pdh3ttc3ld459ckxupxt3tt620v", + "runTime": "19:00", + "minimumReward": 10000 } ], "authzSupport": true From 85a7dbbc43ccf53af886c0f20fe28117f9ed3ca6 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 14 Mar 2022 19:57:52 +0000 Subject: [PATCH 079/862] Add cros-nest --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2e967d88c..0d8d4f8d2 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1744,6 +1744,12 @@ "botAddress": "cerberus1305e92ng6a9pdh3ttc3ld459ckxupxt3tt620v", "runTime": "19:00", "minimumReward": 10000 + }, + { + "address": "cerberusvaloper1y3thykrje2fmdcf8wva8l8kphmwpx89ur4erq3", + "botAddress": "cerberus1v9vae554q55xa4pcfdm0p3kv8nxn50jkxt8mcz", + "runTime": "23:00", + "minimumReward": 10000 } ], "authzSupport": true From 442162ba8a42f7aaf3125d4b94c3fcb3ee13e459 Mon Sep 17 00:00:00 2001 From: Iprouteth0 Date: Mon, 14 Mar 2022 16:59:21 -0500 Subject: [PATCH 080/862] Add HUAHUA Add $HUAHUA restake operator information for Badger Bite --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index d2bc7cee1..12ac31dd8 100644 --- a/src/networks.json +++ b/src/networks.json @@ -606,6 +606,12 @@ "botAddress": "chihuahua1uwqjtgjhjctjc45ugy7ev5prprhehc7w6gcqk8", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "chihuahuavaloper16kctxuen3x2avvjj2mdwsl6g8n7d4vjkq6c6q9", + "botAddress": "chihuahua1m8d834ykcy3dgrdve4xhxky3u3c2tl9d3d7gy9", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": false From e75094a8da8542adc5eb02617993689b018f25bc Mon Sep 17 00:00:00 2001 From: Iprouteth0 Date: Mon, 14 Mar 2022 17:06:14 -0500 Subject: [PATCH 081/862] Add Sifchain Add Sifchain Badger Bite Operator details --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 12ac31dd8..724981b38 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1216,6 +1216,12 @@ "botAddress": "sif1mjq48r6435aewerpruwc8up3tz3rzan2fy3pw7", "runTime": "19:00", "minimumReward": 1000 + }, + { + "address": "sifvaloper1a2ly5lyry8l4wvx27fzdu6ha5mj53sxceu4uvj", + "botAddress": "sif1m8d834ykcy3dgrdve4xhxky3u3c2tl9dh9us2v", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": false From 6658082b7e7cc8a4fd1ee2e0a2ee48a13c4e72cb Mon Sep 17 00:00:00 2001 From: Iprouteth0 Date: Mon, 14 Mar 2022 17:11:32 -0500 Subject: [PATCH 082/862] Add DARC Add Badger Bite Konstellation details --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 724981b38..80adf0e43 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1681,6 +1681,12 @@ "botAddress": "darc1v9vae554q55xa4pcfdm0p3kv8nxn50jk9dnrq7", "runTime": "23:00", "minimumReward": 100000 + }, + { + "address": "darcvaloper1yww5jnu4y39vyea47xs4z7r45yz9rl5dpxg67v", + "botAddress": "darc1m8d834ykcy3dgrdve4xhxky3u3c2tl9dde8hgs", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": false From 90ecb45290b0126d7bfc2e9f6b516289f380459d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Iglesias=20Castre=C3=B1o?= Date: Tue, 15 Mar 2022 00:43:16 +0100 Subject: [PATCH 083/862] Update Stakely networks configuration --- src/networks.json | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/networks.json b/src/networks.json index 4a1962d8a..d8ae5b4dd 100644 --- a/src/networks.json +++ b/src/networks.json @@ -292,7 +292,7 @@ "address": "junovaloper1hx9yj7qgnp8zhkrqfanvz74mcsg9d8eyskvsxg", "botAddress": "juno1hl0jgxre5z0nkyjj07c5vfdy44yk44at0dphgp", "runTime": "22:30", - "minimumReward": 1000 + "minimumReward": 100000 }, { "address": "junovaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgj6jfwcp", @@ -357,7 +357,7 @@ "address": "cosmosvaloper16yupepagywvlk7uhpfchtwa0stu5f8cyhh54f2", "botAddress": "cosmos1hl0jgxre5z0nkyjj07c5vfdy44yk44atelzv0a", "runTime": "22:30", - "minimumReward": 10000 + "minimumReward": 100000 }, { "address": "cosmosvaloper15r4tc0m6hc7z8drq3dzlrtcs6rq2q9l2nvwher", @@ -695,7 +695,7 @@ "address": "regenvaloper1kzhxmgp8z05zrj90nd8h5qx9pm949an5y7nwsz", "botAddress": "regen1hl0jgxre5z0nkyjj07c5vfdy44yk44atxafsee", "runTime": "22:30", - "minimumReward": 10000 + "minimumReward": 2000000 } ], "authzSupport": true @@ -727,7 +727,7 @@ "address": "terravaloper108lmrztvc3pc3w774shgvpry4d3lf79k2ummna", "botAddress": "terra1hl0jgxre5z0nkyjj07c5vfdy44yk44atlmcvda", "runTime": "22:30", - "minimumReward": 1000 + "minimumReward": 10000 }, { "address": "terravaloper1audgfvmgt0js54p3s8kj3r40uwej6vy2tv6rrw", @@ -890,7 +890,7 @@ "address": "bcnavaloper1gsq4dsxvgsswkgrsftz0k905rzjc0n2hv26xgj", "botAddress": "bcna1hl0jgxre5z0nkyjj07c5vfdy44yk44atr0jd80", "runTime": "22:30", - "minimumReward": 10000 + "minimumReward": 20000000 } ], "authzSupport": false @@ -1045,7 +1045,7 @@ "address": "desmosvaloper1s5gnmccngltteh6ugtsz7fdrykc294tnw2e7pz", "botAddress": "desmos1hl0jgxre5z0nkyjj07c5vfdy44yk44atd80uc9", "runTime": "22:30", - "minimumReward": 1000 + "minimumReward": 100000 }, { "address": "desmosvaloper1jrld5g998gqm4yx26l6cvhxz7y5adgxqzfdpes", @@ -1079,7 +1079,7 @@ "address": "crocncl1tkev46yqrjzrjzrqtty30ex68eja2zcltyq2vj", "botAddress": "cro1hl0jgxre5z0nkyjj07c5vfdy44yk44atpy24nv", "runTime": "22:30", - "minimumReward": 100000 + "minimumReward": 1000000 } ], "authzSupport": true @@ -1160,6 +1160,12 @@ "botAddress": "evmos1axp36fwjy2vzlaym52urh80wq9l7z2t0shrj2y", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "evmosvaloper19fxanpnjlggzuur3m3x0puk5ez7j9lrttexwsw", + "botAddress": "evmos1hl0jgxre5z0nkyjj07c5vfdy44yk44atm7nz44", + "runTime": "22:30", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1198,6 +1204,12 @@ "botAddress": "sif1mjq48r6435aewerpruwc8up3tz3rzan2fy3pw7", "runTime": "19:00", "minimumReward": 1000 + }, + { + "address": "sifvaloper1r9ssdmc2xm3kv2y5m35mhrvnqms4pjw27umt2l", + "botAddress": "sif1hl0jgxre5z0nkyjj07c5vfdy44yk44atuzd6qk", + "runTime": "22:30", + "minimumReward": 100000 } ], "authzSupport": false @@ -1283,7 +1295,7 @@ "address": "lumvaloper1zs59ua4l0h6a2hnh08v8qn76f6qh7uxc22vey5", "botAddress": "lum1hl0jgxre5z0nkyjj07c5vfdy44yk44atv4l96f", "runTime": "22:30", - "minimumReward": 1000 + "minimumReward": 100000 } ], "authzSupport": true @@ -1532,6 +1544,12 @@ "botAddress": "agoric148kutx4cag3js995cesc0rpuwpe85hq7zk5ry3", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "agoricvaloper1mxhgvj2c93xahahx9d9fc7rwwtufqza5cn6uhn", + "botAddress": "agoric1hl0jgxre5z0nkyjj07c5vfdy44yk44attzqnlt", + "runTime": "22:30", + "minimumReward": 100000 } ], "authzSupport": true From 55db6eabe405b0bb2b6a7aa0005c18fa545604d1 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 00:14:02 +0000 Subject: [PATCH 084/862] Multiple runtimes --- README.md | 10 ++++-- docker-compose.yml | 1 - package-lock.json | 25 +++++++++++++ package.json | 2 ++ src/components/App.js | 1 - src/components/CountdownRestake.js | 44 ++++++----------------- src/components/Delegate.js | 15 +++++--- src/components/Delegations.js | 29 +++++++++++----- src/networks.json | 2 +- src/utils/Operator.mjs | 56 ++++++++++++++++++++++++++++-- 10 files changed, 130 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 4b5bbe1ab..cfcbc6224 100644 --- a/README.md +++ b/README.md @@ -210,16 +210,20 @@ You now need to update the [networks.json](./src/networks.json) file at `./src/n "operators": [{ "address": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "botAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", - "runTime": "21:00", + "runTime": ["09:00", "21:00"], "minimumReward": 1000 }, ``` -`address` is your validator's address, and `botAddress` is the address from your new hot wallet you generated earlier. `runTime` is the time in UTC that your bot will run (multiple planned in the future). `minimumReward` is the minimum reward to trigger autostaking, otherwise the address be skipped. +`address` is your validator's address, and `botAddress` is the address from your new hot wallet you generated earlier. + +`runTime` is the time *in UTC* that you intend to run your bot, and there are a few options. Pass a single time, e.g. `09:00` to specify a single run at 9am UTC. Use an array for multiple specified times, e.g. `["09:00", "21:00"]`. Use an interval string for multiple times per hour/day, e.g. `"every 15 minutes"`. + +`minimumReward` is the minimum reward to trigger autostaking, otherwise the address be skipped. This could be set higher for more frequent restaking. Note this is in the base denomination, e.g. `uosmo`. Repeat this config for all networks you want to REStake for. -Note that the `botAddress` is the address which will be granted by the delegator to carry out their restaking transactions if it wasn't clear. +Note that the `botAddress` is the address which will be granted by the delegator in the UI to carry out their restaking transactions. #### Submit your operator diff --git a/docker-compose.yml b/docker-compose.yml index cb09cef10..684359d39 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,4 +9,3 @@ services: - '1234:1234' volumes: - .:/usr/src/app - - /usr/src/app/node_modules diff --git a/package-lock.json b/package-lock.json index 8216c0310..88ae2c2d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,8 @@ "buffer": "^6.0.3", "cosmjs-types": "^0.4.1", "fuzzy-search": "^3.2.1", + "moment": "^2.29.1", + "parse-duration": "^1.0.2", "path": "^0.12.7", "react": "^17.0.2", "react-bootstrap": "^2.1.2", @@ -8066,6 +8068,14 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -8421,6 +8431,11 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/parse-duration": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-1.0.2.tgz", + "integrity": "sha512-Dg27N6mfok+ow1a2rj/nRjtCfaKrHUZV2SJpEn/s8GaVUSlf4GGRCRP1c13Hj+wfPKVMrFDqLMLITkYKgKxyyg==" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -15769,6 +15784,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -16034,6 +16054,11 @@ "safe-buffer": "^5.1.1" } }, + "parse-duration": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-1.0.2.tgz", + "integrity": "sha512-Dg27N6mfok+ow1a2rj/nRjtCfaKrHUZV2SJpEn/s8GaVUSlf4GGRCRP1c13Hj+wfPKVMrFDqLMLITkYKgKxyyg==" + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", diff --git a/package.json b/package.json index 629687205..1d847f194 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "buffer": "^6.0.3", "cosmjs-types": "^0.4.1", "fuzzy-search": "^3.2.1", + "moment": "^2.29.1", + "parse-duration": "^1.0.2", "path": "^0.12.7", "react": "^17.0.2", "react-bootstrap": "^2.1.2", diff --git a/src/components/App.js b/src/components/App.js index 015f3f4ef..c0ef0f1f1 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -6,7 +6,6 @@ import AlertMessage from './AlertMessage' import NetworkSelect from './NetworkSelect' import Wallet from './Wallet' import Coins from './Coins' -import ValidatorLink from './ValidatorLink' import About from './About' import { MsgGrant, MsgRevoke } from "cosmjs-types/cosmos/authz/v1beta1/tx.js"; diff --git a/src/components/CountdownRestake.js b/src/components/CountdownRestake.js index e72b37ae7..8476cc5c2 100644 --- a/src/components/CountdownRestake.js +++ b/src/components/CountdownRestake.js @@ -1,41 +1,19 @@ import React from 'react'; import TooltipIcon from './TooltipIcon'; -import Coins from './Coins'; import Countdown from 'react-countdown'; + import { ClockHistory -} from 'react-bootstrap-icons'; +} from 'react-bootstrap-icons' -function RevokeRestake(props) { +function CountdownRestake(props) { const { operator } = props - const minimumReward = () => { - return { amount: operator.data.minimumReward, denom: props.network.denom } - } - - const nextRun = (delayHour) => { - const now = new Date() - if(!operator || !operator.data.runTime){ - return null - } - const runTime = operator.data.runTime.split(':') - let day - if(delayHour){ - day = now.getHours() > runTime[0] ? now.getDate() + 1 : now.getDate() - }else{ - day = now.getHours() >= runTime[0] ? now.getDate() + 1 : now.getDate() - } - - return new Date( - now.getFullYear(), - now.getMonth(), - day, - runTime[0], - runTime[1], - runTime[2] || 0 - ) + const nextRun = () => { + if(!operator) return null + return 0 + operator.nextRun() } const countdownRenderer = ({ hours, minutes, seconds, completed }) => { @@ -44,25 +22,25 @@ function RevokeRestake(props) { } else { let string = '' if(hours > 0) string = string.concat(hours + 'h ') - if(minutes > 0) string = string.concat(minutes + 'm ') + if(minutes > 0) string = string.concat(minutes + (hours > 0 ? 'm ' : ' minutes ')) if(props.showSeconds) string = string.concat(seconds + 's') + if(string === '') return

    Validator is restaking now

    return (

    Validator will REStake in
    {string}

    ) } } return ( - } identifier={operator.address}> + } identifier={operator.address}>
    -

    Minimum reward is

    ) } -export default RevokeRestake; +export default CountdownRestake; diff --git a/src/components/Delegate.js b/src/components/Delegate.js index 3127c4e29..9d5b8d5b3 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -24,7 +24,6 @@ import { function Delegate(props) { const [show, setShow] = useState(false); const [selectedValidator, setSelectedValidator] = useState(!props.redelegate && props.validator); - const target = useRef(null); const handleOpen = () => { setShow(true) @@ -71,6 +70,13 @@ function Delegate(props) { return } + const minimumReward = () => { + return { + amount: operator().data.minimumReward, + denom: props.network.denom + } + } + const actionText = () => { if (props.redelegate) return 'Redelegate' if (props.undelegate) return 'Undelegate' @@ -174,10 +180,9 @@ function Delegate(props) {
    REStake - {!!operator() ? - - : + {!!operator() ? ( + {operator().runTimes().join(', ')} ( min) + ) : } identifier={selectedValidator.operator_address} tooltip="This validator is not a REStake operator" /> }
    {operator ? this.restakePossible() && delegation ? ( this.grantsValid(operator) ? ( - ) : ( } identifier={validatorAddress} tooltip="This validator is not a REStake operator" /> )} {validator.commission.commission_rates.rate * 100}% + {operator && ( + {operator.frequency()}} identifier={operator.address}> +
    +

    Minimum reward is

    +
    +
    + )} +
    {validator.commission.commission_rates.rate * 100}% - + - {denomRewards && } + {denomRewards && }
    @@ -456,8 +469,8 @@ class Delegations extends React.Component {
    Validator REStakeCommissionAPYFrequencyCommission Delegation Rewards REStake {!!operator() ? ( - {operator().runTimes().join(', ')} ( min) + {operator().runTimesString()} ( min) ) : } identifier={selectedValidator.operator_address} tooltip="This validator is not a REStake operator" /> } From 838dfaa82ebaeb0e10c799a11e61045a64a1ad61 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 01:20:19 +0000 Subject: [PATCH 089/862] Improve countdown complete display --- src/components/CountdownRestake.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/CountdownRestake.js b/src/components/CountdownRestake.js index 8476cc5c2..a4eee475d 100644 --- a/src/components/CountdownRestake.js +++ b/src/components/CountdownRestake.js @@ -13,12 +13,12 @@ function CountdownRestake(props) { const nextRun = () => { if(!operator) return null - return 0 + operator.nextRun() + return operator.nextRun() + (60 * 1000) } const countdownRenderer = ({ hours, minutes, seconds, completed }) => { if (completed) { - return

    Running now. The next run will be at {operator.data.runTime} tomorrow

    + return

    Validator is restaking now

    } else { let string = '' if(hours > 0) string = string.concat(hours + 'h ') From 7bfa1e5c0f72b02f7f6b2d3d9f72480631b049a4 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 08:55:10 +0000 Subject: [PATCH 090/862] Update operators and readme --- README.md | 2 +- src/networks.json | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cfcbc6224..fa61e0e94 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Update your local repository and pre-build your Docker containers with the follo ```bash git pull -docker-compose build +docker-compose build --no-cache ``` ### Setting up Cron to make sure the script runs daily diff --git a/src/networks.json b/src/networks.json index 73dcaa871..218a5b380 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1862,13 +1862,13 @@ { "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", "botAddress": "cerberus1h43hjfxlz8t6y5wewma63jz7melv46gjcaue9a", - "runTime": "21:00", + "runTime": "every 15 minutes", "minimumReward": 100000000 }, { "address": "cerberusvaloper1g3d36rfxfqtlnz3hzd05cs2wrjgykcz2g649a8", "botAddress": "cerberus1e44rluarkdw56dy2turnwjtvtg4wqvs0wv4dzw", - "runTime": "21:00", + "runTime": "every 15 minutes", "minimumReward": 100000000 }, { @@ -1886,7 +1886,7 @@ { "address": "cerberusvaloper1lxz6ucd5qydhpwtpatn9wu83fc002v08tek6zh", "botAddress": "cerberus1305e92ng6a9pdh3ttc3ld459ckxupxt3tt620v", - "runTime": "19:00", + "runTime": "every 5 minutes", "minimumReward": 10000 }, { @@ -1900,6 +1900,12 @@ "botAddress": "cerberus1m8d834ykcy3dgrdve4xhxky3u3c2tl9dwln0sv", "runTime": "every 1 hour", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1l6kfy4xvy0a34fseyhvc6f6k8asukfdz8462mg", + "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", + "runTime": "22:10", + "minimumReward": 1000000 } ], "authzSupport": true From 81defa7950093d062e2f67ff4d8f7e83acf97b00 Mon Sep 17 00:00:00 2001 From: FarmerL <88308388+chleeab@users.noreply.github.com> Date: Tue, 15 Mar 2022 17:03:23 +0800 Subject: [PATCH 091/862] Update networks.json --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 218a5b380..9bd406ba0 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1906,7 +1906,13 @@ "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", "runTime": "22:10", "minimumReward": 1000000 - } + }, + { + "address": "cerberusvalcons1ludd24gnug8drlp665uv4clnk04dednug6sexl", + "botAddress": "cerberus1x9e5qkjzzmfhw4dt05aludazwvd5x6340zl42n", + "runTime": "every 1 hour" + "minimumReward": 100000000 + } ], "authzSupport": true } From 7aefdc0f06989153502c248ddde4803bdf856c58 Mon Sep 17 00:00:00 2001 From: NvztCRK Date: Tue, 15 Mar 2022 12:58:49 +0300 Subject: [PATCH 092/862] Goatlabs V is added to Cerberus Authz. --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 218a5b380..1ac5817cf 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1847,6 +1847,12 @@ "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ + { + "address": "cerberusvaloper18twaqg4vaextnm5dddgmlm5quwymaswkchqt3a", + "botAddress": "cerberus1uxwnhyjd2m5y6mpqsdg95y4ty0a5jtfrjhkgq5", + "runTime": "every 5 minutes", + "minimumReward": 100000000 + }, { "address": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "botAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", From 0cb9eb38ebf89ff947f28ca49a153b611c940c40 Mon Sep 17 00:00:00 2001 From: zdeadex <51711900+zdeadex@users.noreply.github.com> Date: Tue, 15 Mar 2022 11:12:07 +0100 Subject: [PATCH 093/862] Update networks.json --- src/networks.json | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/networks.json b/src/networks.json index 218a5b380..b1ad4fb39 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1639,7 +1639,14 @@ "botAddress": "bitsong1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh6gfrgl", "runTime": "10:00", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper18wf0w252jxk3kgl5vlst8ttat8xzfnvejuftk2", + "botAddress": "bitsong16swn43y3wt70jex2l0dzcf987w8000psq7mpu8", + "runTime": "21:00", + "minimumReward": 100000 } + ], "authzSupport": true }, @@ -1677,6 +1684,12 @@ "botAddress": "persistence1m5hgzum68rjf4c7zhezgkj8hlnmr0kghcdacy5", "runTime": "10:00", "minimumReward": 100000 + }, + { + "address": "persistencevaloper1r7gdc8ag4ktmrvhed2xp09n3klrjuznwdzsru2", + "botAddress": "persistence1cuzmc99n343he6mvj3mzq7fnc4ncqlau3zter9", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true @@ -1731,6 +1744,12 @@ "botAddress": "ixo1m5hgzum68rjf4c7zhezgkj8hlnmr0kghf59ewr", "runTime": "10:00", "minimumReward": 100000 + }, + { + "address": "ixovaloper1ktjjvaurdupt7nvmfkgycn7mv0jcx5frz0dwzf", + "botAddress": "ixo1mjq48r6435aewerpruwc8up3tz3rzan2nvq99x", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": false @@ -1757,6 +1776,12 @@ "botAddress": "ki1v9vae554q55xa4pcfdm0p3kv8nxn50jktpkafa", "runTime": "23:00", "minimumReward": 100000 + }, + { + "address": "kivaloper14g4ztk36q529vx8ash7y8hr047kgzxlvyh2ch5", + "botAddress": "ki1mjq48r6435aewerpruwc8up3tz3rzan2a50c9p", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": false @@ -1807,6 +1832,12 @@ "botAddress": "darc1m8d834ykcy3dgrdve4xhxky3u3c2tl9dde8hgs", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "darcvaloper1pk8e47hdlw9mtnphz3klhkeed8wlq8gwjw7m0w", + "botAddress": "darc1mjq48r6435aewerpruwc8up3tz3rzan2nc2xvz", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": false @@ -1831,6 +1862,12 @@ "botAddress": "fetch1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh9uj0g8", "runTime": "10:00", "minimumReward": 10000000000000000 + }, + { + "address": "fetchvaloper1mr8tqsr3cjp4vh30j8h32uluh5nhnjnh8lkef0", + "botAddress": "fetch1mjq48r6435aewerpruwc8up3tz3rzan2lyhnrz", + "runTime": "21:00", + "minimumReward": 10000000000000000 } ], "authzSupport": false @@ -1906,6 +1943,12 @@ "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", "runTime": "22:10", "minimumReward": 1000000 + }, + { + "address": "cerberusvaloper16cfkt9n0pcftg0ky3rk9aegqdp383uwge38rdl", + "botAddress": "cerberus1mjq48r6435aewerpruwc8up3tz3rzan2s77757", + "runTime": "every 30 minutes", + "minimumReward": 1000000 } ], "authzSupport": true From 00c2831ce5f8edbd89ca5d4e4a3af1f176131378 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Tue, 15 Mar 2022 12:28:45 +0200 Subject: [PATCH 094/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 218a5b380..b050afb14 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1874,7 +1874,7 @@ { "address": "cerberusvaloper1td92z5qwl39f407gxf6lu2x80enat7t3jfefnf", "botAddress": "cerberus1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjuux537", - "runTime": "18:00", + "runTime": "every 1 hour", "minimumReward": 100000000 }, { From 3ecbe600981b03056aea2ae291e99a26b7b5ee85 Mon Sep 17 00:00:00 2001 From: Albert Andrejev Date: Tue, 15 Mar 2022 12:33:50 +0200 Subject: [PATCH 095/862] Update networks.json --- src/networks.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/networks.json b/src/networks.json index b050afb14..34ef2a725 100644 --- a/src/networks.json +++ b/src/networks.json @@ -149,7 +149,7 @@ { "address": "osmovaloper1e8238v24qccht9mqc2w0r4luq462yxttfpaeam", "botAddress": "osmo1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjgq4dj8", - "runTime": "18:00", + "runTime": "every 1 hour", "minimumReward": 10000 }, { @@ -321,7 +321,7 @@ { "address": "junovaloper1e8238v24qccht9mqc2w0r4luq462yxttjzn7qt", "botAddress": "juno1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjkf9xrf", - "runTime": "18:00", + "runTime": "every 1 hour", "minimumReward": 10000 }, { @@ -416,7 +416,7 @@ { "address": "cosmosvaloper157v7tczs40axfgejp2m43kwuzqe0wsy0rv8puv", "botAddress": "cosmos1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjqmxay4", - "runTime": "18:00", + "runTime": "every 1 hour", "minimumReward": 10000 } ], @@ -640,7 +640,7 @@ { "address": "chihuahuavaloper1fm68jvjpk0g7dvdq75czjynyszeaduxt5lc0a8", "botAddress": "chihuahua1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjrwtn9h", - "runTime": "18:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -722,7 +722,7 @@ { "address": "gravityvaloper1epfpvqsc34sfserdx8x4t3aszdkar3w684fwr6", "botAddress": "gravity1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjyt59pa", - "runTime": "18:00", + "runTime": "every 1 hour", "minimumReward": 100000 }, { @@ -1396,7 +1396,7 @@ { "address": "lumvaloper1z7ss5slnpjfkceahl323ag9tkf3a6yrs88h4ng", "botAddress": "lum1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgj43m53p", - "runTime": "18:00", + "runTime": "every 1 hour", "minimumReward": 10000 } ], @@ -1669,7 +1669,7 @@ { "address": "persistencevaloper10sc98vt6saux8asexnsp2hgvkgmjmful8w5cuw", "botAddress": "persistence1n8htrjks32y59u3fxwuy6fnz868n667k8ltezs", - "runTime": "18:00", + "runTime": "every 1 hour", "minimumReward": 10000 }, { From 027e81021e46e3d5edc51ab69570c63aa2046044 Mon Sep 17 00:00:00 2001 From: chillyvee Date: Tue, 15 Mar 2022 10:46:50 +0000 Subject: [PATCH 096/862] Add Cerberus Chill Validation --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 218a5b380..5c7cfae60 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1906,6 +1906,12 @@ "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", "runTime": "22:10", "minimumReward": 1000000 + }, + { + "address": "cerberusvaloper10ypajp3q5zu5yxfud3ayd95th0k7467k3s5vh7", + "botAddress": "cerberus13yfd74cezsrjcmhvmh6wkfwfuj7fds5eenhn64", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From aec89864b19a0579ab284111f22440fb6245f4ba Mon Sep 17 00:00:00 2001 From: zdeadex <51711900+zdeadex@users.noreply.github.com> Date: Tue, 15 Mar 2022 12:10:15 +0100 Subject: [PATCH 097/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index b1ad4fb39..ba16ca980 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1947,7 +1947,7 @@ { "address": "cerberusvaloper16cfkt9n0pcftg0ky3rk9aegqdp383uwge38rdl", "botAddress": "cerberus1mjq48r6435aewerpruwc8up3tz3rzan2s77757", - "runTime": "every 30 minutes", + "runTime": "21:00", "minimumReward": 1000000 } ], From 87022a2f957a282402bc7b14ee202239c6c6ad41 Mon Sep 17 00:00:00 2001 From: Luis Lopes Date: Tue, 15 Mar 2022 11:11:48 +0000 Subject: [PATCH 098/862] Add tedcrypto --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 218a5b380..7a670e6bc 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1906,6 +1906,12 @@ "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", "runTime": "22:10", "minimumReward": 1000000 + }, + { + "address": "cerberusvaloper1c4pewt03f0fnwrf4vcgawzl6pllm3l9kfffu7l", + "botAddress": "cerberus1jsmtq9z9qpeda0pyff9w8u66mkxrtzser8cjew", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From 81efa6879e67e6c908a7069abe0a2b6bbae68f3a Mon Sep 17 00:00:00 2001 From: windpowerstake <93733899+windpowerstake@users.noreply.github.com> Date: Tue, 15 Mar 2022 12:20:29 +0100 Subject: [PATCH 099/862] update networks.json - windpowerstake cerberus --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 218a5b380..867984966 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1906,6 +1906,12 @@ "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", "runTime": "22:10", "minimumReward": 1000000 + }, + { + "address": "cerberusvaloper1cy9jhuf3cx2z33duxld7p57gtege6hfhrar9g9", + "botAddress": "cerberus1hpwxnnrtsc6zjx4glup77tfe5z54lmtqe3tusv", + "runTime": "10:40", + "minimumReward": 1000000 } ], "authzSupport": true From 30843b86e47823ab78ed5952e0de22024bf1e67f Mon Sep 17 00:00:00 2001 From: Kleomedes Date: Tue, 15 Mar 2022 11:23:43 +0000 Subject: [PATCH 100/862] Add Kleomedes to Cerberus in networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 218a5b380..1f16922b8 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1847,6 +1847,12 @@ "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ + { + "address": "cerberusvaloper1u7m9kwj8tvwjaxympp3r8xq7d0pf5la994x7u8", + "botAddress": "cerberus1r8egcurpwxftegr07gjv9gwffw4fk009hq0rdg", + "runTime": "every 15 minutes", + "minimumReward": 100000000 + }, { "address": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "botAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", From dad5c0138a2d7fa44eca9227ad577842f02619d9 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 11:29:31 +0000 Subject: [PATCH 101/862] Fix node_modules issue with docker-compose --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 684359d39..cb09cef10 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,3 +9,4 @@ services: - '1234:1234' volumes: - .:/usr/src/app + - /usr/src/app/node_modules From c075f857a57f9718137b8968abed5cb63d3aaac6 Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Tue, 15 Mar 2022 08:10:15 -0400 Subject: [PATCH 102/862] Update WhisperNode + Infinity frequency --- src/networks.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/networks.json b/src/networks.json index 218a5b380..088419578 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1468,7 +1468,7 @@ "botAddress": "stars14gra89zm8753pfuhvujjulmtkf2qq70fdcp9k9", "runTime": "17:28", "minimumReward": 10000 - }, + }, { "address": "starsvaloper1y58hfnm90r4efhlydx0gavz57lvm7k6uulkg3h", "botAddress": "stars1grfk7t0k42yxpwqhr7kt4te7j73m2a3vke2d2h", @@ -1862,14 +1862,14 @@ { "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", "botAddress": "cerberus1h43hjfxlz8t6y5wewma63jz7melv46gjcaue9a", - "runTime": "every 15 minutes", - "minimumReward": 100000000 + "runTime": "every 4 minutes", + "minimumReward": 100 }, { "address": "cerberusvaloper1g3d36rfxfqtlnz3hzd05cs2wrjgykcz2g649a8", "botAddress": "cerberus1e44rluarkdw56dy2turnwjtvtg4wqvs0wv4dzw", - "runTime": "every 15 minutes", - "minimumReward": 100000000 + "runTime": "every 4 minutes", + "minimumReward": 100 }, { "address": "cerberusvaloper1td92z5qwl39f407gxf6lu2x80enat7t3jfefnf", From 6c714e15822b01fa0db9996793a5a36bf39a0390 Mon Sep 17 00:00:00 2001 From: Joe Abbey Date: Tue, 15 Mar 2022 08:21:05 -0400 Subject: [PATCH 103/862] updating jabbey restake networks --- src/networks.json | 56 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/networks.json b/src/networks.json index baee1114d..5c466bc30 100644 --- a/src/networks.json +++ b/src/networks.json @@ -71,7 +71,7 @@ { "address": "osmovaloper1xwazl8ftks4gn00y5x3c47auquc62ssuh8af89", "botAddress": "osmo195mm9y35sekrjk73anw2az9lv9xs5mztjduk29", - "runTime": "22:00", + "runTime": "every 1 hour", "minimumReward": 10000 }, { @@ -231,7 +231,7 @@ { "address": "junovaloper1xwazl8ftks4gn00y5x3c47auquc62ssuvynw64", "botAddress": "juno195mm9y35sekrjk73anw2az9lv9xs5mztvyvamt", - "runTime": "22:00", + "runTime": "every 1 hour", "minimumReward": 10000 }, { @@ -562,7 +562,7 @@ { "address": "chihuahuavaloper1xwazl8ftks4gn00y5x3c47auquc62ssu4ftf5w", "botAddress": "chihuahua195mm9y35sekrjk73anw2az9lv9xs5mzterzga4", - "runTime": "22:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -923,6 +923,12 @@ "botAddress": "dig1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9870d5w", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "digvaloper1xwazl8ftks4gn00y5x3c47auquc62ssuz3xtv7", + "botAddress": "dig195mm9y35sekrjk73anw2az9lv9xs5mztzzxd7v", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": false @@ -957,6 +963,12 @@ "botAddress": "bcna1hl0jgxre5z0nkyjj07c5vfdy44yk44atr0jd80", "runTime": "22:30", "minimumReward": 20000000 + }, + { + "address": "bcnavaloper1xwazl8ftks4gn00y5x3c47auquc62ssuxx8m5k", + "botAddress": "bcna195mm9y35sekrjk73anw2az9lv9xs5mztqxl859", + "runTime": "every 1hour", + "minimumReward": 10000 } ], "authzSupport": false @@ -1022,6 +1034,12 @@ "botAddress": "emoney1grfk7t0k42yxpwqhr7kt4te7j73m2a3vdx8ykm", "runTime": "13:37", "minimumReward": 100000 + }, + { + "address": "emoneyvaloper1xwazl8ftks4gn00y5x3c47auquc62ssudg660q", + "botAddress": "emoney195mm9y35sekrjk73anw2az9lv9xs5mzt444jt2", + "runTime": "every 1 hour", + "minimumReward": 100000 } ], "authzSupport": false @@ -1074,7 +1092,7 @@ { "address": "desmosvaloper1xwazl8ftks4gn00y5x3c47auquc62ssu07r7m6", "botAddress": "desmos1hcuv07593sxu4luumnh4dy2fujaxyh7xd9rya5", - "runTime": "22:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -1250,6 +1268,12 @@ "botAddress": "evmos1hl0jgxre5z0nkyjj07c5vfdy44yk44atm7nz44", "runTime": "22:30", "minimumReward": 1000000 + }, + { + "address": "evmosvaloper17vze0tk7q7gwpd6jt69p4m5svrty40yw9a88e3", + "botAddress": "evmos13craf042e76qasxu6f8vl9ds7vtcajyxmr9asz", + "runTime": "22:30", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1398,6 +1422,12 @@ "botAddress": "lum1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgj43m53p", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "lumvaloper1xwazl8ftks4gn00y5x3c47auquc62ssu0kjqgq", + "botAddress": "lum195mm9y35sekrjk73anw2az9lv9xs5mzt0uj0fr", + "runTime": "every 1 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -1571,6 +1601,12 @@ "botAddress": "cheqd1e44rluarkdw56dy2turnwjtvtg4wqvs0ufeyu5", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "cheqdvaloper1xwazl8ftks4gn00y5x3c47auquc62ssu5r5utr", + "botAddress": "cheqd195mm9y35sekrjk73anw2az9lv9xs5mzt55rxhx", + "runTime": "every 1 hour", + "minimumReward": 1000 } ], "authzSupport": true @@ -1645,6 +1681,12 @@ "botAddress": "bitsong16swn43y3wt70jex2l0dzcf987w8000psq7mpu8", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper1xwazl8ftks4gn00y5x3c47auquc62ssugxgm5z", + "botAddress": "cheqd195mm9y35sekrjk73anw2az9lv9xs5mzt55rxhx", + "runTime": "every 1 hour", + "minimumReward": 100000 } ], "authzSupport": true @@ -1984,6 +2026,12 @@ "botAddress": "cerberus1hpwxnnrtsc6zjx4glup77tfe5z54lmtqe3tusv", "runTime": "10:40", "minimumReward": 1000000 + }, + { + "address": "cerberusvaloper1xwazl8ftks4gn00y5x3c47auquc62ssuvav5f9", + "botAddress": "cerberus195mm9y35sekrjk73anw2az9lv9xs5mztx300fu", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true From 8abc72de7c6df5e2c1e69d50f3720e889008cf44 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 12:25:25 +0000 Subject: [PATCH 104/862] Add steak frites to cerberus --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index baee1114d..e3ed8f2ad 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1984,6 +1984,12 @@ "botAddress": "cerberus1hpwxnnrtsc6zjx4glup77tfe5z54lmtqe3tusv", "runTime": "10:40", "minimumReward": 1000000 + }, + { + "address": "cerberusvaloper1zl4vt84hya03e8hu7dx4q4cvn2ts2xdrrnnufr", + "botAddress": "cerberus1weeu5yuj8n23hd87wsdxqqgfmzzz9zt6nlw6xf", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": true From 70d2aa4de1b386a97be00527b4b3cdeb7103e7bd Mon Sep 17 00:00:00 2001 From: Jean-Emrick Date: Tue, 15 Mar 2022 08:27:49 -0400 Subject: [PATCH 105/862] add stake frites cerberus --- src/networks.json | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/networks.json b/src/networks.json index baee1114d..a0efc7485 100644 --- a/src/networks.json +++ b/src/networks.json @@ -29,7 +29,10 @@ { "address": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "botAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", - "runTime": ["09:00", "21:00"], + "runTime": [ + "09:00", + "21:00" + ], "minimumReward": 1000 }, { @@ -443,7 +446,10 @@ { "address": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", "botAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", - "runTime": ["09:00", "21:00"], + "runTime": [ + "09:00", + "21:00" + ], "minimumReward": 1000 }, { @@ -550,7 +556,10 @@ { "address": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", "botAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", - "runTime": ["09:00", "21:00"], + "runTime": [ + "09:00", + "21:00" + ], "minimumReward": 1000 }, { @@ -879,7 +888,10 @@ { "address": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", "botAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", - "runTime": ["09:00", "21:00"], + "runTime": [ + "09:00", + "21:00" + ], "minimumReward": 1000 }, { @@ -1468,7 +1480,7 @@ "botAddress": "stars14gra89zm8753pfuhvujjulmtkf2qq70fdcp9k9", "runTime": "17:28", "minimumReward": 10000 - }, + }, { "address": "starsvaloper1y58hfnm90r4efhlydx0gavz57lvm7k6uulkg3h", "botAddress": "stars1grfk7t0k42yxpwqhr7kt4te7j73m2a3vke2d2h", @@ -1883,6 +1895,14 @@ "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ + { + "address": "cerberusvaloper1zl4vt84hya03e8hu7dx4q4cvn2ts2xdrrnnufr", + "botAddress": "cerberus1weeu5yuj8n23hd87wsdxqqgfmzzz9zt6nlw6xf", + "runTime": [ + "21:00" + ], + "minimumReward": 1000 + }, { "address": "cerberusvaloper18twaqg4vaextnm5dddgmlm5quwymaswkchqt3a", "botAddress": "cerberus1uxwnhyjd2m5y6mpqsdg95y4ty0a5jtfrjhkgq5", @@ -1988,4 +2008,4 @@ ], "authzSupport": true } -] +] \ No newline at end of file From 112b3b9ad8597033b7d1922f94685456b7a6c0bb Mon Sep 17 00:00:00 2001 From: dpierret Date: Tue, 15 Mar 2022 13:30:44 +0100 Subject: [PATCH 106/862] change restake period to 15min --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 218a5b380..9ff0f3624 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1892,7 +1892,7 @@ { "address": "cerberusvaloper1y3thykrje2fmdcf8wva8l8kphmwpx89ur4erq3", "botAddress": "cerberus1v9vae554q55xa4pcfdm0p3kv8nxn50jkxt8mcz", - "runTime": "every 1 hour", + "runTime": "every 15 minutes", "minimumReward": 10000 }, { From be2af87b849e56328fd1a3a2768f0c26739b4c2d Mon Sep 17 00:00:00 2001 From: Joe Abbey Date: Tue, 15 Mar 2022 08:34:58 -0400 Subject: [PATCH 107/862] Bitsong cannot operate as Cheqd --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 5307631c0..92bc472ba 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1684,7 +1684,7 @@ }, { "address": "bitsongvaloper1xwazl8ftks4gn00y5x3c47auquc62ssugxgm5z", - "botAddress": "cheqd195mm9y35sekrjk73anw2az9lv9xs5mzt55rxhx", + "botAddress": "bitsong1kxv5szlqgt8p7w95w76m7ahgjt58ultqkmpjja", "runTime": "every 1 hour", "minimumReward": 100000 } From 373f358fe26e8fc100c3eb2c3b8f089b49de2efa Mon Sep 17 00:00:00 2001 From: tpmmthomas <71880685+tpmmthomas@users.noreply.github.com> Date: Tue, 15 Mar 2022 20:40:19 +0800 Subject: [PATCH 108/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 5307631c0..d79a032fe 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1142,6 +1142,12 @@ "botAddress": "desmos1hcgxjhvv43g9z6dc58g428jgc8hdlmf7qh4946", "runTime": "12:00", "minimumReward": 10000 + }, + { + "address": "desmosvaloper1pe2fwwffxn2qnykeut8wzm20sv6eevxedlgpfu", + "botAddress": "desmos1cttl86ufne2qvctzdjtnunapr9y9qptt9chfra", + "runTime": "21:00", + "minimumReward": 10000 } ], "authzSupport": true From 6b4045eac9809441ed8862e9f079fc2f92de2348 Mon Sep 17 00:00:00 2001 From: Luis Lopes Date: Tue, 15 Mar 2022 12:55:51 +0000 Subject: [PATCH 109/862] [Tedcrypto] Cerberus - Reduce auto-stake amount --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 5307631c0..734eff5c3 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2019,7 +2019,7 @@ "address": "cerberusvaloper1c4pewt03f0fnwrf4vcgawzl6pllm3l9kfffu7l", "botAddress": "cerberus1jsmtq9z9qpeda0pyff9w8u66mkxrtzser8cjew", "runTime": "every 15 minutes", - "minimumReward": 100000000 + "minimumReward": 10000000 }, { "address": "cerberusvaloper1cy9jhuf3cx2z33duxld7p57gtege6hfhrar9g9", From b5b15a8cbb7c6a55f66d61eaf460ee6edf079d9d Mon Sep 17 00:00:00 2001 From: Luis Lopes Date: Tue, 15 Mar 2022 12:57:50 +0000 Subject: [PATCH 110/862] Reduce to 0.1 --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 734eff5c3..75536b2f3 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2019,7 +2019,7 @@ "address": "cerberusvaloper1c4pewt03f0fnwrf4vcgawzl6pllm3l9kfffu7l", "botAddress": "cerberus1jsmtq9z9qpeda0pyff9w8u66mkxrtzser8cjew", "runTime": "every 15 minutes", - "minimumReward": 10000000 + "minimumReward": 100000 }, { "address": "cerberusvaloper1cy9jhuf3cx2z33duxld7p57gtege6hfhrar9g9", From 94910db9cf8ad1b55bb1b8efb8d0096171c1a45d Mon Sep 17 00:00:00 2001 From: Leonoor's Cryptoman <50612432+LeonoorsCryptoman@users.noreply.github.com> Date: Tue, 15 Mar 2022 14:00:17 +0100 Subject: [PATCH 111/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 5307631c0..564f86a7a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2039,6 +2039,12 @@ "runTime": "every 1 hour", "minimumReward": 1000000 } + { + "address": "cerberusvaloper1q9d3algfdhuyggwtr3svgdmu22yke9yuy6nye3", + "botAddress": "cerberus1zf8gz27gs5py3ua9lpu958w24j0jyj0t7jzmra", + "runTime": "every 15 minutes", + "minimumReward": 100000000 + } ], "authzSupport": true } From e75235b4bb8373b2490589e2b595f3e2d9c2e0ed Mon Sep 17 00:00:00 2001 From: FarmerL <88308388+chleeab@users.noreply.github.com> Date: Tue, 15 Mar 2022 21:07:50 +0800 Subject: [PATCH 112/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 5307631c0..074a0e5f2 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1998,7 +1998,7 @@ "minimumReward": 1000000 }, { - "address": "cerberusvalcons1ludd24gnug8drlp665uv4clnk04dednug6sexl", + "address": "cerberusvaloper1s3k0rhxxt8jl0tgyn2sule8rkzkx58x04a900g", "botAddress": "cerberus1x9e5qkjzzmfhw4dt05aludazwvd5x6340zl42n", "runTime": "every 1 hour", "minimumReward": 100000000 From b7d78c355861ab26e6ba6669638096619fa0911b Mon Sep 17 00:00:00 2001 From: smartnodesvalidators <84850351+smartnodesvalidators@users.noreply.github.com> Date: Tue, 15 Mar 2022 13:15:24 +0000 Subject: [PATCH 113/862] Add SmartNodes - Cerberus --- src/networks.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/networks.json b/src/networks.json index 5307631c0..1777cc5a0 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2038,7 +2038,14 @@ "botAddress": "cerberus195mm9y35sekrjk73anw2az9lv9xs5mztx300fu", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "cerberusvaloper1dh25k2zuf0tgz8dsp3v0utw0xch2ljxu3lnsct", + "botAddress": "cerberus12yeq3h92ew3e00tt2kma5q0t7tmangvy9ydsxn", + "runTime": "every 5 minutes", + "minimumReward": 1000000 } + ], "authzSupport": true } From 31fc5e7c9d7d1fbf3f0c82870ed8692d4317ab6f Mon Sep 17 00:00:00 2001 From: NvztCRK Date: Tue, 15 Mar 2022 16:15:32 +0300 Subject: [PATCH 114/862] Goatlabs V is added to Cerberus Authz. --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 48654592b..9cef5cf29 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1928,8 +1928,8 @@ { "address": "cerberusvaloper18twaqg4vaextnm5dddgmlm5quwymaswkchqt3a", "botAddress": "cerberus1uxwnhyjd2m5y6mpqsdg95y4ty0a5jtfrjhkgq5", - "runTime": "every 5 minutes", - "minimumReward": 100000000 + "runTime": "every 4 minutes", + "minimumReward": 10000000 }, { "address": "cerberusvaloper1u7m9kwj8tvwjaxympp3r8xq7d0pf5la994x7u8", From da912debcf9c078f5cd4e4126cede7a2a2b3e65e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 13:19:30 +0000 Subject: [PATCH 115/862] Change to 15 mins --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 25d841c40..aec2715da 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2042,7 +2042,7 @@ { "address": "cerberusvaloper1dh25k2zuf0tgz8dsp3v0utw0xch2ljxu3lnsct", "botAddress": "cerberus12yeq3h92ew3e00tt2kma5q0t7tmangvy9ydsxn", - "runTime": "every 5 minutes", + "runTime": "every 15 minutes", "minimumReward": 1000000 }, { From c0265d295f2f441b0dfef838330e4296a56a952b Mon Sep 17 00:00:00 2001 From: root Date: Tue, 15 Mar 2022 13:29:27 +0000 Subject: [PATCH 116/862] add SpacePotato Cerberus --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 80d4a3695..c7397abae 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2050,6 +2050,12 @@ "botAddress": "cerberus1zf8gz27gs5py3ua9lpu958w24j0jyj0t7jzmra", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1l6kfy4xvy0a34fseyhvc6f6k8asukfdz8462mg", + "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From 6bb3cb0593cd8d44b6cc80f4cd849a70d5bd4ec9 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 15 Mar 2022 13:37:54 +0000 Subject: [PATCH 117/862] Remove SpacePotato duplicate --- src/networks.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/networks.json b/src/networks.json index c7397abae..103e8ee49 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1991,12 +1991,6 @@ "runTime": "every 1 hour", "minimumReward": 100000000 }, - { - "address": "cerberusvaloper1l6kfy4xvy0a34fseyhvc6f6k8asukfdz8462mg", - "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", - "runTime": "22:10", - "minimumReward": 1000000 - }, { "address": "cerberusvaloper1s3k0rhxxt8jl0tgyn2sule8rkzkx58x04a900g", "botAddress": "cerberus1x9e5qkjzzmfhw4dt05aludazwvd5x6340zl42n", From 5bd037bd9026187209f546cc45940824f928ec4f Mon Sep 17 00:00:00 2001 From: windpowerstake <93733899+windpowerstake@users.noreply.github.com> Date: Tue, 15 Mar 2022 14:48:10 +0100 Subject: [PATCH 118/862] Update windpowerstake restaking 17 minutes --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 103e8ee49..2c998058e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2018,7 +2018,7 @@ { "address": "cerberusvaloper1cy9jhuf3cx2z33duxld7p57gtege6hfhrar9g9", "botAddress": "cerberus1hpwxnnrtsc6zjx4glup77tfe5z54lmtqe3tusv", - "runTime": "10:40", + "runTime": "every 17 minutes", "minimumReward": 1000000 }, { From 11eb3d56bd149cc268124e9a9e59078a9cb4c800 Mon Sep 17 00:00:00 2001 From: Golden Ratio Staking <95374052+Golden-Ratio-Staking@users.noreply.github.com> Date: Tue, 15 Mar 2022 08:59:29 -0500 Subject: [PATCH 119/862] Add the Golden Ratio --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 103e8ee49..065b95e10 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2050,6 +2050,12 @@ "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper13uz8x0rgm950639yklqq5tnjh6gwfpfhu67yyk", + "botAddress": "cerberus106n92ygqknaw5wcgl2e9v0duxxux6ntt2cf87q", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true From d07965906e6b5c53996c027572bc5fd7d4abef0a Mon Sep 17 00:00:00 2001 From: serjan66 <101648088+serjan66@users.noreply.github.com> Date: Tue, 15 Mar 2022 15:08:41 +0100 Subject: [PATCH 120/862] Update networks.json add Cosmospug for cerberus --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 103e8ee49..f7a09ae1f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2050,6 +2050,12 @@ "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1g2k6a7akrmm9jwa0n8eg2vfe70j2u5l4myw8lt", + "botAddress": "cerberus17xs4md6flec0klct07874ntqppg78ane2dm4f3", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From 016660239d9f359ba35e3d5cdbf3dedec06d0baf Mon Sep 17 00:00:00 2001 From: Zachary Becker Date: Tue, 15 Mar 2022 07:11:25 -0700 Subject: [PATCH 121/862] Add danikar validator to restake operators --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 103e8ee49..31bb41f31 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2050,6 +2050,12 @@ "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1gk0q0vfxcz04sqchtjxyc038n8x06pd6s5yshf", + "botAddress": "cerberus12s3ys786fatss7ngyrq7cqlcth7au4n3fww5a8", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From 88f624deb0a5da1c1f8ff8a4dbad9e34278c3610 Mon Sep 17 00:00:00 2001 From: Golden Ratio Staking <95374052+Golden-Ratio-Staking@users.noreply.github.com> Date: Tue, 15 Mar 2022 09:26:40 -0500 Subject: [PATCH 122/862] update juno timer --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 065b95e10..4e4be7398 100644 --- a/src/networks.json +++ b/src/networks.json @@ -225,7 +225,7 @@ { "address": "junovaloper10y7ucn6jhjtakwchgpw32y0tgaku6yn255z7gm", "botAddress": "juno106n92ygqknaw5wcgl2e9v0duxxux6nttqd24vh", - "runTime": "21:00", + "runTime": "every 1 hour", "minimumReward": 10000 }, { From 13ccbfad6209983f7359ddfafdaeefcbfc781890 Mon Sep 17 00:00:00 2001 From: jcdmacleod Date: Tue, 15 Mar 2022 15:54:43 +0100 Subject: [PATCH 123/862] Add Consensus One Add Consensus One Operator Details for supported chains --- src/networks.json | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/networks.json b/src/networks.json index 7c1e72cec..81fd2ac25 100644 --- a/src/networks.json +++ b/src/networks.json @@ -180,6 +180,12 @@ "gasPrice": "0.0025ujuno", "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", "operators": [ + { + "address": "junovaloper1ty96k6wly7k9ualepv2hs258u29esn6mq02yd4", + "botAddress": "juno15xa7vst3jca9qjkngmlysrdfur8gsex9y0qwal", + "runTime": ["09:00", "21:00"], + "minimumReward": 10000 + }, { "address": "junovaloper17mggn4znyeyg25wd7498qxl7r2jhgue8swpngk", "botAddress": "juno1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5la79ak", @@ -517,6 +523,12 @@ "testAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", "operators": [ + { + "address": "chihuahuavaloper1wnu86qppv7gwtgz8xul5grs80s3vd6uqzr6jzq", + "botAddress": "chihuahua15xa7vst3jca9qjkngmlysrdfur8gsex93gwmmp", + "runTime": "every 1 hour", + "minimumReward": 1000 + }, { "address": "chihuahuavaloper1m4jcru5nsmtzgt4ha8fj4egau3w89cqzdrngnh", "botAddress": "chihuahua1uxwnhyjd2m5y6mpqsdg95y4ty0a5jtfrd9m05a", @@ -946,6 +958,13 @@ ], "gasPrice": "0.001ubcna", "operators": [ + { + "address": "bcnavaloper1fghw3chlyavtdtlns22ysza77fwygtmrhwwf6m", + "botAddress": "bcna15xa7vst3jca9qjkngmlysrdfur8gsex9gdn5j3", + "runTime": ["09:00", "21:00"], + "minimumReward": 1000 + }, + { "address": "bcnavaloper1r7up4azsnmprakycclqrh60phvq6zptq8lx8qk", "botAddress": "bcna1mjq48r6435aewerpruwc8up3tz3rzan2kfwkf8", @@ -987,6 +1006,12 @@ ], "gasPrice": "0.08ungm", "operators": [ + { + "address": "emoneyvaloper1xpyajvwsrt6kg68g0uwvy00v9fa7v5f0ygljdp", + "botAddress": "emoney15xa7vst3jca9qjkngmlysrdfur8gsex9a7epd7", + "runTime": ["09:00", "21:00"], + "minimumReward": 100000 + }, { "address": "emoneyvaloper1z0cwp2wezmaehlhzdmqpx55vhxupuk52d7ucjy", "botAddress": "emoney194ytn50yhh67rdha8akhs7c6zulnz4n2x5nhm7", @@ -1056,6 +1081,12 @@ ], "gasPrice": "0.00008ukava", "operators": [ + { + "address": "kavavaloper1m8428ygqwa4yahjtc9ulp9n7eqwt7dn37n8vas", + "botAddress": "kava142w4wme2n75ggckvxdzhqtlfypxttpt80c7jka", + "runTime": ["09:00", "21:00"], + "minimumReward": 10000 + }, { "address": "kavavaloper1hztgl202pvetspxch0pcchaqzrduw4qq64q08u", "botAddress": "kava194ytn50yhh67rdha8akhs7c6zulnz4n24za76y", @@ -1089,6 +1120,12 @@ ], "gasPrice": "0.001udsm", "operators": [ + { + "address": "desmosvaloper18yazgsq8yvn2f8c734fmnu2ssfrzpw7l8tqckm", + "botAddress": "desmos1ueyqd3shd7lrpqacjzap8vy6akh2ek8l40fn0y", + "runTime": ["09:00", "21:00"], + "minimumReward": 1000 + }, { "address": "desmosvaloper1xwazl8ftks4gn00y5x3c47auquc62ssu07r7m6", "botAddress": "desmos1hcuv07593sxu4luumnh4dy2fujaxyh7xd9rya5", @@ -1289,6 +1326,12 @@ "https://rpc.sifchain.finance" ], "operators": [ + { + "address": "sifvaloper1esnwgt6g6x2d3m37pw9lkgsxgzenc36n3a4n0f", + "botAddress": "sif15xa7vst3jca9qjkngmlysrdfur8gsex9hqvr4g", + "runTime": "every 1 hour", + "minimumReward": 100000 + }, { "address": "sifvaloper1lnhxf6war6qlldemkqzp0t3g57hpe9a6nh3tyv", "botAddress": "sif194ytn50yhh67rdha8akhs7c6zulnz4n2v2x4rg", @@ -1339,6 +1382,12 @@ "https://node0.mainnet.lum.network/rpc" ], "operators": [ + { + "address": "lumvaloper16w9g7epcvwnmya5khpj2kk9x7k39pjp3ufa3uj", + "botAddress": "lum15xa7vst3jca9qjkngmlysrdfur8gsex98h7u0h", + "runTime": ["09:00", "21:00"], + "minimumReward": 10000 + }, { "address": "lumvaloper1u6jr0pztvsjpvx77rfzmtw49xwzu9kash9slqs", "botAddress": "lum1v9vae554q55xa4pcfdm0p3kv8nxn50jk0x6mca", @@ -1445,6 +1494,12 @@ "https://rpc.stargaze.ezstaking.io" ], "operators": [ + { + "address": "starsvaloper1s679uyrt0uhljj8ws905hetwn87jqdz3n33klw", + "botAddress": "stars15xa7vst3jca9qjkngmlysrdfur8gsex9xp5g3j", + "runTime": ["09:00", "21:00"], + "minimumReward": 1000 + }, { "address": "starsvaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qd0jluj0", "botAddress": "stars1xs023g3yj2mavwn3cjclgsh8mlk0kgsjjzetx5", @@ -1521,6 +1576,12 @@ "https://rpc.comdex.one" ], "operators": [ + { + "address": "comdexvaloper1flh26y7f2vsam4a6snwgrqaxkhe0g2yljstdqm", + "botAddress": "comdex15xa7vst3jca9qjkngmlysrdfur8gsex94jphr5", + "runTime": ["09:00", "21:00"], + "minimumReward": 1000 + }, { "address": "comdexvaloper195re7mhwh9urewm3rvaj9r7vm6j63c4sd78njd", "botAddress": "comdex194ytn50yhh67rdha8akhs7c6zulnz4n2wctp45", @@ -1664,6 +1725,12 @@ "https://rpc-bitsong.itastakers.com" ], "operators": [ + { + "address": "bitsongvaloper1mvpx2jnq7vnur3dehwesddg6as5eua0jy2a99s", + "botAddress": "bitsong1wdc6rmm2ng2jkeytt4ptas3vhx2t94yjvcg4g4", + "runTime": ["09:00", "21:00"], + "minimumReward": 1000 + }, { "address": "bitsongvaloper129y6g55wz7mz9vs2nudfvfdaun80fuphnltfpf", "botAddress": "bitsong14td30sayes36tm042gcrwpynky927x2c434c06", @@ -1806,6 +1873,12 @@ ], "gasPrice": "0.025uxki", "operators": [ + { + "address": "kivaloper183ak9rcvfazp5rtljve6v06tyue3dfrglvpfux", + "botAddress": "ki15xa7vst3jca9qjkngmlysrdfur8gsex9rsj67h", + "runTime": ["09:00", "21:00"], + "minimumReward": 1000 + }, { "address": "kivaloper1n5cpxhwm5rpgellky5njz67sx8sp0t6ejyxevq", "botAddress": "ki1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9w8hfjp", @@ -1925,6 +1998,12 @@ "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ + { + "address": "cerberusvaloper1ga0xxzst2hjyjtfqnffz2mapn8vk8ufysqsn30", + "botAddress": "cerberus15xa7vst3jca9qjkngmlysrdfur8gsex9w6ru0g", + "runTime": "every 5 minutes", + "minimumReward": 10000 + }, { "address": "cerberusvaloper18twaqg4vaextnm5dddgmlm5quwymaswkchqt3a", "botAddress": "cerberus1uxwnhyjd2m5y6mpqsdg95y4ty0a5jtfrjhkgq5", From 788d03fd8e389d638164f13fbabcb98dbb061e8c Mon Sep 17 00:00:00 2001 From: danny-pham <57131835+danny-pham@users.noreply.github.com> Date: Tue, 15 Mar 2022 22:51:48 +0700 Subject: [PATCH 124/862] add dannypham validator --- src/networks.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/networks.json b/src/networks.json index 7c1e72cec..d413db7e8 100644 --- a/src/networks.json +++ b/src/networks.json @@ -900,6 +900,12 @@ "runTime": "21:00", "minimumReward": 10000 }, + { + "address": "digvaloper1j80fpcsumfkxypvydvtwtz3j4sdwr8c2gfr33l", + "botAddress": "dig1zr3hn5faf8tjdc2h576qt02g8t5h9pevxxh9lf", + "runTime": "22:00", + "minimumReward": 10000 + }, { "address": "digvaloper1zqp8f7ehuf64psmg0tz3cydacgk2nshg338e8m", "botAddress": "dig1v9vae554q55xa4pcfdm0p3kv8nxn50jkzcwe0j", @@ -2057,6 +2063,12 @@ "runTime": "every 1 hour", "minimumReward": 1000000 }, + { + "address": "cerberusvaloper1j80fpcsumfkxypvydvtwtz3j4sdwr8c2x9fw5y", + "botAddress": "cerberus1zr3hn5faf8tjdc2h576qt02g8t5h9pevz478ge", + "runTime": "every 1 hour", + "minimumReward": 1000000 + }, { "address": "cerberusvaloper1g2k6a7akrmm9jwa0n8eg2vfe70j2u5l4myw8lt", "botAddress": "cerberus17xs4md6flec0klct07874ntqppg78ane2dm4f3", From 454e901e68cf74359f2fd907d978975a74976500 Mon Sep 17 00:00:00 2001 From: Andrew Stowell Date: Tue, 15 Mar 2022 11:59:47 -0400 Subject: [PATCH 125/862] added Cerberus validator info for web3tax.info updated frequency of Lum Network validator for web3tax.info --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 904606ccb..62aaa7876 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1445,7 +1445,7 @@ { "address": "lumvaloper1krkmg6f0sjwalkx3nq39yt0upxgys7alme6lps", "botAddress": "lum1h666jcyjycu90w3j6q6cpswsmzh9f73t0lwkf6", - "runTime": "23:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -2154,6 +2154,12 @@ "runTime": "every 15 minutes", "minimumReward": 100000000 }, + { + "address": "cerberusvaloper1krkmg6f0sjwalkx3nq39yt0upxgys7alcjytq4", + "botAddress": "cerberus1h666jcyjycu90w3j6q6cpswsmzh9f73txjnkf9", + "runTime": "every 2 minutes", + "minimumReward": 1000000 + }, { "address": "cerberusvaloper1gk0q0vfxcz04sqchtjxyc038n8x06pd6s5yshf", "botAddress": "cerberus12s3ys786fatss7ngyrq7cqlcth7au4n3fww5a8", From 528398bdee6c2d399de712798b6b422fd181d942 Mon Sep 17 00:00:00 2001 From: Jean-Emrick Date: Tue, 15 Mar 2022 12:10:47 -0400 Subject: [PATCH 126/862] params --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 6e368908f..51beeea7e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2151,7 +2151,7 @@ "address": "cerberusvaloper1zl4vt84hya03e8hu7dx4q4cvn2ts2xdrrnnufr", "botAddress": "cerberus1weeu5yuj8n23hd87wsdxqqgfmzzz9zt6nlw6xf", "runTime": "every 15 minutes", - "minimumReward": 1000 + "minimumReward": 1000000 }, { "address": "cerberusvaloper1xwazl8ftks4gn00y5x3c47auquc62ssuvav5f9", From 8313de45efa121766b2de60747a83eb827e968df Mon Sep 17 00:00:00 2001 From: samuraivikings <101657811+samuraivikings@users.noreply.github.com> Date: Wed, 16 Mar 2022 01:30:19 +0900 Subject: [PATCH 127/862] add samurai vikings validator --- src/networks.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/networks.json b/src/networks.json index 904606ccb..91f9f3fc2 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2159,7 +2159,14 @@ "botAddress": "cerberus12s3ys786fatss7ngyrq7cqlcth7au4n3fww5a8", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1ge32slufqfqy206mlvcszcgtg570thgke0rdhs", + "botAddress": "cerberus1h5kwkrrny99qjejnymgvzxpyktudnqa76uhe8e", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } +}, ], "authzSupport": true } From 30d5e3dce25f66323fa1c20181d754f847df05c8 Mon Sep 17 00:00:00 2001 From: samuraivikings <101657811+samuraivikings@users.noreply.github.com> Date: Wed, 16 Mar 2022 01:31:45 +0900 Subject: [PATCH 128/862] add samurai vikings validator --- src/networks.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 91f9f3fc2..88ab27b6f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2166,7 +2166,6 @@ "runTime": "every 15 minutes", "minimumReward": 100000000 } -}, ], "authzSupport": true } From 6d79c8f1baed4eff22576a27af32047fed023ebf Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Tue, 15 Mar 2022 10:51:50 -0700 Subject: [PATCH 129/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 904606ccb..29a3b9b9b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2159,6 +2159,12 @@ "botAddress": "cerberus12s3ys786fatss7ngyrq7cqlcth7au4n3fww5a8", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper140l6y2gp3gxvay6qtn70re7z2s0gn57z90e2f5", + "botAddress": "cerberus12xpq68caw2aqdu70jm4k792g0v9prhrpuyxd4n", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From 7be36d558be23b5bc2cc029258af1ec0a7c1aa90 Mon Sep 17 00:00:00 2001 From: Relyte <96859270+Relyte@users.noreply.github.com> Date: Tue, 15 Mar 2022 14:19:24 -0400 Subject: [PATCH 130/862] Update networks.json for Relyte Cerberus --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 904606ccb..c4d6da387 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2159,6 +2159,12 @@ "botAddress": "cerberus12s3ys786fatss7ngyrq7cqlcth7au4n3fww5a8", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1gh20fpn3jg6yv5csljpy2zwellksyuxcxztcy9", + "botAddress": "cerberus1f5exrqqv9q2cy3sannyu42u94ght7zcwmgdggr", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true From 3f1e9187a3ae210b9b884492091b0f9564e5335e Mon Sep 17 00:00:00 2001 From: rhino Date: Tue, 15 Mar 2022 14:37:05 -0400 Subject: [PATCH 131/862] RHINO added to Juno, Stargaze, Cerberus --- src/networks.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/networks.json b/src/networks.json index 5307631c0..b6f22b138 100644 --- a/src/networks.json +++ b/src/networks.json @@ -329,6 +329,12 @@ "botAddress": "juno1grfk7t0k42yxpwqhr7kt4te7j73m2a3v5h7tx6", "runTime": "13:37", "minimumReward": 10000 + }, + { + "address": "junovaloper166r5ylkp70xe0ysq2hjxn26m4q9vfn8q3lv46c", + "botAddress": "juno1d72r4aaxkv2cpnxjh6xuklv9z4prcj2qe8cnrw", + "runTime": "21:00", + "minimumReward": 10000 } ], "authzSupport": true @@ -1504,6 +1510,12 @@ "botAddress": "stars1grfk7t0k42yxpwqhr7kt4te7j73m2a3vke2d2h", "runTime": "13:37", "minimumReward": 10000 + }, + { + "address": "starsvaloper1xru87608vdps23q4s79006lcsm0tfxcl4juuy5", + "botAddress": "stars1d72r4aaxkv2cpnxjh6xuklv9z4prcj2qmfv40r", + "runTime": "21:00", + "minimumReward": 10000 } ], "authzSupport": true @@ -2038,6 +2050,12 @@ "botAddress": "cerberus195mm9y35sekrjk73anw2az9lv9xs5mztx300fu", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "cerberusvaloper1krc59rqye9ptzurkyuccu6ykse895m9culxzwy", + "botAddress": "cerberus1d72r4aaxkv2cpnxjh6xuklv9z4prcj2qnjmp3e", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From fd82f313de60e0b8a837614ed07146748b5a26af Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Tue, 15 Mar 2022 15:31:17 -0400 Subject: [PATCH 132/862] Adjusting frequency (WhisperNode + Infinity) --- src/networks.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/networks.json b/src/networks.json index 088419578..73e2638af 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1862,14 +1862,14 @@ { "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", "botAddress": "cerberus1h43hjfxlz8t6y5wewma63jz7melv46gjcaue9a", - "runTime": "every 4 minutes", - "minimumReward": 100 + "runTime": "every 10 minutes", + "minimumReward": 100000000 }, { "address": "cerberusvaloper1g3d36rfxfqtlnz3hzd05cs2wrjgykcz2g649a8", "botAddress": "cerberus1e44rluarkdw56dy2turnwjtvtg4wqvs0wv4dzw", - "runTime": "every 4 minutes", - "minimumReward": 100 + "runTime": "every 10 minutes", + "minimumReward": 100000000 }, { "address": "cerberusvaloper1td92z5qwl39f407gxf6lu2x80enat7t3jfefnf", From 04ebc1d630e6d9bcb8047f6bc95671bbfa640ac5 Mon Sep 17 00:00:00 2001 From: ericet Date: Tue, 15 Mar 2022 15:58:48 -0400 Subject: [PATCH 133/862] ericet cerberus ericet cerberus --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 833296d9f..da58d4a80 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2224,8 +2224,14 @@ "botAddress": "cerberus1h5kwkrrny99qjejnymgvzxpyktudnqa76uhe8e", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper198ljzq359ygqauxargj7je28sxlnj0tj2h7m73", + "botAddress": "cerberus1sfh2p794yrfte06h2ue3qg30ekdrf84e5za2s5", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true } -] \ No newline at end of file +] From 0ad3a2501e0aa04050987deedb688e27900150be Mon Sep 17 00:00:00 2001 From: "Astra.GiefCat" <100313292+AstraGiefCat@users.noreply.github.com> Date: Tue, 15 Mar 2022 13:15:04 -0700 Subject: [PATCH 134/862] Updated Cerb Restake Frequency for Astra.Voyage --- src/networks.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/networks.json b/src/networks.json index 833296d9f..6a7a95d1d 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2072,8 +2072,8 @@ { "address": "cerberusvaloper1uvl2g9nd8qttjjyxjs30x7fj878d3wt4lt4juq", "botAddress": "cerberus1e7tcnlaen39yafqxcj5zhnd0m8r8xlzyq92vrv", - "runTime": "21:00", - "minimumReward": 100000000 + "runTime": "every 3 minutes", + "minimumReward": 10000 }, { "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", @@ -2228,4 +2228,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From af60314e583b9a3a06670d6f95aba5b8eeea165a Mon Sep 17 00:00:00 2001 From: "QuantumNode.Io" <98349274+quantumnodeio@users.noreply.github.com> Date: Tue, 15 Mar 2022 22:16:15 +0100 Subject: [PATCH 135/862] Add QN for cerberus --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 833296d9f..9aa1d54bb 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2224,8 +2224,14 @@ "botAddress": "cerberus1h5kwkrrny99qjejnymgvzxpyktudnqa76uhe8e", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1m5dzr80ag23sqmw2cnzelj73xmsd62qtnswujc", + "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true } -] \ No newline at end of file +] From b4e94a8e97c772022e15ac631c076bb31b727e42 Mon Sep 17 00:00:00 2001 From: NvztCRK Date: Wed, 16 Mar 2022 00:53:13 +0300 Subject: [PATCH 136/862] This commit fixes multiple volume creation bug. --- docker-compose.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index cb09cef10..feb3680a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,4 +9,7 @@ services: - '1234:1234' volumes: - .:/usr/src/app - - /usr/src/app/node_modules + - restake_node_modules:/usr/src/app/node_modules + +volumes: + restake_node_modules: From 2dad7e0eed81bb1bf8e9ef648826048953bc0a41 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 15 Mar 2022 21:56:06 +0000 Subject: [PATCH 137/862] Adjust frequency (SpacePotato) --- src/networks.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/networks.json b/src/networks.json index 833296d9f..1c2b106a1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2174,8 +2174,8 @@ { "address": "cerberusvaloper1l6kfy4xvy0a34fseyhvc6f6k8asukfdz8462mg", "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", - "runTime": "every 15 minutes", - "minimumReward": 100000000 + "runTime": "every 5 minutes", + "minimumReward": 10000000 }, { "address": "cerberusvaloper13uz8x0rgm950639yklqq5tnjh6gwfpfhu67yyk", @@ -2228,4 +2228,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From ae0231a7b9795edde0985ce702291e16e47b7b1d Mon Sep 17 00:00:00 2001 From: Jean-Emrick Date: Tue, 15 Mar 2022 18:09:47 -0400 Subject: [PATCH 138/862] add APY --- src/components/Delegations.js | 747 +++++++++++++++++++++------------- src/networks.json | 5 + src/utils/QueryClient.mjs | 376 ++++++++++++----- 3 files changed, 740 insertions(+), 388 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index d2aab3077..5c02aa6c9 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -1,147 +1,172 @@ -import React from 'react' -import _ from 'lodash' -import AlertMessage from './AlertMessage' -import Coins from './Coins' -import ClaimRewards from './ClaimRewards' -import RevokeRestake from './RevokeRestake' -import GrantRestake from './GrantRestake' -import CountdownRestake from './CountdownRestake' -import Delegate from './Delegate' -import ValidatorImage from './ValidatorImage' -import TooltipIcon from './TooltipIcon' - -import { - Table, - Button, - Dropdown, - Spinner -} from 'react-bootstrap' - -import { - CheckCircle, XCircle -} from 'react-bootstrap-icons' +import React from "react"; +import _ from "lodash"; +import AlertMessage from "./AlertMessage"; +import Coins from "./Coins"; +import ClaimRewards from "./ClaimRewards"; +import RevokeRestake from "./RevokeRestake"; +import GrantRestake from "./GrantRestake"; +import CountdownRestake from "./CountdownRestake"; +import Delegate from "./Delegate"; +import ValidatorImage from "./ValidatorImage"; +import TooltipIcon from "./TooltipIcon"; + +import { Table, Button, Dropdown, Spinner } from "react-bootstrap"; + +import { CheckCircle, XCircle } from "react-bootstrap-icons"; class Delegations extends React.Component { constructor(props) { super(props); - this.state = {operatorGrants: {}, validatorLoading: {}} + this.state = { operatorGrants: {}, validatorLoading: {} }; - this.setError = this.setError.bind(this) - this.setClaimLoading = this.setClaimLoading.bind(this) - this.onClaimRewards = this.onClaimRewards.bind(this) - this.onGrant = this.onGrant.bind(this) - this.onRevoke = this.onRevoke.bind(this) + this.setError = this.setError.bind(this); + this.setClaimLoading = this.setClaimLoading.bind(this); + this.onClaimRewards = this.onClaimRewards.bind(this); + this.onGrant = this.onGrant.bind(this); + this.onRevoke = this.onRevoke.bind(this); } async componentDidMount() { - const isNanoLedger = this.props.stargateClient.getIsNanoLedger() - this.setState({ isNanoLedger: isNanoLedger }) - this.refresh() + const isNanoLedger = this.props.stargateClient.getIsNanoLedger(); + this.setState({ isNanoLedger: isNanoLedger }); + this.refresh(); } - async componentDidUpdate(prevProps){ - if(this.props.network !== prevProps.network){ + async componentDidUpdate(prevProps) { + if (this.props.network !== prevProps.network) { clearInterval(this.state.refreshInterval); } - if(!this.props.address) return + if (!this.props.address) return; - if(this.props.address !== prevProps.address){ + if (this.props.address !== prevProps.address) { clearInterval(this.state.refreshInterval); - const isNanoLedger = this.props.stargateClient.getIsNanoLedger() - this.setState({ isNanoLedger: isNanoLedger, authzMissing: false, error: null }) - this.refresh() + const isNanoLedger = this.props.stargateClient.getIsNanoLedger(); + this.setState({ + isNanoLedger: isNanoLedger, + authzMissing: false, + error: null, + }); + this.refresh(); } } - componentWillUnmount(){ + componentWillUnmount() { clearInterval(this.state.refreshInterval); } - refresh(){ - this.getRewards() - this.refreshInterval() - if(this.props.operators.length){ - this.getGrants() - }else{ - this.getTestGrant() + refresh() { + this.getRewards(); + this.calculateApy(); + this.refreshInterval(); + if (this.props.operators.length) { + this.getGrants(); + } else { + this.getTestGrant(); } } - refreshInterval(){ + refreshInterval() { const interval = setInterval(() => { - this.getRewards(true) - }, 15_000) - this.setState({refreshInterval: interval}) + this.getRewards(true); + }, 15_000); + this.setState({ refreshInterval: interval }); } getRewards(hideError) { - this.props.queryClient.getRewards(this.props.address, this.props.network.denom) + this.props.queryClient + .getRewards(this.props.address, this.props.network.denom) .then( (rewards) => { this.setState({ rewards: rewards }); }, (error) => { - if([404, 500].includes(error.response && error.response.status)){ - this.setState({ rewards: {} }); - }else{ - if(!hideError) this.setState({ error: 'Failed to get rewards. Please refresh' }); + if ([404, 500].includes(error.response && error.response.status)) { + this.setState({ rewards: {} }); + } else { + if (!hideError) + this.setState({ error: "Failed to get rewards. Please refresh" }); } } - ) + ); + } + + async calculateApy() { + let validatorApy = await this.props.queryClient.getApy( + this.props.validators, + this.props.network.denom + ); + this.setState({ validatorApy }); } getGrants() { - this.props.operators.forEach(operator => { - const {botAddress, address} = operator - this.props.queryClient.getGrants(botAddress, this.props.address) - .then( - (result) => { - let grantValidators - if(result.stakeGrant){ - grantValidators = result.stakeGrant.authorization.allow_list.address - } - const operatorGrant = { - claimGrant: result.claimGrant, - stakeGrant: result.stakeGrant, - validators: grantValidators || [], - grantsValid: !!(result.claimGrant && result.stakeGrant && grantValidators.includes(address)), - grantsExist: !!(result.claimGrant || result.stakeGrant), - } - this.setState((state, props) => ({ - operatorGrants: _.set(state.operatorGrants, botAddress, operatorGrant) - })) - }, (error) => { - if (error.response && error.response.status === 501) { - this.setState({ authzMissing: true }); - }else{ - this.setState({ error: 'Failed to get grants. Please refresh' }); - } - }) - }) + this.props.operators.forEach((operator) => { + const { botAddress, address } = operator; + this.props.queryClient.getGrants(botAddress, this.props.address).then( + (result) => { + let grantValidators; + if (result.stakeGrant) { + grantValidators = + result.stakeGrant.authorization.allow_list.address; + } + const operatorGrant = { + claimGrant: result.claimGrant, + stakeGrant: result.stakeGrant, + validators: grantValidators || [], + grantsValid: !!( + result.claimGrant && + result.stakeGrant && + grantValidators.includes(address) + ), + grantsExist: !!(result.claimGrant || result.stakeGrant), + }; + this.setState((state, props) => ({ + operatorGrants: _.set( + state.operatorGrants, + botAddress, + operatorGrant + ), + })); + }, + (error) => { + if (error.response && error.response.status === 501) { + this.setState({ authzMissing: true }); + } else { + this.setState({ error: "Failed to get grants. Please refresh" }); + } + } + ); + }); } - getTestGrant(){ - this.props.queryClient.getGrants(this.props.network.testAddress, this.props.address) + getTestGrant() { + this.props.queryClient + .getGrants(this.props.network.testAddress, this.props.address) .then( - (result) => { }, (error) => { + (result) => {}, + (error) => { if (error.response && error.response.status === 501) { this.setState({ authzMissing: true }); } - }) + } + ); } - onGrant(operator){ + onGrant(operator) { const operatorGrant = { grantsValid: true, - grantsExist: true - } + grantsExist: true, + }; this.setState((state, props) => ({ - operatorGrants: _.set(state.operatorGrants, operator.botAddress, operatorGrant), + operatorGrants: _.set( + state.operatorGrants, + operator.botAddress, + operatorGrant + ), error: null, - validatorLoading: _.set(state.validatorLoading, operator.address, false) - })) - setTimeout(() => this.getGrants(), 10_000) + validatorLoading: _.set(state.validatorLoading, operator.address, false), + })); + setTimeout(() => this.getGrants(), 10_000); } onRevoke(operator) { @@ -150,116 +175,147 @@ class Delegations extends React.Component { stakeGrant: null, validators: [], grantsValid: false, - grantsExist: false - } + grantsExist: false, + }; this.setState((state, props) => ({ - operatorGrants: _.set(state.operatorGrants, operator.botAddress, operatorGrant), + operatorGrants: _.set( + state.operatorGrants, + operator.botAddress, + operatorGrant + ), error: null, - validatorLoading: _.set(state.validatorLoading, operator.address, false) - })) + validatorLoading: _.set(state.validatorLoading, operator.address, false), + })); } - onClaimRewards(){ - this.setState({claimLoading: false, validatorLoading: {}, error: null}) + onClaimRewards() { + this.setState({ claimLoading: false, validatorLoading: {}, error: null }); setTimeout(() => { - this.props.getDelegations() - this.getRewards() - }, 6_000) + this.props.getDelegations(); + this.getRewards(); + }, 6_000); } - setClaimLoading(value){ - if(value) this.setState({error: null}) - this.setState({claimLoading: !!value}) + setClaimLoading(value) { + if (value) this.setState({ error: null }); + this.setState({ claimLoading: !!value }); } - setValidatorLoading(validatorAddress, value){ - if(value) this.setState({error: null}) + setValidatorLoading(validatorAddress, value) { + if (value) this.setState({ error: null }); this.setState((state, props) => ({ - validatorLoading: _.set(state.validatorLoading, validatorAddress, value) - })) + validatorLoading: _.set(state.validatorLoading, validatorAddress, value), + })); } - setError(error){ - this.setState({ error: error }) + setError(error) { + this.setState({ error: error }); } - grantsValid(operator){ - const grants = this.state.operatorGrants[operator.botAddress] - return grants && grants.grantsValid + grantsValid(operator) { + const grants = this.state.operatorGrants[operator.botAddress]; + return grants && grants.grantsValid; } - restakePossible(){ - return !this.state.isNanoLedger && !this.state.authzMissing + restakePossible() { + return !this.state.isNanoLedger && !this.state.authzMissing; } - operatorForValidator(validatorAddress){ - return this.props.operators.find(el => el.address === validatorAddress) + operatorForValidator(validatorAddress) { + return this.props.operators.find((el) => el.address === validatorAddress); } - operatorAddresses(){ - return this.props.operators.map(operator => operator.address) + operatorAddresses() { + return this.props.operators.map((operator) => operator.address); } - operatorBotAddresses(){ - return this.props.operators.map(operator => operator.botAddress) + operatorBotAddresses() { + return this.props.operators.map((operator) => operator.botAddress); } - orderedOperators(){ - return _.sortBy(this.props.operators, ({address}) => this.props.delegations[address] ? 0 : 1) + orderedOperators() { + return _.sortBy(this.props.operators, ({ address }) => + this.props.delegations[address] ? 0 : 1 + ); } - regularDelegations(){ - return Object.values(_.omit(this.props.delegations, this.operatorAddresses())) + regularDelegations() { + return Object.values( + _.omit(this.props.delegations, this.operatorAddresses()) + ); } - noDelegations(){ - return Object.values(this.props.operators).length < 1 && Object.values(this.props.delegations).length < 1 + noDelegations() { + return ( + Object.values(this.props.operators).length < 1 && + Object.values(this.props.delegations).length < 1 + ); } - totalRewards(validators){ - if(!this.state.rewards) return; + totalRewards(validators) { + if (!this.state.rewards) return; - const denom = this.props.network.denom + const denom = this.props.network.denom; const total = Object.values(this.state.rewards).reduce((sum, item) => { - const reward = item.reward.find(el => el.denom === denom) - if(reward && (validators === undefined || validators.includes(item.validator_address))){ - return sum + parseInt(reward.amount) + const reward = item.reward.find((el) => el.denom === denom); + if ( + reward && + (validators === undefined || + validators.includes(item.validator_address)) + ) { + return sum + parseInt(reward.amount); } - return sum - }, 0) + return sum; + }, 0); return { amount: total, - denom: denom - } + denom: denom, + }; } - denomRewards(rewards){ - return rewards.reward.find(reward => reward.denom === this.props.network.denom) + denomRewards(rewards) { + return rewards.reward.find( + (reward) => reward.denom === this.props.network.denom + ); } - renderValidator(validatorAddress, delegation){ - const validator = this.props.validators[validatorAddress] - if(validator){ - const rewards = this.state.rewards && this.state.rewards[validatorAddress] - const denomRewards = rewards && this.denomRewards(rewards) - const operator = this.operatorForValidator(validatorAddress) - let rowVariant = operator && delegation ? ( - this.grantsValid(operator) ? 'table-success' : 'table-warning' - ) : undefined + renderValidator(validatorAddress, delegation) { + const validator = this.props.validators[validatorAddress]; + if (validator) { + const rewards = + this.state.rewards && this.state.rewards[validatorAddress]; + const denomRewards = rewards && this.denomRewards(rewards); + const operator = this.operatorForValidator(validatorAddress); + let rowVariant = + operator && delegation + ? this.grantsValid(operator) + ? "table-success" + : "table-warning" + : undefined; const delegationBalance = (delegation && delegation.balance) || { amount: 0, - denom: this.props.network.denom - } + denom: this.props.network.denom, + }; - const minimumReward = operator && { - amount: operator.data.minimumReward, - denom: this.props.network.denom - } + const minimumReward = operator && { + amount: operator.data.minimumReward, + denom: this.props.network.denom, + }; return (
    + + {validator.description.moniker} + onDelegate={this.onClaimRewards} + > + {validator.description.moniker} + + {this.state.validatorApy + ? !isNaN(this.state.validatorApy[validatorAddress]) + ? (this.state.validatorApy[validatorAddress] * 100) + .toFixed(2) + .toString() + " %" + : "" + : ""} + - {operator ? this.restakePossible() && delegation ? ( - this.grantsValid(operator) ? ( - + {operator ? ( + this.restakePossible() && delegation ? ( + this.grantsValid(operator) ? ( + + ) : ( + + ) ) : ( - + } + identifier={validatorAddress} + tooltip="This validator can REStake your rewards" + /> ) ) : ( - } identifier={validatorAddress} tooltip="This validator can REStake your rewards" /> - ) : ( - } identifier={validatorAddress} tooltip="This validator is not a REStake operator" /> + } + identifier={validatorAddress} + tooltip="This validator is not a REStake operator" + /> )} {operator && ( - {operator.frequency()}} identifier={operator.address}> + {operator.frequency()}} + identifier={operator.address} + >

    REStakes {operator.runTimesString()}

    -

    Minimum reward is

    +

    + Minimum reward is{" "} + +

    )}
    {validator.commission.commission_rates.rate * 100}% + {validator.commission.commission_rates.rate * 100}% + - + + + - {denomRewards && } + {denomRewards && ( + + + + )}
    - {!this.state.validatorLoading[validatorAddress] - ? delegation ? ( + {!this.state.validatorLoading[validatorAddress] ? ( + delegation ? ( - + Manage - {operator && this.restakePossible() && this.grantsValid(operator) && ( - <> - this.setValidatorLoading(validatorAddress, loading)} - setError={this.setError} /> -
    - - )} + {operator && + this.restakePossible() && + this.grantsValid(operator) && ( + <> + + this.setValidatorLoading( + validatorAddress, + loading + ) + } + setError={this.setError} + /> +
    + + )} this.setValidatorLoading(validatorAddress, loading)} - setError={this.setError} /> + setLoading={(loading) => + this.setValidatorLoading(validatorAddress, loading) + } + setError={this.setError} + /> this.setValidatorLoading(validatorAddress, loading)} - setError={this.setError} /> + setLoading={(loading) => + this.setValidatorLoading(validatorAddress, loading) + } + setError={this.setError} + />
    + onDelegate={this.onClaimRewards} + /> + onDelegate={this.onClaimRewards} + /> + onDelegate={this.onClaimRewards} + />
    ) : ( - ): ( - + onDelegate={this.onClaimRewards} + /> ) - } + ) : ( + + )}
    ValidatorAPYREStakeFrequencyCommission + Frequency + + Commission + Delegation Rewards
    )} @@ -504,51 +677,61 @@ class Delegations extends React.Component { delegations={this.props.delegations} availableBalance={this.props.balance} stargateClient={this.props.stargateClient} - onDelegate={this.props.onAddValidator} /> + onDelegate={this.props.onAddValidator} + />
    - {this.state.rewards && ( - !this.state.claimLoading - ? ( - - - All Rewards - - - - - - - - ) : ( - - ) - )} + {this.state.rewards && + (!this.state.claimLoading ? ( + + + All Rewards + + + + + + + + ) : ( + + ))}
    - ) + ); } } diff --git a/src/networks.json b/src/networks.json index 51beeea7e..e80346fa2 100644 --- a/src/networks.json +++ b/src/networks.json @@ -172,6 +172,7 @@ }, { "name": "juno", + "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/juno", "https://lcd-juno.itastakers.com" @@ -1017,6 +1018,7 @@ }, { "name": "emoney", + "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/emoney", "https://lcd-emoney.keplr.app", @@ -1245,6 +1247,7 @@ }, { "name": "evmos", + "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/evmos", "https://rest.bd.evmos.org:1317" @@ -1518,6 +1521,7 @@ }, { "name": "stargaze", + "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/stargaze", "https://rest.stargaze-apis.com", @@ -1603,6 +1607,7 @@ }, { "name": "comdex", + "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/comdex", "https://api.comdex.audit.one/rest", diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 925282be3..54ff71148 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -1,143 +1,307 @@ -import axios from 'axios' -import _ from 'lodash' +import axios from "axios"; +import _ from "lodash"; +import { + setupStakingExtension, + QueryClient as CosmjsQueryClient, + setupBankExtension, + setupDistributionExtension, + setupMintExtension, + setupGovExtension, +} from "@cosmjs/stargate"; +import { Tendermint34Client } from "@cosmjs/tendermint-rpc"; +import { Decimal } from "@cosmjs/math"; + +function parseCommissionRate(validator) { + return ( + parseInt(validator.commission.commission_rates.rate) / 1000000000000000000 + ); +} + +function duration(epochs, epochIdentifier) { + const epoch = epochs.find((epoch) => epoch.identifier === epochIdentifier); + if (!epoch) { + return 0; + } + + // Actually, the date type of golang protobuf is returned by the unit of seconds. + return parseInt(epoch.duration.replace("s", "")); +} const QueryClient = async (chainId, rpcUrls, restUrls) => { + const rpcUrl = await findAvailableUrl( + Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls], + "rpc" + ); + const restUrl = await findAvailableUrl( + Array.isArray(restUrls) ? restUrls : [restUrls], + "rest" + ); - const rpcUrl = await findAvailableUrl(Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls], 'rpc') - const restUrl = await findAvailableUrl(Array.isArray(restUrls) ? restUrls : [restUrls], 'rest') + const makeClient = async () => { + const tmClient = await Tendermint34Client.connect(rpcUrl); + return CosmjsQueryClient.withExtensions( + tmClient, + setupStakingExtension, + setupBankExtension, + setupDistributionExtension, + setupMintExtension, + setupGovExtension + ); + }; const getAllValidators = (pageSize, pageCallback) => { return getAllPages((nextKey) => { - return getValidators(pageSize, nextKey) - }, pageCallback).then(pages => { - const validators = _.shuffle(pages.map(el => el.validators).flat()) - return validators.reduce((a, v) => ({ ...a, [v.operator_address]: v}), {}) - }) - } + return getValidators(pageSize, nextKey); + }, pageCallback).then((pages) => { + const validators = _.shuffle(pages.map((el) => el.validators).flat()); + return validators.reduce( + (a, v) => ({ ...a, [v.operator_address]: v }), + {} + ); + }); + }; const getValidators = (pageSize, nextKey) => { - const searchParams = new URLSearchParams() - searchParams.append('status', 'BOND_STATUS_BONDED') - if(pageSize) searchParams.append('pagination.limit', pageSize) - if(nextKey) searchParams.append('pagination.key', nextKey) - return axios.get(restUrl + "/cosmos/staking/v1beta1/validators?" + searchParams.toString(), { - timeout: 5000 - }).then(res => res.data) - } - - const getBalance = (address, denom) => { - return axios.get(restUrl + "/cosmos/bank/v1beta1/balances/" + address) - .then(res => res.data) - .then( - (result) => { - const balance = result.balances.find(element => element.denom === denom) || {denom: denom, amount: 0} - return balance + const searchParams = new URLSearchParams(); + searchParams.append("status", "BOND_STATUS_BONDED"); + if (pageSize) searchParams.append("pagination.limit", pageSize); + if (nextKey) searchParams.append("pagination.key", nextKey); + return axios + .get( + restUrl + + "/cosmos/staking/v1beta1/validators?" + + searchParams.toString(), + { + timeout: 5000, } ) - } + .then((res) => res.data); + }; + + const getBalance = (address, denom) => { + return axios + .get(restUrl + "/cosmos/bank/v1beta1/balances/" + address) + .then((res) => res.data) + .then((result) => { + const balance = result.balances.find( + (element) => element.denom === denom + ) || { denom: denom, amount: 0 }; + return balance; + }); + }; const getDelegations = (address) => { - return axios.get(restUrl + "/cosmos/staking/v1beta1/delegations/" + address) - .then(res => res.data) - .then( - (result) => { - const delegations = _.shuffle(result.delegation_responses).reduce((a, v) => ({ ...a, [v.delegation.validator_address]: v}), {}) - return delegations - } - ) - } + return axios + .get(restUrl + "/cosmos/staking/v1beta1/delegations/" + address) + .then((res) => res.data) + .then((result) => { + const delegations = _.shuffle(result.delegation_responses).reduce( + (a, v) => ({ ...a, [v.delegation.validator_address]: v }), + {} + ); + return delegations; + }); + }; const getRewards = (address) => { - return axios.get(restUrl + "/cosmos/distribution/v1beta1/delegators/" + address + "/rewards") - .then(res => res.data) - .then( - (result) => { - const rewards = result.rewards.reduce((a, v) => ({ ...a, [v.validator_address]: v}), {}) - return rewards - } + return axios + .get( + restUrl + + "/cosmos/distribution/v1beta1/delegators/" + + address + + "/rewards" ) - } + .then((res) => res.data) + .then((result) => { + const rewards = result.rewards.reduce( + (a, v) => ({ ...a, [v.validator_address]: v }), + {} + ); + return rewards; + }); + }; const getGrants = (botAddress, address) => { const searchParams = new URLSearchParams(); searchParams.append("grantee", botAddress); searchParams.append("granter", address); // searchParams.append("msg_type_url", "/cosmos.staking.v1beta1.MsgDelegate"); - return axios.get(restUrl + "/cosmos/authz/v1beta1/grants?" + searchParams.toString()) - .then(res => res.data) - .then( - (result) => { - const claimGrant = result.grants.find(el => { - if (el.authorization['@type'] === "/cosmos.authz.v1beta1.GenericAuthorization" && - el.authorization.msg === "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward"){ - return Date.parse(el.expiration) > new Date() - }else{ - return false - } - }) - const stakeGrant = result.grants.find(el => { - if(el.authorization['@type'] === "/cosmos.staking.v1beta1.StakeAuthorization"){ - return Date.parse(el.expiration) > new Date() - }else{ - return false - } - }) - return { - claimGrant, - stakeGrant + return axios + .get(restUrl + "/cosmos/authz/v1beta1/grants?" + searchParams.toString()) + .then((res) => res.data) + .then((result) => { + const claimGrant = result.grants.find((el) => { + if ( + el.authorization["@type"] === + "/cosmos.authz.v1beta1.GenericAuthorization" && + el.authorization.msg === + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward" + ) { + return Date.parse(el.expiration) > new Date(); + } else { + return false; } - } - ) - } + }); + const stakeGrant = result.grants.find((el) => { + if ( + el.authorization["@type"] === + "/cosmos.staking.v1beta1.StakeAuthorization" + ) { + return Date.parse(el.expiration) > new Date(); + } else { + return false; + } + }); + return { + claimGrant, + stakeGrant, + }; + }); + }; - const getAllValidatorDelegations = (validatorAddress, pageSize, pageCallback) => { + const getAllValidatorDelegations = ( + validatorAddress, + pageSize, + pageCallback + ) => { return getAllPages((nextKey) => { - return getValidatorDelegations(validatorAddress, pageSize, nextKey) - }, pageCallback).then(pages => { - return pages.map(el => el.delegation_responses).flat() - }) - } + return getValidatorDelegations(validatorAddress, pageSize, nextKey); + }, pageCallback).then((pages) => { + return pages.map((el) => el.delegation_responses).flat(); + }); + }; const getValidatorDelegations = (validatorAddress, pageSize, nextKey) => { - const searchParams = new URLSearchParams() - if(pageSize) searchParams.append("pagination.limit", pageSize) - if(nextKey) searchParams.append("pagination.key", nextKey) + const searchParams = new URLSearchParams(); + if (pageSize) searchParams.append("pagination.limit", pageSize); + if (nextKey) searchParams.append("pagination.key", nextKey); - return axios.get(restUrl + "/cosmos/staking/v1beta1/validators/" + validatorAddress + "/delegations?" + searchParams.toString()) - .then(res => res.data) - } + return axios + .get( + restUrl + + "/cosmos/staking/v1beta1/validators/" + + validatorAddress + + "/delegations?" + + searchParams.toString() + ) + .then((res) => res.data); + }; const getAllPages = async (getPage, pageCallback) => { - let pages = [] - let nextKey, error + let pages = []; + let nextKey, error; do { - const result = await getPage(nextKey) - pages.push(result) - nextKey = result.pagination.next_key - if(pageCallback) pageCallback(pages) - } while (nextKey) - return pages - } + const result = await getPage(nextKey); + pages.push(result); + nextKey = result.pagination.next_key; + if (pageCallback) pageCallback(pages); + } while (nextKey); + return pages; + }; - function findAvailableUrl(urls, type){ - const path = type === 'rest' ? '/blocks/latest' : '/block' - return Promise.any(urls.map(url => { - return axios.get(url + path, {timeout: 10000}) - .then(res => res.data) - .then(data => { - if(type === 'rpc') data = data.result - if(!data.block.header.chain_id === chainId){ - throw false; - } - return url - }) - })) + /* const getInflation = async () => { + const client = await makeClient(); + const inflation = await client.mint.inflation(); + return inflation.toFloatApproximation(); + }; */ + + const getChainApr = async (denom) => { + const client = await makeClient(); + const pool = await client.staking.pool(); + const supply = await client.bank.supplyOf(denom); + const bondedTokens = pool.pool.bondedTokens; + const totalSupply = supply.amount; + if (chainId.startsWith("osmosis")) { + const apr = await osmosisApr(totalSupply, bondedTokens); + return apr; + } else if (chainId.startsWith("sifchain")) { + const aprRequest = await axios.get( + "https://data.sifchain.finance/beta/validator/stakingRewards" + ); + const apr = aprRequest.data.rate; + return apr; + } else { + const req = await client.mint.inflation(); + const baseInflation = req.toFloatApproximation(); + const ratio = bondedTokens / totalSupply; + const apr = baseInflation / ratio; + return apr; + } + }; + + const osmosisApr = async (totalSupply, bondedTokens) => { + const mintParams = await axios.get( + restUrl + "/osmosis/mint/v1beta1/params" + ); + const osmosisEpochs = await axios.get( + restUrl + "/osmosis/epochs/v1beta1/epochs" + ); + const epochProvisions = await axios.get( + restUrl + "/osmosis/mint/v1beta1/epoch_provisions" + ); + const { params } = mintParams.data; + const { epochs } = osmosisEpochs.data; + const { epoch_provisions } = epochProvisions.data; + const mintingEpochProvision = + parseFloat(params.distribution_proportions.staking) * epoch_provisions; + const epochDuration = duration(epochs, params.epoch_identifier); + const yearMintingProvision = + (mintingEpochProvision * (365 * 24 * 3600)) / epochDuration; + const baseInflation = yearMintingProvision / totalSupply; + const bondedRatio = bondedTokens / totalSupply; + const apr = baseInflation / bondedRatio; + console.log( + params, + epochs, + mintingEpochProvision, + epochDuration, + yearMintingProvision, + baseInflation, + bondedRatio, + apr, + "getting osmosis apr", + epochProvisions, + osmosisEpochs, + mintParams, + apr + ); + return apr; + }; + const getApy = async (validators, denom) => { + const periodPerYear = 365; + const chainApr = await getChainApr(denom); + let validatorApy = {}; + for (const [address, validator] of Object.entries(validators)) { + const realApr = chainApr * (1 - parseCommissionRate(validator)); + const apy = (1 + realApr / periodPerYear) ** periodPerYear - 1; + validatorApy[address] = apy; + } + return validatorApy; + }; + + function findAvailableUrl(urls, type) { + const path = type === "rest" ? "/blocks/latest" : "/block"; + return Promise.any( + urls.map((url) => { + return axios + .get(url + path, { timeout: 10000 }) + .then((res) => res.data) + .then((data) => { + if (type === "rpc") data = data.result; + if (!data.block.header.chain_id === chainId) { + throw false; + } + return url; + }); + }) + ); } return { connected: !!rpcUrl && !!restUrl, rpcUrl, restUrl, + getApy, getAllValidators, getValidators, getAllValidatorDelegations, @@ -145,8 +309,8 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { getBalance, getDelegations, getRewards, - getGrants - } -} + getGrants, + }; +}; export default QueryClient; From e79368d942c47262528ecccd3fb0710bd04fac0e Mon Sep 17 00:00:00 2001 From: Marko Baricevic Date: Wed, 16 Mar 2022 00:18:40 +0100 Subject: [PATCH 139/862] add binary for umee & osmosis --- src/networks.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 833296d9f..a5a616fde 100644 --- a/src/networks.json +++ b/src/networks.json @@ -166,6 +166,12 @@ "botAddress": "osmo1syc66updwkat9nhmfplzzvflh5789phc3z4kgk", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "osmovaloper15czt5nhlnvayqq37xun9s9yus0d6y26d5jws45", + "botAddress": "osmo1mmg6pqqnu6ktl4yp0afwzz6g33w8kpw35sufdn", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": true @@ -1751,6 +1757,12 @@ "botAddress": "umee19pqrxrl6n0g0mky4y79hlfzchprmsp5jp3yygg", "runTime": "17:00", "minimumReward": 10000 + }, + { + "address": "umeevaloper1q5z5vdxtvqmf4eq37pm2xnytc69st382cadf0x", + "botAddress": "umee1mmg6pqqnu6ktl4yp0afwzz6g33w8kpw3wajxln", + "runTime": "every 1 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -2228,4 +2240,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 4d95610795da2d155663c1edafe5f73753b414bd Mon Sep 17 00:00:00 2001 From: David Pierret Date: Wed, 16 Mar 2022 00:55:47 +0100 Subject: [PATCH 140/862] fix Persistence wallet --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 833296d9f..7f7f7df2b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1821,7 +1821,7 @@ }, { "address": "persistencevaloper1nchnrey36nrvzjslscu0c3l8j0r4z92hlsz3gk", - "botAddress": "persistence1f204r62y7l3ye7s3z05pvwufjawaf6vefpjmk0", + "botAddress": "persistence1v9vae554q55xa4pcfdm0p3kv8nxn50jk5qpprd", "runTime": "23:00", "minimumReward": 10000 }, From 786c333b8c1de89ced0b5af4d71901efd23941cb Mon Sep 17 00:00:00 2001 From: Jean-Emrick Date: Tue, 15 Mar 2022 20:03:16 -0400 Subject: [PATCH 141/862] remove unused code --- src/utils/QueryClient.mjs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 54ff71148..a8231577a 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -199,12 +199,6 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { return pages; }; - /* const getInflation = async () => { - const client = await makeClient(); - const inflation = await client.mint.inflation(); - return inflation.toFloatApproximation(); - }; */ - const getChainApr = async (denom) => { const client = await makeClient(); const pool = await client.staking.pool(); From 7e8070a11bebc880566dd2f4004b8d90c60eb567 Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Tue, 15 Mar 2022 20:03:41 -0400 Subject: [PATCH 142/862] Adjust Whisper + Infinity time (15m) --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 73e2638af..dec37c6d9 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1862,13 +1862,13 @@ { "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", "botAddress": "cerberus1h43hjfxlz8t6y5wewma63jz7melv46gjcaue9a", - "runTime": "every 10 minutes", + "runTime": "every 15 minutes", "minimumReward": 100000000 }, { "address": "cerberusvaloper1g3d36rfxfqtlnz3hzd05cs2wrjgykcz2g649a8", "botAddress": "cerberus1e44rluarkdw56dy2turnwjtvtg4wqvs0wv4dzw", - "runTime": "every 10 minutes", + "runTime": "every 15 minutes", "minimumReward": 100000000 }, { From f9e69b778e7cea046ce066370af3ed649661d7da Mon Sep 17 00:00:00 2001 From: StregoFren Date: Wed, 16 Mar 2022 00:17:07 +0000 Subject: [PATCH 143/862] Add Frens for Cerberus --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 833296d9f..45d83795f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2057,6 +2057,12 @@ "runTime": "every 4 minutes", "minimumReward": 10000000 }, + { + "address": "cerberusvaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5w2yl7tt", + "botAddress": "cerberus1ftaxf307mumu6gwg05q360xeryspcgsq6fvnyc", + "runTime": "every 5 minutes", + "minimumReward": 1000000 + }, { "address": "cerberusvaloper1u7m9kwj8tvwjaxympp3r8xq7d0pf5la994x7u8", "botAddress": "cerberus1r8egcurpwxftegr07gjv9gwffw4fk009hq0rdg", From 0deeeb473234ebbf3031cc31ac99dd537620ed2f Mon Sep 17 00:00:00 2001 From: poorbuthappy <97994242+poorbuthappy@users.noreply.github.com> Date: Wed, 16 Mar 2022 08:32:02 +0800 Subject: [PATCH 144/862] Update networks.json add LUM for validator PoorButHappy validator. --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 833296d9f..d2089a604 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1512,6 +1512,12 @@ "botAddress": "lum195mm9y35sekrjk73anw2az9lv9xs5mzt0uj0fr", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "lumvaloper1lnqradz7whff07t60pzetvkhgqamt9xu9f2j6k", + "botAddress": "lum14a78h6202u4xg6phjnd8rmr5fq98zzdlsmdrw5", + "runTime": "every 4 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -2228,4 +2234,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From cd1509b51e2095fb673f5776bea7c552d439bc9a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 16 Mar 2022 01:10:47 +0000 Subject: [PATCH 145/862] Highlight frequency --- src/components/Delegations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index d2aab3077..3b5d6a22c 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -294,7 +294,7 @@ class Delegations extends React.Component { {operator && ( - {operator.frequency()}} identifier={operator.address}> + {operator.frequency()}} identifier={operator.address}>

    REStakes {operator.runTimesString()}

    Minimum reward is

    From 98aec5c2e023e2b0b5a82ddd7e80f8ee072af188 Mon Sep 17 00:00:00 2001 From: poorbuthappy <97994242+poorbuthappy@users.noreply.github.com> Date: Wed, 16 Mar 2022 10:09:10 +0800 Subject: [PATCH 146/862] Update networks.json Update PoorButHappy Validator for Fetch.AI. --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..08fb4966f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2059,6 +2059,12 @@ "botAddress": "fetch1mjq48r6435aewerpruwc8up3tz3rzan2lyhnrz", "runTime": "21:00", "minimumReward": 10000000000000000 + }, + { + "address": "fetchvaloper132elwf5n6pjhxjh464khn8m2rzzr7wv4rlkwev", + "botAddress": "fetch14a78h6202u4xg6phjnd8rmr5fq98zzdlkveweh", + "runTime": "every 4 hour", + "minimumReward": 10000000000000000 } ], "authzSupport": false From 64cbf90d739d73bb67b2bec85860d9f764e22007 Mon Sep 17 00:00:00 2001 From: nodestake Date: Wed, 16 Mar 2022 04:28:28 +0000 Subject: [PATCH 147/862] update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..b35a25584 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2278,6 +2278,12 @@ "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper19f0w9svr905fhefusyx4z8sf83j6et0gfa2l5e", + "botAddress": "cerberus1etsdevgm5ps240lrw9mg3p2j32tj96ktjc9g8x", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From 2da8f3368f0cbe1dcdefb4a18837ab80d647e584 Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Wed, 16 Mar 2022 00:37:28 -0400 Subject: [PATCH 148/862] Update JUNO runtime --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..c854a3adc 100644 --- a/src/networks.json +++ b/src/networks.json @@ -231,8 +231,8 @@ { "address": "junovaloper193xl2tqh2tjkld2zv49ku5s44ee4qmgr65jcep", "botAddress": "juno1e44rluarkdw56dy2turnwjtvtg4wqvs0yeklse", - "runTime": "21:00", - "minimumReward": 1000 + "runTime": "every 45 minutes", + "minimumReward": 9999 }, { "address": "junovaloper17n3w6v5q3n0tws4xv8upd9ul4qqes0nlg7q0xd", From 64e034672e2634bedfc0ac83be5e1a0593d8dd97 Mon Sep 17 00:00:00 2001 From: FarmerL <88308388+chleeab@users.noreply.github.com> Date: Wed, 16 Mar 2022 14:09:42 +0800 Subject: [PATCH 149/862] Update SF RES Land minimum reward on Cerberus Also added zenchainlabs public rpc --- src/networks.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..3a12c1b95 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2066,10 +2066,12 @@ { "name": "cerberus", "restUrl": [ - "https://rest.cosmos.directory/cerberus" + "https://rest.cosmos.directory/cerberus", + "https://api-cerberus.zenchainlabs.io/" ], "rpcUrl": [ - "https://rpc.cosmos.directory/cerberus" + "https://rpc.cosmos.directory/cerberus", + "https://rpc-cerberus.zenchainlabs.io/" ], "gasPrice": "0.025ucrbrus", "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", @@ -2157,7 +2159,7 @@ "address": "cerberusvaloper1s3k0rhxxt8jl0tgyn2sule8rkzkx58x04a900g", "botAddress": "cerberus1x9e5qkjzzmfhw4dt05aludazwvd5x6340zl42n", "runTime": "every 1 hour", - "minimumReward": 100000000 + "minimumReward": 1000000 }, { "address": "cerberusvaloper16cfkt9n0pcftg0ky3rk9aegqdp383uwge38rdl", From 8a227495664b482c3fcf2f98b79be38e94832e1c Mon Sep 17 00:00:00 2001 From: "QuantumNode.Io" <98349274+quantumnodeio@users.noreply.github.com> Date: Wed, 16 Mar 2022 09:58:16 +0100 Subject: [PATCH 150/862] Update QN to autocompound less amounts --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..cb9bc1c20 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2277,7 +2277,7 @@ "address": "cerberusvaloper1m5dzr80ag23sqmw2cnzelj73xmsd62qtnswujc", "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", "runTime": "every 15 minutes", - "minimumReward": 100000000 + "minimumReward": 100000 } ], "authzSupport": true From 42d9efe03f4a6de5c690d8667f89dab6d1748ec5 Mon Sep 17 00:00:00 2001 From: BlockNgine Validator Date: Wed, 16 Mar 2022 09:07:06 +0000 Subject: [PATCH 151/862] Update BlockNgine configs --- src/networks.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..ffe01af19 100644 --- a/src/networks.json +++ b/src/networks.json @@ -23,7 +23,7 @@ { "address": "osmovaloper1pxphtfhqnx9ny27d53z4052e3r76e7qq495ehm", "botAddress": "osmo194ytn50yhh67rdha8akhs7c6zulnz4n2pv6n63", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 1 }, { @@ -461,7 +461,7 @@ { "address": "akashvaloper1strxz39h5fapp7zvn5mvf8xm0ea9aajqjstv6g", "botAddress": "akash194ytn50yhh67rdha8akhs7c6zulnz4n2yvyy4e", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 10000 }, { @@ -816,7 +816,7 @@ { "address": "terravaloper1f2t96sz9hnwsqnneux6v28xfgn07pkxjduvwjz", "botAddress": "terra194ytn50yhh67rdha8akhs7c6zulnz4n20nnrwr", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 10000 }, { @@ -856,7 +856,7 @@ { "address": "sentvaloper138dhtmfslt0ls4ec0jumj8tyajry4dfuv27jay", "botAddress": "sent194ytn50yhh67rdha8akhs7c6zulnz4n2jvl6gv", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -1053,7 +1053,7 @@ { "address": "emoneyvaloper1z0cwp2wezmaehlhzdmqpx55vhxupuk52d7ucjy", "botAddress": "emoney194ytn50yhh67rdha8akhs7c6zulnz4n2x5nhm7", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -1131,7 +1131,7 @@ { "address": "kavavaloper1hztgl202pvetspxch0pcchaqzrduw4qq64q08u", "botAddress": "kava194ytn50yhh67rdha8akhs7c6zulnz4n24za76y", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -1269,7 +1269,7 @@ { "address": "evmosvaloper1shvcjzhcxau6qtlz9w82a646ecwp4hq7ayqt0w", "botAddress": "evmos194ytn50yhh67rdha8akhs7c6zulnz4n2tkcdkt", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -1379,7 +1379,7 @@ { "address": "sifvaloper1lnhxf6war6qlldemkqzp0t3g57hpe9a6nh3tyv", "botAddress": "sif194ytn50yhh67rdha8akhs7c6zulnz4n2v2x4rg", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -1650,7 +1650,7 @@ { "address": "comdexvaloper195re7mhwh9urewm3rvaj9r7vm6j63c4sd78njd", "botAddress": "comdex194ytn50yhh67rdha8akhs7c6zulnz4n2wctp45", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 1000 }, { @@ -1767,7 +1767,7 @@ { "address": "umeevaloper1py60fpud4w468zn7mthfa25xnzuawjum7w90q2", "botAddress": "umee194ytn50yhh67rdha8akhs7c6zulnz4n2mp5ug3", - "runTime": "20:00", + "runTime": "every 1 hour", "minimumReward": 1 }, { @@ -2008,8 +2008,8 @@ "operators": [ { "address": "darcvaloper14kuh954mw4xp7gz5kvr3vtul0lpz5xt3tecpep", - "botAddress": "darc14kuh954mw4xp7gz5kvr3vtul0lpz5xt36agx4f", - "runTime": "20:00", + "botAddress": "darc194ytn50yhh67rdha8akhs7c6zulnz4n2kkajp5", + "runTime": "every 1 hour", "minimumReward": 100000 }, { From 32b259ffcae737a9f4403d72ea7e0d513f27f869 Mon Sep 17 00:00:00 2001 From: OrochiOrochi <91251550+OrochiOrochi@users.noreply.github.com> Date: Wed, 16 Mar 2022 17:18:56 +0700 Subject: [PATCH 152/862] Update networks.json IONode Update networks.json for IONode.online validator --- src/networks.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..d157a33a6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2273,6 +2273,14 @@ "runTime": "every 15 minutes", "minimumReward": 100000000 }, + +{ + "address": "cerberusvaloper1d0pk9sx7gfthuwmcepvuq9wcreqdr5cxmaj6et", + "botAddress": "cerberus1usypwpuwuvwmks4ymqz5s4csya00xttuqf4fcr", + "runTime": ["09:00", "21:00"], + "minimumReward": 100000000 +}, + { "address": "cerberusvaloper1m5dzr80ag23sqmw2cnzelj73xmsd62qtnswujc", "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", From b123615537ac4b432391affa89731e6638a96249 Mon Sep 17 00:00:00 2001 From: Jason <33862521+JasonBonnici@users.noreply.github.com> Date: Wed, 16 Mar 2022 12:27:34 +0100 Subject: [PATCH 153/862] Update networks.json Update Bitponder Validator for Bitsong --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..26eb301d0 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1828,6 +1828,12 @@ "botAddress": "bitsong1kxv5szlqgt8p7w95w76m7ahgjt58ultqkmpjja", "runTime": "every 1 hour", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper1ech3swu5jeuenxzgd0rq9xz6yz3yn6t0v2x0tw", + "botAddress": "bitsong1m52073hvnza73uwrf45avgdwljyqx7p04qh3yw", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true From e2342e43c94c0313eaadde6cf0754a9abfb2631c Mon Sep 17 00:00:00 2001 From: tpmmthomas <71880685+tpmmthomas@users.noreply.github.com> Date: Wed, 16 Mar 2022 19:52:28 +0800 Subject: [PATCH 154/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index d79a032fe..475568285 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1145,7 +1145,7 @@ }, { "address": "desmosvaloper1pe2fwwffxn2qnykeut8wzm20sv6eevxedlgpfu", - "botAddress": "desmos1cttl86ufne2qvctzdjtnunapr9y9qptt9chfra", + "botAddress": "desmos169n5p9xuhsnn85xunkg7sdmhtwqgwc3jhnzc0u", "runTime": "21:00", "minimumReward": 10000 } From 798dc391ed2c9aabb42a744aab3a576aa8fb98c4 Mon Sep 17 00:00:00 2001 From: Jacques Vella Critien <30493486+jacquesvcritien@users.noreply.github.com> Date: Wed, 16 Mar 2022 14:49:21 +0100 Subject: [PATCH 155/862] Added Simply Staking Validator for cerberus --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..df3efb0f3 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2278,6 +2278,12 @@ "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1j5f0gy8qa79v2eddmh32ef0z827345w04utym0", + "botAddress": "cerberus1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9rdx0r7", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From 516d62eb2b75a630f83b0b9e609bb67d57ee5f5d Mon Sep 17 00:00:00 2001 From: broVeider Date: Wed, 16 Mar 2022 16:13:40 +0200 Subject: [PATCH 156/862] add CRBRUS for CyberDuck validator --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..a0ffba7ad 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2278,6 +2278,12 @@ "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1j5dmnpyk4hghkph33c5fz9nqw2tkj43u2lmw0w", + "botAddress": "cerberus1fat08u2m6a4nqqqh55agp82gx4r6hzwwh0705f", + "runTime": "every 10 minutes", + "minimumReward": 25000000 } ], "authzSupport": true From 4c65636f5d0b372cab3784ab7c02a492225c0cf6 Mon Sep 17 00:00:00 2001 From: CatsNetwork Date: Wed, 16 Mar 2022 15:56:56 +0000 Subject: [PATCH 157/862] CATS - Update networks.json --- src/networks.json | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..2e82d80a1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1161,6 +1161,15 @@ ], "gasPrice": "0.001udsm", "operators": [ + { + "address": "desmosvaloper1xk2kv26fjd9zrg4l3qc7lwxv03w4ydeeppqkkp", + "botAddress": "desmos1dpcdg5qr033gdzlde5qqgd9ysssfcjhq6w9fjw", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000000 + }, { "address": "desmosvaloper18yazgsq8yvn2f8c734fmnu2ssfrzpw7l8tqckm", "botAddress": "desmos1ueyqd3shd7lrpqacjzap8vy6akh2ek8l40fn0y", @@ -1426,6 +1435,15 @@ "https://node0.mainnet.lum.network/rpc" ], "operators": [ + { + "address": "lumvaloper1xkv494sduqkpadwesqlsp6069yepsj587dvf46", + "botAddress": "lum1js340eq8082essggjtv8lf9df7qlnny88wzu7n", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000000 + }, { "address": "lumvaloper16w9g7epcvwnmya5khpj2kk9x7k39pjp3ufa3uj", "botAddress": "lum15xa7vst3jca9qjkngmlysrdfur8gsex98h7u0h", @@ -1796,6 +1814,15 @@ "https://rpc-bitsong.itastakers.com" ], "operators": [ + { + "address": "bitsongvaloper10uv3t6yru5dryz2yy9em2pzmqezyhsp0gkkxd2", + "botAddress": "bitsong1xkst6jjmtrd7v9wahqazguxuy04msfuy5ljm09", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000000 + }, { "address": "bitsongvaloper1mvpx2jnq7vnur3dehwesddg6as5eua0jy2a99s", "botAddress": "bitsong1wdc6rmm2ng2jkeytt4ptas3vhx2t94yjvcg4g4", @@ -2075,6 +2102,12 @@ "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ + { + "address": "cerberusvaloper1xkv494sduqkpadwesqlsp6069yepsj58axja5l", + "botAddress": "cerberus1js340eq8082essggjtv8lf9df7qlnny8wrlu7v", + "runTime": "every 2 hour", + "minimumReward": 100000000 + }, { "address": "cerberusvaloper1ga0xxzst2hjyjtfqnffz2mapn8vk8ufysqsn30", "botAddress": "cerberus15xa7vst3jca9qjkngmlysrdfur8gsex9w6ru0g", From 9d1f71e8b8c68c48690bd493be7452a4c1e51dc9 Mon Sep 17 00:00:00 2001 From: LPX Date: Thu, 17 Mar 2022 02:53:01 +0900 Subject: [PATCH 158/862] update ShapeShift operator run times --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..345de56df 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1773,7 +1773,7 @@ { "address": "umeevaloper1gvt9l5tshr0fp8ksl2tuem584z69eyvt9m8yh6", "botAddress": "umee19pqrxrl6n0g0mky4y79hlfzchprmsp5jp3yygg", - "runTime": "17:00", + "runTime": "every 6 hours", "minimumReward": 10000 }, { From c52a600ad2ccd4e632b73408114ccc7d3231ae28 Mon Sep 17 00:00:00 2001 From: silentnoname <635397523pl@gmail.com> Date: Thu, 17 Mar 2022 02:47:53 +0800 Subject: [PATCH 159/862] update silent validator for cerberus and stargaze --- src/networks.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..c6b9a88e4 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1621,6 +1621,12 @@ "botAddress": "stars1d72r4aaxkv2cpnxjh6xuklv9z4prcj2qmfv40r", "runTime": "21:00", "minimumReward": 10000 + }, + { + "address": "starsvaloper1xqum4wt30f3aqw8mmv823ty3x7gpmmde9zfndf", + "botAddress": "stars1r3g7japkptsvt3gv52gvuudprtcue762n08yrf", + "runTime": "12:00", + "minimumReward": 10000 } ], "authzSupport": true @@ -2278,7 +2284,14 @@ "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper13pdq3ee636ak6tytg5htyf0jcp20vfuxekeaeq", + "botAddress": "cerberus1r3g7japkptsvt3gv52gvuudprtcue762m5ssan", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } + ], "authzSupport": true } From 1f261bf164b388c7254cb3afc4a0389ad5a94a80 Mon Sep 17 00:00:00 2001 From: silentnoname <44841346+silentnoname@users.noreply.github.com> Date: Thu, 17 Mar 2022 02:52:24 +0800 Subject: [PATCH 160/862] update silent validator for cerberus and stargaze --- src/networks.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index c6b9a88e4..9574bab5b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2291,7 +2291,6 @@ "runTime": "every 15 minutes", "minimumReward": 100000000 } - ], "authzSupport": true } From d0933a7df012082e2f309242aa0ed362e3147f3b Mon Sep 17 00:00:00 2001 From: "Astra.GiefCat" <100313292+AstraGiefCat@users.noreply.github.com> Date: Wed, 16 Mar 2022 14:31:47 -0700 Subject: [PATCH 161/862] Updated Astra Voyage to 15 minutes and 1 Cerb Min --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 6a7a95d1d..c967853cd 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2072,8 +2072,8 @@ { "address": "cerberusvaloper1uvl2g9nd8qttjjyxjs30x7fj878d3wt4lt4juq", "botAddress": "cerberus1e7tcnlaen39yafqxcj5zhnd0m8r8xlzyq92vrv", - "runTime": "every 3 minutes", - "minimumReward": 10000 + "runTime": "every 15 minutes", + "minimumReward": 1000000 }, { "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", From 03bd140107343acdb71e39a2c2d14f38844d48d3 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 16 Mar 2022 22:14:31 +0000 Subject: [PATCH 162/862] Add Secret Network --- src/networks.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/networks.json b/src/networks.json index 0dc1db020..c87c134ab 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2281,5 +2281,26 @@ } ], "authzSupport": true + }, + { + "name": "secretnetwork", + "restUrl": [ + "https://rest.cosmos.directory/secretnetwork" + ], + "rpcUrl": [ + "https://rpc.cosmos.directory/secretnetwork" + ], + "gasPrice": "0.025uscrt", + "testAddress": "secret1mpf7w0yyz9n3dh42un2zqu2dmsqv7dhkdlh78e", + "ownerAddress": "", + "operators": [ + { + "address": "secretvaloper16k26akna7h295rfjx3278s7xusnt736vy437y8", + "botAddress": "secret1yqv7y5ynhas45hu59vhe3qvw0kmc8amtx7s7ur", + "runTime": "21:00", + "minimumReward": 100000 + } + ], + "authzSupport": true } ] From 661e9bf6e8bbe0ff5c4388b3f0cf7fe77eba3437 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 16 Mar 2022 22:29:59 +0000 Subject: [PATCH 163/862] Fix formatting --- src/networks.json | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/networks.json b/src/networks.json index 678573807..751c9dc8f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2281,14 +2281,15 @@ "runTime": "every 15 minutes", "minimumReward": 100000000 }, - -{ - "address": "cerberusvaloper1d0pk9sx7gfthuwmcepvuq9wcreqdr5cxmaj6et", - "botAddress": "cerberus1usypwpuwuvwmks4ymqz5s4csya00xttuqf4fcr", - "runTime": ["09:00", "21:00"], - "minimumReward": 100000000 -}, - + { + "address": "cerberusvaloper1d0pk9sx7gfthuwmcepvuq9wcreqdr5cxmaj6et", + "botAddress": "cerberus1usypwpuwuvwmks4ymqz5s4csya00xttuqf4fcr", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 100000000 + }, { "address": "cerberusvaloper1m5dzr80ag23sqmw2cnzelj73xmsd62qtnswujc", "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", @@ -2325,4 +2326,4 @@ ], "authzSupport": true } -] +] \ No newline at end of file From 76488d00c17b0365f1b185b6f862bece593affc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Iglesias=20Castre=C3=B1o?= Date: Thu, 17 Mar 2022 01:07:25 +0100 Subject: [PATCH 164/862] Add Stakely Secret validator --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..5a88e4262 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2391,6 +2391,12 @@ "botAddress": "secret1yqv7y5ynhas45hu59vhe3qvw0kmc8amtx7s7ur", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "secretvaloper1vzkdmu0sa8gaj686jh5all7hpmmsp8x87vyz8z", + "botAddress": "secret1hl0jgxre5z0nkyjj07c5vfdy44yk44atm6k9jp", + "runTime": "22:30", + "minimumReward": 100000 } ], "authzSupport": true From 8b2d21f4b39abb10f311eba5052f829444363b3c Mon Sep 17 00:00:00 2001 From: EB Date: Wed, 16 Mar 2022 17:23:08 -0700 Subject: [PATCH 165/862] Add FreshSCRT --- src/networks.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..600a1f7f6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -141,7 +141,7 @@ "address": "osmovaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjp38f93", "botAddress": "osmo14gra89zm8753pfuhvujjulmtkf2qq70f3l9gtx", "runTime": "17:28", - "minimumReward": 10000 + "minimumReward": 100000 }, { "address": "osmovaloper1xf9zpq5kpxks49cg606tzd8qstaykxgt2vs0d5", @@ -334,7 +334,7 @@ "address": "junovaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgj6jfwcp", "botAddress": "juno14gra89zm8753pfuhvujjulmtkf2qq70f0k4r6g", "runTime": "17:28", - "minimumReward": 10000 + "minimumReward": 100000 }, { "address": "junovaloper1e8238v24qccht9mqc2w0r4luq462yxttjzn7qt", @@ -435,7 +435,7 @@ "address": "cosmosvaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjkfg0gh", "botAddress": "cosmos14gra89zm8753pfuhvujjulmtkf2qq70feykca5", "runTime": "17:28", - "minimumReward": 10000 + "minimumReward": 100000 }, { "address": "cosmosvaloper157v7tczs40axfgejp2m43kwuzqe0wsy0rv8puv", @@ -835,7 +835,7 @@ "address": "terravaloper1audgfvmgt0js54p3s8kj3r40uwej6vy2tv6rrw", "botAddress": "terra1hz2dutqm7e0t7q3h5cm9d8lchztskm8pekadus", "runTime": "17:28", - "minimumReward": 5000 + "minimumReward": 50000 } ], "authzSupport": true @@ -1632,7 +1632,7 @@ "address": "starsvaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjvlln5d", "botAddress": "stars14gra89zm8753pfuhvujjulmtkf2qq70fdcp9k9", "runTime": "17:28", - "minimumReward": 10000 + "minimumReward": 1000000 }, { "address": "starsvaloper1y58hfnm90r4efhlydx0gavz57lvm7k6uulkg3h", @@ -2391,8 +2391,14 @@ "botAddress": "secret1yqv7y5ynhas45hu59vhe3qvw0kmc8amtx7s7ur", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "secretvaloper182dg8p7nshdjjt5sypcx6hw9p8vlwqpntpm5k6", + "botAddress": "secret14gra89zm8753pfuhvujjulmtkf2qq70fmpz3qg", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true } -] \ No newline at end of file +] From 7e7d5822e29e2c740b63cb44850dc939376b8620 Mon Sep 17 00:00:00 2001 From: poorbuthappy <97994242+poorbuthappy@users.noreply.github.com> Date: Thu, 17 Mar 2022 09:04:22 +0800 Subject: [PATCH 166/862] Update networks.json Update BitSong Validator PoorButHappy. --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..1d4eb3f0d 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1873,6 +1873,12 @@ "botAddress": "bitsong1m52073hvnza73uwrf45avgdwljyqx7p04qh3yw", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper1fk4up90cx352jwr6clug8pkg5jt28ha9whf6pu", + "botAddress": "bitsong14a78h6202u4xg6phjnd8rmr5fq98zzdlfczze0", + "runTime": "every 4 hour", + "minimumReward": 100000 } ], "authzSupport": true @@ -2395,4 +2401,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 43486d3e2b5cff0c19265221b1503698fcd64e40 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Wed, 16 Mar 2022 18:09:52 -0700 Subject: [PATCH 167/862] Update networks.json --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..0cd7cb190 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2391,8 +2391,14 @@ "botAddress": "secret1yqv7y5ynhas45hu59vhe3qvw0kmc8amtx7s7ur", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "secretvaloper1t5wtcuwjkdct9qkw2h6m48zu2hectpd6ulmekk", + "botAddress": "secret18zzklau04kf3qtwrnnzlf20xrxpx3a89dqu42m", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true } -] \ No newline at end of file +] From 01fa05d97fa90015c2473a5149f5cf6a21c4a57e Mon Sep 17 00:00:00 2001 From: poorbuthappy <97994242+poorbuthappy@users.noreply.github.com> Date: Thu, 17 Mar 2022 09:11:53 +0800 Subject: [PATCH 168/862] Update networks.json Add Desmos Validator for PoorButHappy --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..745db0708 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1238,6 +1238,12 @@ "botAddress": "desmos169n5p9xuhsnn85xunkg7sdmhtwqgwc3jhnzc0u", "runTime": "21:00", "minimumReward": 10000 + }, + { + "address": "desmosvaloper1axt95v5rpc97dtnl24mxtfj95gpxu59vn6xyvw", + "botAddress": "desmos1qfp2g2ve043h9538nvns6ggda7686m7zvyvz8t", + "runTime": "every 4 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -2395,4 +2401,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 218acde70f75b3657dfd6c8e89da9e1a5dcac5ee Mon Sep 17 00:00:00 2001 From: Songhua Hu Date: Wed, 16 Mar 2022 19:25:11 -0700 Subject: [PATCH 169/862] update polkachu's restake minimum and time --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..32ae9cbd7 100644 --- a/src/networks.json +++ b/src/networks.json @@ -315,8 +315,8 @@ { "address": "junovaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pvzc6k3", "botAddress": "juno1xrrfek3qpz4ja8ccrjklfy064asyqq9k9q45nn", - "runTime": "21:00", - "minimumReward": 10000 + "runTime": "every 1 hour", + "minimumReward": 100000 }, { "address": "junovaloper124maqmcqv8tquy764ktz7cu0gxnzfw54l2d073", From 285407d42e2491eb4927f899262a8f77ba7b71be Mon Sep 17 00:00:00 2001 From: Songhua Hu Date: Wed, 16 Mar 2022 19:38:55 -0700 Subject: [PATCH 170/862] add polkachu's cerberus validator --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 32ae9cbd7..5ca5f2e1a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2370,6 +2370,12 @@ "botAddress": "cerberus1r3g7japkptsvt3gv52gvuudprtcue762m5ssan", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pvm8q9p", + "botAddress": "cerberus1xrrfek3qpz4ja8ccrjklfy064asyqq9k04kxpy", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From d22dc85e636aa52ab1c852972eb7b45d64670e17 Mon Sep 17 00:00:00 2001 From: Songhua Hu Date: Wed, 16 Mar 2022 21:27:30 -0700 Subject: [PATCH 171/862] update polkachu juno min rewards --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 5ca5f2e1a..017d1ce50 100644 --- a/src/networks.json +++ b/src/networks.json @@ -316,7 +316,7 @@ "address": "junovaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pvzc6k3", "botAddress": "juno1xrrfek3qpz4ja8ccrjklfy064asyqq9k9q45nn", "runTime": "every 1 hour", - "minimumReward": 100000 + "minimumReward": 50000 }, { "address": "junovaloper124maqmcqv8tquy764ktz7cu0gxnzfw54l2d073", From 791cb4129c706a82104dbf2c1727f216ba47bc9d Mon Sep 17 00:00:00 2001 From: BlockNgine Validator Date: Thu, 17 Mar 2022 06:28:06 +0000 Subject: [PATCH 172/862] Add BlockNgine validator --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..748d44634 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2391,8 +2391,14 @@ "botAddress": "secret1yqv7y5ynhas45hu59vhe3qvw0kmc8amtx7s7ur", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "secretvaloper12y30xefd0wg53xtyv9lw7mjcdvf8nxgzzva3sr", + "botAddress": "secret194ytn50yhh67rdha8akhs7c6zulnz4n2tja23l", + "runTime": "every 1 hour", + "minimumReward": 100000 } ], "authzSupport": true } -] \ No newline at end of file +] From b7ccbad147dce60f0460e051e7c92d581594bc27 Mon Sep 17 00:00:00 2001 From: bulldogjack Date: Thu, 17 Mar 2022 14:31:07 +0800 Subject: [PATCH 173/862] Pupmos support for JUNO. --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..b03c81b7c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -353,6 +353,12 @@ "botAddress": "juno1d72r4aaxkv2cpnxjh6xuklv9z4prcj2qe8cnrw", "runTime": "21:00", "minimumReward": 10000 + }, + { + "address": "junovaloper1ka8v934kgrw6679fs9cuu0kesyl0ljjy2kdtrl", + "botAddress": "juno1ka6r58g5h27x6gyyjkzxtpdk95k2yurf4ge0fl", + "runTime": "01:00", + "minimumReward": 10000 } ], "authzSupport": true From 3828385ddf38838ece6f974b2cd3ce2589a1714c Mon Sep 17 00:00:00 2001 From: stan-bl <72133698+stan-bl@users.noreply.github.com> Date: Thu, 17 Mar 2022 09:02:53 +0100 Subject: [PATCH 174/862] Updates BlueStake runtimes --- src/networks.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..afd7fa98a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -309,8 +309,8 @@ { "address": "junovaloper1z3svprqzmquw6mckh6rgyt0dmd4yhv7c6l58qc", "botAddress": "juno1m5hgzum68rjf4c7zhezgkj8hlnmr0kghqncsdv", - "runTime": "10:00", - "minimumReward": 1000 + "runTime": "every 1 hour", + "minimumReward": 10000 }, { "address": "junovaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pvzc6k3", @@ -892,8 +892,8 @@ { "address": "sentvaloper1wd944at7mn6eqm7c7m5lfd9ej3g6astqawjnqn", "botAddress": "sent1m5hgzum68rjf4c7zhezgkj8hlnmr0kghd6djwl", - "runTime": "10:00", - "minimumReward": 1000 + "runTime": "every 1 hour", + "minimumReward": 100000 } ], "authzSupport": false @@ -1009,8 +1009,8 @@ { "address": "bcnavaloper12wskdt5tcc9yx5ajdx8xjjujuc0fgs5p54j6ug", "botAddress": "bcna1m5hgzum68rjf4c7zhezgkj8hlnmr0kghv3t2zz", - "runTime": "10:00", - "minimumReward": 10000 + "runTime": "every 1 hour", + "minimumReward": 100000 }, { "address": "bcnavaloper1gsq4dsxvgsswkgrsftz0k905rzjc0n2hv26xgj", @@ -1083,8 +1083,8 @@ { "address": "emoneyvaloper10rk00srmct4z5huqjh7fc64vgqasl46jwm9g9l", "botAddress": "emoney1m5hgzum68rjf4c7zhezgkj8hlnmr0kghezplad", - "runTime": "10:00", - "minimumReward": 1000 + "runTime": "every 1 hour", + "minimumReward": 100000 }, { "address": "emoneyvaloper1sz7rp920hgupfl0p6c7kwkaes8q8tx3lmzhcrc", @@ -1212,8 +1212,8 @@ { "address": "desmosvaloper19c6nnp6afxj82ehxtlejyvg6zh58050jcgpdj6", "botAddress": "desmos1m5hgzum68rjf4c7zhezgkj8hlnmr0kghzekmag", - "runTime": "10:00", - "minimumReward": 10000 + "runTime": "every 1 hour", + "minimumReward": 100000 }, { "address": "desmosvaloper1zm3l7p8n5dxqeadsfxy3rd0j3c2knnx3chg77a", @@ -1613,8 +1613,8 @@ { "address": "starsvaloper1hr4ag3mdzy08rl6r7pga832kvchqctvmzdneds", "botAddress": "stars1m5hgzum68rjf4c7zhezgkj8hlnmr0kghzavkpp", - "runTime": "10:00", - "minimumReward": 10000 + "runTime": "every 1 hour", + "minimumReward": 100000 }, { "address": "starsvaloper1yskcx5zfkr3nacn6f5zd08yw72zwm4s06x8tpz", @@ -1728,8 +1728,8 @@ { "address": "comdexvaloper120g4zfrj75ezl6cmd466tk0rj9h3dkskc5tvkh", "botAddress": "comdex1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh3wefn8", - "runTime": "10:00", - "minimumReward": 1000 + "runTime": "every 1 hour", + "minimumReward": 100000 } ], "authzSupport": false @@ -1791,8 +1791,8 @@ { "address": "umeevaloper14vmuhgm04ffvzts9jxm2wy5d538g96ru9fsg5v", "botAddress": "umee1m5hgzum68rjf4c7zhezgkj8hlnmr0kghyhx5wz", - "runTime": "10:00", - "minimumReward": 10000 + "runTime": "every 1 hour", + "minimumReward": 100000 }, { "address": "umeevaloper1py60fpud4w468zn7mthfa25xnzuawjum7w90q2", @@ -1853,7 +1853,7 @@ { "address": "bitsongvaloper1mceksy7l3xenuyx4dzl64erf3fh29a3r2ercnz", "botAddress": "bitsong1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh6gfrgl", - "runTime": "10:00", + "runTime": "every 1 hour", "minimumReward": 100000 }, { @@ -1909,7 +1909,7 @@ { "address": "persistencevaloper1f9p23ru4sw8p2044237ckfhwdpklrn0ahdaujg", "botAddress": "persistence1m5hgzum68rjf4c7zhezgkj8hlnmr0kghcdacy5", - "runTime": "10:00", + "runTime": "every 1 hour", "minimumReward": 100000 }, { @@ -1969,7 +1969,7 @@ { "address": "ixovaloper1a3qpp5ff9cgnusxwufdkmnvn83446lf9vq88ga", "botAddress": "ixo1m5hgzum68rjf4c7zhezgkj8hlnmr0kghf59ewr", - "runTime": "10:00", + "runTime": "every 1 hour", "minimumReward": 100000 }, { @@ -2096,7 +2096,7 @@ { "address": "fetchvaloper13uwrjldm8m45cuarjdvgk9qwnysf835rgrugc6", "botAddress": "fetch1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh9uj0g8", - "runTime": "10:00", + "runTime": "every 1 hour", "minimumReward": 10000000000000000 }, { @@ -2395,4 +2395,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 3b704dad9212f9fcd92f11547b82719b4835beb0 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 16 Mar 2022 21:58:23 +0000 Subject: [PATCH 175/862] Quick refactor and tidy UI. Disable some networks --- src/ApyClient.mjs | 101 ++++++++++++++++++++++++++++++ src/components/Delegate.js | 31 +++++++++- src/components/Delegations.js | 71 ++++++++++++--------- src/components/Validators.js | 37 +++++++++-- src/networks.json | 12 ++-- src/utils/Chain.mjs | 7 ++- src/utils/Network.mjs | 8 ++- src/utils/Operator.mjs | 16 ++++- src/utils/QueryClient.mjs | 113 ---------------------------------- 9 files changed, 237 insertions(+), 159 deletions(-) create mode 100644 src/ApyClient.mjs diff --git a/src/ApyClient.mjs b/src/ApyClient.mjs new file mode 100644 index 000000000..7db5f4b88 --- /dev/null +++ b/src/ApyClient.mjs @@ -0,0 +1,101 @@ +import axios from "axios"; +import { + setupStakingExtension, + QueryClient as CosmjsQueryClient, + setupBankExtension, + setupDistributionExtension, + setupMintExtension, + setupGovExtension, +} from "@cosmjs/stargate"; +import { Tendermint34Client } from "@cosmjs/tendermint-rpc"; + +const ApyClient = (chain, rpcUrl, restUrl) => { + const { chainId, denom } = chain + + async function getApy(validators, operators) { + const chainApr = await getChainApr(denom); + let validatorApy = {}; + for (const [address, validator] of Object.entries(validators)) { + const commission = validator.commission.commission_rates.rate + const operator = operators.find((el) => el.address === address) + const periodPerYear = operator && chain.authzSupport ? operator.runsPerDay() * 365 : 1; + const realApr = chainApr * (1 - commission); + const apy = (1 + realApr / periodPerYear) ** periodPerYear - 1; + validatorApy[address] = apy; + } + return validatorApy; + } + + function duration(epochs, epochIdentifier) { + const epoch = epochs.find((epoch) => epoch.identifier === epochIdentifier); + if (!epoch) { + return 0; + } + + // Actually, the date type of golang protobuf is returned by the unit of seconds. + return parseInt(epoch.duration.replace("s", "")); + } + + async function makeClient() { + const tmClient = await Tendermint34Client.connect(rpcUrl); + return CosmjsQueryClient.withExtensions( + tmClient, + setupStakingExtension, + setupBankExtension, + setupDistributionExtension, + setupMintExtension, + setupGovExtension + ); + } + + async function getChainApr(denom) { + const client = await makeClient(); + const pool = await client.staking.pool(); + const supply = await client.bank.supplyOf(denom); + const bondedTokens = pool.pool.bondedTokens; + const totalSupply = supply.amount; + if (chainId.startsWith("osmosis")) { + const apr = await osmosisApr(totalSupply, bondedTokens); + return apr; + } else if (chainId.startsWith("sifchain")) { + const aprRequest = await axios.get( + "https://data.sifchain.finance/beta/validator/stakingRewards" + ); + const apr = aprRequest.data.rate; + return apr; + } else { + const req = await client.mint.inflation(); + const baseInflation = req.toFloatApproximation(); + const ratio = bondedTokens / totalSupply; + const apr = baseInflation / ratio; + return apr; + } + } + + async function osmosisApr(totalSupply, bondedTokens) { + const mintParams = await axios.get( + restUrl + "/osmosis/mint/v1beta1/params" + ); + const osmosisEpochs = await axios.get( + restUrl + "/osmosis/epochs/v1beta1/epochs" + ); + const epochProvisions = await axios.get( + restUrl + "/osmosis/mint/v1beta1/epoch_provisions" + ); + const { params } = mintParams.data; + const { epochs } = osmosisEpochs.data; + const { epoch_provisions } = epochProvisions.data; + const mintingEpochProvision = parseFloat(params.distribution_proportions.staking) * epoch_provisions; + const epochDuration = duration(epochs, params.epoch_identifier); + const yearMintingProvision = (mintingEpochProvision * (365 * 24 * 3600)) / epochDuration; + const baseInflation = yearMintingProvision / totalSupply; + const bondedRatio = bondedTokens / totalSupply; + const apr = baseInflation / bondedRatio; + return apr; + } + + return { + getApy + }; +} +export default ApyClient; \ No newline at end of file diff --git a/src/components/Delegate.js b/src/components/Delegate.js index 54a9b27ba..4a8185002 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -4,7 +4,6 @@ import ValidatorImage from './ValidatorImage' import ValidatorLink from './ValidatorLink' import Coins from './Coins' import TooltipIcon from './TooltipIcon' -import CountdownRestake from './CountdownRestake' import React, { useState, useRef } from 'react'; @@ -14,7 +13,8 @@ import { Modal, OverlayTrigger, Tooltip, - Table + Table, + Spinner } from 'react-bootstrap' import { @@ -150,6 +150,7 @@ function Delegate(props) { operators={props.operators} exclude={excludeValidators()} validators={props.validators} + validatorApy={props.validatorApy} getValidatorImage={props.getValidatorImage} delegations={props.delegations} selectValidator={(selectedValidator) => setSelectedValidator(selectedValidator)} />} @@ -167,6 +168,32 @@ function Delegate(props) { Commission {selectedValidator.commission.commission_rates.rate * 100}% + {props.network.data.apyEnabled !== false && ( + + + APY} + identifier="delegations-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is an estimate and best case scenario.

    +
    +
    + + + {Object.keys(props.validatorApy).length > 0 + ? props.validatorApy[selectedValidator.operator_address] + ? Math.round(props.validatorApy[selectedValidator.operator_address] * 100) + "%" + : "" + : ( + + Loading... + + )} + + + )} {!!securityContact() && ( Contact diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 38e354c47..c55008099 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -17,7 +17,7 @@ import { CheckCircle, XCircle } from "react-bootstrap-icons"; class Delegations extends React.Component { constructor(props) { super(props); - this.state = { operatorGrants: {}, validatorLoading: {} }; + this.state = { operatorGrants: {}, validatorLoading: {}, validatorApy: {} }; this.setError = this.setError.bind(this); this.setClaimLoading = this.setClaimLoading.bind(this); @@ -46,6 +46,7 @@ class Delegations extends React.Component { isNanoLedger: isNanoLedger, authzMissing: false, error: null, + validatorApy: {} }); this.refresh(); } @@ -92,11 +93,14 @@ class Delegations extends React.Component { } async calculateApy() { - let validatorApy = await this.props.queryClient.getApy( + this.props.network.getApy( this.props.validators, - this.props.network.denom - ); - this.setState({ validatorApy }); + this.props.operators + ).then(validatorApy => { + this.setState({ validatorApy }); + }, error => { + this.setState({ error: "Failed to get APY. Please refresh" }); + }) } getGrants() { @@ -321,6 +325,7 @@ class Delegations extends React.Component { network={this.props.network} address={this.props.address} validator={validator} + validatorApy={this.state.validatorApy} operators={this.props.operators} getValidatorImage={this.props.getValidatorImage} availableBalance={this.props.balance} @@ -330,19 +335,6 @@ class Delegations extends React.Component { {validator.description.moniker} - {this.props.network.data.apyEnabled !== false ? ( - - {this.state.validatorApy - ? !isNaN(this.state.validatorApy[validatorAddress]) - ? (this.state.validatorApy[validatorAddress] * 100) - .toFixed(2) - .toString() + " %" - : "" - : ""} - - ) : ( - "" - )} {operator ? ( this.restakePossible() && delegation ? ( @@ -397,9 +389,19 @@ class Delegations extends React.Component { )} - - {validator.commission.commission_rates.rate * 100}% - + {this.props.network.data.apyEnabled !== false && ( + + {Object.keys(this.state.validatorApy).length > 0 + ? this.state.validatorApy[validatorAddress] + ? {Math.round(this.state.validatorApy[validatorAddress] * 100) + "%"} + : "" + : ( + + Loading... + + )} + + )} Validator - {this.props.network.data.apyEnabled !== false ? ( - APY - ) : ( - "" - )} REStake Frequency - - Commission - + {this.props.network.apyEnabled !== false && ( + + APY} + identifier="delegations-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is a best case scenario and may not be 100% accurate.

    +
    +
    + + )} Delegation Rewards @@ -673,6 +685,7 @@ class Delegations extends React.Component { operators={this.props.operators} address={this.props.address} validators={this.props.validators} + validatorApy={this.state.validatorApy} getValidatorImage={this.props.getValidatorImage} delegations={this.props.delegations} availableBalance={this.props.balance} diff --git a/src/components/Validators.js b/src/components/Validators.js index 5a9fbbef0..d497ea8a1 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -47,14 +47,15 @@ function Validators(props) { } function renderItem(item, isOperator){ + const address = item.operator_address let variant = isOperator ? 'warning' : null variant = variant ? 'table-' + variant : '' return ( - +
    - +
    {item.description.moniker} @@ -63,13 +64,26 @@ function Validators(props) { {isOperator - ? } identifier={item.operator_address} + ? } identifier={address} tooltip="This validator can REStake your rewards" /> - : } identifier={item.operator_address} + : } identifier={address} tooltip="This validator is not a REStake operator" /> } - + {props.network.data.apyEnabled !== false && ( + + {Object.keys(props.validatorApy).length > 0 + ? props.validatorApy[address] + ? {Math.round(props.validatorApy[address] * 100) + "%"} + : "" + : ( + + Loading... + + )} + + )} + @@ -87,6 +101,19 @@ function Validators(props) { Validator REStake + {props.network.apyEnabled !== false && ( + + APY} + identifier="validators-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is an estimate and best case scenario.

    +
    +
    + + )} {filteredOperators().map(([validator_address, item], i) => renderItem(item, true))} diff --git a/src/networks.json b/src/networks.json index b20e7f83b..070c21e97 100644 --- a/src/networks.json +++ b/src/networks.json @@ -805,6 +805,7 @@ }, { "name": "terra", + "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/terra" ], @@ -1153,6 +1154,7 @@ }, { "name": "desmos", + "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/desmos", "https://api.mainnet.desmos.network" @@ -1928,6 +1930,7 @@ }, { "name": "agoric", + "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/agoric" ], @@ -1989,8 +1992,7 @@ { "name": "kichain", "restUrl": [ - "https://rest.cosmos.directory/kichain", - "https://api-mainnet.blockchain.ki" + "https://rest.cosmos.directory/kichain" ], "rpcUrl": [ "https://rpc.cosmos.directory/kichain" @@ -2122,12 +2124,10 @@ { "name": "cerberus", "restUrl": [ - "https://rest.cosmos.directory/cerberus", - "https://api-cerberus.zenchainlabs.io/" + "https://rest.cosmos.directory/cerberus" ], "rpcUrl": [ - "https://rpc.cosmos.directory/cerberus", - "https://rpc-cerberus.zenchainlabs.io/" + "https://rpc.cosmos.directory/cerberus" ], "gasPrice": "0.025ucrbrus", "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index d94d4cf11..863dc5320 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -19,7 +19,8 @@ const Chain = async (data) => { prettyName: data.prettyName || chainData.pretty_name, chainId: data.chainId || chainData.chain_id, prefix: data.prefix || chainData.bech32_prefix, - slip44: data.slip44 || chainData.slip44 || 118 + slip44: data.slip44 || chainData.slip44 || 118, + authzSupport: !!data.authzSupport } } @@ -40,7 +41,8 @@ const Chain = async (data) => { prettyName, chainId, prefix, - slip44 + slip44, + authzSupport } = getChainInfo() const { @@ -56,6 +58,7 @@ const Chain = async (data) => { chainId, prefix, slip44, + authzSupport, denom, symbol, decimals, diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index c5ba81d31..ba918ea46 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -1,6 +1,7 @@ import _ from 'lodash' import QueryClient from './QueryClient.mjs' import SigningClient from './SigningClient.mjs' +import ApyClient from '../ApyClient.mjs' import Operator from './Operator.mjs' import Chain from './Chain.mjs' @@ -19,6 +20,8 @@ const Network = async (data, withoutQueryClient) => { return SigningClient(queryClient.rpcUrl, chain.chainId, gasPrice, wallet, key) } + const apyClient = queryClient && ApyClient(chain, queryClient.rpcUrl, queryClient.restUrl) + const getOperator = (operators, operatorAddress) => { return operators.find(elem => elem.address === operatorAddress) } @@ -48,6 +51,8 @@ const Network = async (data, withoutQueryClient) => { return { connected: queryClient && queryClient.connected, + enabled: data.enabled, + apyEnabled: data.apyEnabled, name: data.name, prettyName: chain.prettyName, chainId: chain.chainId, @@ -63,10 +68,11 @@ const Network = async (data, withoutQueryClient) => { restUrl: queryClient && queryClient.restUrl, rpcUrl: queryClient && queryClient.rpcUrl, operators: data.operators, - authzSupport: data.authzSupport, + authzSupport: chain.authzSupport, data, chain, queryClient, + getApy: apyClient && apyClient.getApy, signingClient, getValidators, getOperators, diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index d5a648d90..ff242a62c 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -3,6 +3,19 @@ import parse from 'parse-duration' const Operator = (data, validatorData) => { + function runsPerDay() { + const { runTime } = data + if(Array.isArray(runTime)){ + return runTime.length + }else{ + if(runTime.startsWith('every')){ + const interval = parse(runTime.replace('every ', '')) + return (1000 * 60 * 60 * 24) / interval + } + return 1 + } + } + function runTimes() { if(Array.isArray(data.runTime)) return data.runTime return [data.runTime] @@ -67,7 +80,8 @@ const Operator = (data, validatorData) => { nextRun, frequency, runTimes, - runTimesString + runTimesString, + runsPerDay } } diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index a8231577a..f8ebbfa29 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -1,31 +1,5 @@ import axios from "axios"; import _ from "lodash"; -import { - setupStakingExtension, - QueryClient as CosmjsQueryClient, - setupBankExtension, - setupDistributionExtension, - setupMintExtension, - setupGovExtension, -} from "@cosmjs/stargate"; -import { Tendermint34Client } from "@cosmjs/tendermint-rpc"; -import { Decimal } from "@cosmjs/math"; - -function parseCommissionRate(validator) { - return ( - parseInt(validator.commission.commission_rates.rate) / 1000000000000000000 - ); -} - -function duration(epochs, epochIdentifier) { - const epoch = epochs.find((epoch) => epoch.identifier === epochIdentifier); - if (!epoch) { - return 0; - } - - // Actually, the date type of golang protobuf is returned by the unit of seconds. - return parseInt(epoch.duration.replace("s", "")); -} const QueryClient = async (chainId, rpcUrls, restUrls) => { const rpcUrl = await findAvailableUrl( @@ -37,18 +11,6 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { "rest" ); - const makeClient = async () => { - const tmClient = await Tendermint34Client.connect(rpcUrl); - return CosmjsQueryClient.withExtensions( - tmClient, - setupStakingExtension, - setupBankExtension, - setupDistributionExtension, - setupMintExtension, - setupGovExtension - ); - }; - const getAllValidators = (pageSize, pageCallback) => { return getAllPages((nextKey) => { return getValidators(pageSize, nextKey); @@ -199,80 +161,6 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { return pages; }; - const getChainApr = async (denom) => { - const client = await makeClient(); - const pool = await client.staking.pool(); - const supply = await client.bank.supplyOf(denom); - const bondedTokens = pool.pool.bondedTokens; - const totalSupply = supply.amount; - if (chainId.startsWith("osmosis")) { - const apr = await osmosisApr(totalSupply, bondedTokens); - return apr; - } else if (chainId.startsWith("sifchain")) { - const aprRequest = await axios.get( - "https://data.sifchain.finance/beta/validator/stakingRewards" - ); - const apr = aprRequest.data.rate; - return apr; - } else { - const req = await client.mint.inflation(); - const baseInflation = req.toFloatApproximation(); - const ratio = bondedTokens / totalSupply; - const apr = baseInflation / ratio; - return apr; - } - }; - - const osmosisApr = async (totalSupply, bondedTokens) => { - const mintParams = await axios.get( - restUrl + "/osmosis/mint/v1beta1/params" - ); - const osmosisEpochs = await axios.get( - restUrl + "/osmosis/epochs/v1beta1/epochs" - ); - const epochProvisions = await axios.get( - restUrl + "/osmosis/mint/v1beta1/epoch_provisions" - ); - const { params } = mintParams.data; - const { epochs } = osmosisEpochs.data; - const { epoch_provisions } = epochProvisions.data; - const mintingEpochProvision = - parseFloat(params.distribution_proportions.staking) * epoch_provisions; - const epochDuration = duration(epochs, params.epoch_identifier); - const yearMintingProvision = - (mintingEpochProvision * (365 * 24 * 3600)) / epochDuration; - const baseInflation = yearMintingProvision / totalSupply; - const bondedRatio = bondedTokens / totalSupply; - const apr = baseInflation / bondedRatio; - console.log( - params, - epochs, - mintingEpochProvision, - epochDuration, - yearMintingProvision, - baseInflation, - bondedRatio, - apr, - "getting osmosis apr", - epochProvisions, - osmosisEpochs, - mintParams, - apr - ); - return apr; - }; - const getApy = async (validators, denom) => { - const periodPerYear = 365; - const chainApr = await getChainApr(denom); - let validatorApy = {}; - for (const [address, validator] of Object.entries(validators)) { - const realApr = chainApr * (1 - parseCommissionRate(validator)); - const apy = (1 + realApr / periodPerYear) ** periodPerYear - 1; - validatorApy[address] = apy; - } - return validatorApy; - }; - function findAvailableUrl(urls, type) { const path = type === "rest" ? "/blocks/latest" : "/block"; return Promise.any( @@ -295,7 +183,6 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { connected: !!rpcUrl && !!restUrl, rpcUrl, restUrl, - getApy, getAllValidators, getValidators, getAllValidatorDelegations, From bf7e719cf53cd6db45927423027ac82fdf5136e9 Mon Sep 17 00:00:00 2001 From: freemanhk <82752002+freemanhk@users.noreply.github.com> Date: Thu, 17 Mar 2022 18:12:16 +0800 Subject: [PATCH 176/862] Update networks.json --- src/networks.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..a13bf9130 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1489,6 +1489,12 @@ "runTime": "21:00", "minimumReward": 1000 }, + { + "address": "lumvaloper1v73py5myadey4cal2wh4nlk99h59jh52muuv55", + "botAddress": "lum1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97z52r7m0", + "runTime": "every 1 hour", + "minimumReward": 1000 + }, { "address": "lumvaloper1up3slj4nr5y23gxkvqn8h5ra9mv0pj2vv70zs4", "botAddress": "lum17xyeczwsax3st6aykyu4f28s5n3zg6r7w0wm7h", @@ -1868,6 +1874,12 @@ "runTime": "every 1 hour", "minimumReward": 100000 }, + { + "address": "bitsongvaloper1pqlxe507avzg2hq8ylcju037ywyh3myfsv9wln", + "botAddress": "bitsong1nyq65uezlhy9qpyfd4uqrtahhy70wtc6v4yzg8", + "runTime": "every 1 hour", + "minimumReward": 100000 + }, { "address": "bitsongvaloper1ech3swu5jeuenxzgd0rq9xz6yz3yn6t0v2x0tw", "botAddress": "bitsong1m52073hvnza73uwrf45avgdwljyqx7p04qh3yw", @@ -2395,4 +2407,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From b1c64c1910d22e34e6ea16c9b5ed12b5d7ad9fe6 Mon Sep 17 00:00:00 2001 From: coffeeroaster Date: Thu, 17 Mar 2022 08:12:41 -0400 Subject: [PATCH 177/862] add CRBRUS, Chihuahua for CafeValidator --- src/networks.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 00b4760ec..7d06471c5 100644 --- a/src/networks.json +++ b/src/networks.json @@ -690,6 +690,12 @@ "botAddress": "chihuahua1m8d834ykcy3dgrdve4xhxky3u3c2tl9d3d7gy9", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "chihuahuavaloper1y53zdszvt87fjg9hn50kg78850el26k0l9wxwl", + "botAddress": "chihuahua1cvj2yu8fmtr2my4a7y4r5rnt69flqvcvfxl4yy", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": false @@ -2370,6 +2376,12 @@ "botAddress": "cerberus1r3g7japkptsvt3gv52gvuudprtcue762m5ssan", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper13r2avh5y99w2duw4wpsh3vtx77j0s9h5zp2q3q", + "botAddress": "cerberus1cvj2yu8fmtr2my4a7y4r5rnt69flqvcvk5jjsd", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true @@ -2395,4 +2407,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 4fcd6bd42ab663f2912cb199871a16b582c1e8c4 Mon Sep 17 00:00:00 2001 From: poorbuthappy <97994242+poorbuthappy@users.noreply.github.com> Date: Thu, 17 Mar 2022 21:03:28 +0800 Subject: [PATCH 178/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index d12fc5eb9..e57df6869 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1257,7 +1257,7 @@ }, { "address": "desmosvaloper1axt95v5rpc97dtnl24mxtfj95gpxu59vn6xyvw", - "botAddress": "desmos1qfp2g2ve043h9538nvns6ggda7686m7zvyvz8t", + "botAddress": "desmos14a78h6202u4xg6phjnd8rmr5fq98zzdl3fa6vc", "runTime": "every 4 hour", "minimumReward": 10000 } From b4bb0a8de84f94822f98fdef451d9de83091a82e Mon Sep 17 00:00:00 2001 From: clemensgg <79083520+clemensgg@users.noreply.github.com> Date: Thu, 17 Mar 2022 14:36:24 +0100 Subject: [PATCH 179/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index d12fc5eb9..c0f50ed8b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2238,7 +2238,7 @@ { "address": "cerberusvaloper1lxz6ucd5qydhpwtpatn9wu83fc002v08tek6zh", "botAddress": "cerberus1305e92ng6a9pdh3ttc3ld459ckxupxt3tt620v", - "runTime": "every 5 minutes", + "runTime": "every 20 minutes", "minimumReward": 10000 }, { From ed63e7f52bc9f8d1c63d9968b8fe56f54acd3e2b Mon Sep 17 00:00:00 2001 From: freemanhk <82752002+freemanhk@users.noreply.github.com> Date: Thu, 17 Mar 2022 22:09:59 +0800 Subject: [PATCH 180/862] Update networks.json --- src/networks.json | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/networks.json b/src/networks.json index d12fc5eb9..3f5a74e45 100644 --- a/src/networks.json +++ b/src/networks.json @@ -241,6 +241,12 @@ "runTime": "21:00", "minimumReward": 10000 }, + { + "address": "junovaloper1gwd9l44t22e3nteu68akfgykuxu6lq6m6u786d", + "botAddress": "juno1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zhjavf8", + "runTime": "every 1 hour", + "minimumReward": 10000 + }, { "address": "junovaloper10y7ucn6jhjtakwchgpw32y0tgaku6yn255z7gm", "botAddress": "juno106n92ygqknaw5wcgl2e9v0duxxux6nttqd24vh", @@ -768,6 +774,12 @@ "runTime": "20:00", "minimumReward": 1000000 }, + { + "address": "gravityvaloper122mak6mduuzyww573jlpgv57qhk96aqgu7sd8g", + "botAddress": "gravity1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97z9sv0tn", + "runTime": "every 1 hour", + "minimumReward": 1000000 + }, { "address": "gravityvaloper1epfpvqsc34sfserdx8x4t3aszdkar3w684fwr6", "botAddress": "gravity1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjyt59pa", @@ -1020,6 +1032,12 @@ "runTime": "19:00", "minimumReward": 100000 }, + { + "address": "bcnavaloper1s43h79qx6j85whr2p6060wvxjuyq9yqdak39dj", + "botAddress": "bcna1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zmswkxf", + "runTime": "every 1 hour", + "minimumReward": 1000000 + }, { "address": "bcnavaloper12wskdt5tcc9yx5ajdx8xjjujuc0fgs5p54j6ug", "botAddress": "bcna1m5hgzum68rjf4c7zhezgkj8hlnmr0kghv3t2zz", @@ -1225,6 +1243,12 @@ "runTime": "21:00", "minimumReward": 10000 }, + { + "address": "desmosvaloper1d6xe3ldswgaurszrp3emspvhspvu7hxm4ty8mv", + "botAddress": "desmos17srygfu0ky47ctnjrx740c53v57vnerp2rdnh0", + "runTime": "every 1 hour", + "minimumReward": 100000 + }, { "address": "desmosvaloper19c6nnp6afxj82ehxtlejyvg6zh58050jcgpdj6", "botAddress": "desmos1m5hgzum68rjf4c7zhezgkj8hlnmr0kghzekmag", @@ -1414,6 +1438,12 @@ "runTime": "every 1 hour", "minimumReward": 100000 }, + { + "address": "sifvaloper1gstrmcv3tqrskdj7uqu4x4tkh8nfw4qp9f85pm", + "botAddress": "sif1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zya3pps", + "runTime": "every 1 hour", + "minimumReward": 1000000 + }, { "address": "sifvaloper1lnhxf6war6qlldemkqzp0t3g57hpe9a6nh3tyv", "botAddress": "sif194ytn50yhh67rdha8akhs7c6zulnz4n2v2x4rg", @@ -2050,6 +2080,12 @@ "runTime": "20:00", "minimumReward": 1000000 }, + { + "address": "kivaloper14932eu6d398f0ycqj6xtmy9xhaewzegkhz2eap", + "botAddress": "ki1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zsd0c20", + "runTime": "every 1 hour", + "minimumReward": 1000000 + }, { "address": "kivaloper1lykm0h574w0r3jm545hhlfnd8937a70022xlwl", "botAddress": "ki1v9vae554q55xa4pcfdm0p3kv8nxn50jktpkafa", From 5157306f2e038ff9ec868ac3679b677e31c58e99 Mon Sep 17 00:00:00 2001 From: azstake <75195495+nhhtrung@users.noreply.github.com> Date: Thu, 17 Mar 2022 22:02:57 +0700 Subject: [PATCH 181/862] Update networks.json --- src/networks.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/networks.json b/src/networks.json index d12fc5eb9..47b719bbf 100644 --- a/src/networks.json +++ b/src/networks.json @@ -779,6 +779,12 @@ "botAddress": "gravity1grfk7t0k42yxpwqhr7kt4te7j73m2a3vx40gyw", "runTime": "13:37", "minimumReward": 10000 + }, + { + "address": "gravityvaloper10m848yf5f66qytun249z9k56nvtvknwenp0dts", + "botAddress": "gravity1kxd2f28ugzwyv9twexw88kpajvgf94xgeghxtr", + "runTime": "every 1 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -1681,6 +1687,12 @@ "botAddress": "stars1r3g7japkptsvt3gv52gvuudprtcue762n08yrf", "runTime": "12:00", "minimumReward": 10000 + }, + { + "address": "starsvaloper1926sz228mm6yjlwye9hfxxh0hh4nm4aqfp29l7", + "botAddress": "stars1kxd2f28ugzwyv9twexw88kpajvgf94xgfyjr96", + "runTime": "every 1 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -1842,6 +1854,12 @@ "botAddress": "umee1mmg6pqqnu6ktl4yp0afwzz6g33w8kpw3wajxln", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "umeevaloper142xnwdgpmpzgsyckxwyd5c9ppmmvqa9k7c4fh8", + "botAddress": "umee1kxd2f28ugzwyv9twexw88kpajvgf94xg0wcp2e", + "runTime": "every 1 hour", + "minimumReward": 10000 } ], "authzSupport": true From 2eac5cea34bc4a38e5ca45ebda702de80f72b44e Mon Sep 17 00:00:00 2001 From: zdeadex <51711900+zdeadex@users.noreply.github.com> Date: Thu, 17 Mar 2022 16:26:21 +0100 Subject: [PATCH 182/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index d12fc5eb9..f27386122 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2468,6 +2468,12 @@ "botAddress": "secret194ytn50yhh67rdha8akhs7c6zulnz4n2tja23l", "runTime": "every 1 hour", "minimumReward": 100000 + }, + { + "address": "secretvaloper16w5hlcf389le2n60t32eqf43plp539ged9sruy", + "botAddress": "secret1mjq48r6435aewerpruwc8up3tz3rzan2wu27uf", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true From 761111c386cd86014dbb186c2c5f97864bc7c049 Mon Sep 17 00:00:00 2001 From: Jeffrey Ford Date: Thu, 17 Mar 2022 08:33:16 -0700 Subject: [PATCH 183/862] Add entropic.nodes to cerberus --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index d12fc5eb9..0c2f9e785 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2423,6 +2423,12 @@ "botAddress": "cerberus1cvj2yu8fmtr2my4a7y4r5rnt69flqvcvk5jjsd", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1kdqjzvznnufaykuwpu2glu9g33jshw8w2zxpsr", + "botAddress": "cerberus1w3pe97xn72djdhdvmk7wm3ylzht7z3kcdk7j0s", + "runTime": "every 15 minutes", + "minimumReward": 50000000 } ], "authzSupport": true From d1e5ddf7382f8ad36ce7795fc7fc403ad96886d7 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Thu, 17 Mar 2022 09:01:51 -0700 Subject: [PATCH 184/862] Update Lavender.Five Secret bot address --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index d12fc5eb9..5e640e175 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2459,7 +2459,7 @@ }, { "address": "secretvaloper1t5wtcuwjkdct9qkw2h6m48zu2hectpd6ulmekk", - "botAddress": "secret18zzklau04kf3qtwrnnzlf20xrxpx3a89dqu42m", + "botAddress": "secret12xpq68caw2aqdu70jm4k792g0v9prhrpzxjday", "runTime": "21:00", "minimumReward": 100000 }, From d42c2c1670a62352c3e773756f83a3c54541a3fe Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Thu, 17 Mar 2022 12:55:55 -0400 Subject: [PATCH 185/862] Fixed WhisperNode SCRT bot addy --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 76774a1a3..654dacc76 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2465,7 +2465,7 @@ "operators": [ { "address": "secretvaloper16k26akna7h295rfjx3278s7xusnt736vy437y8", - "botAddress": "secret1yqv7y5ynhas45hu59vhe3qvw0kmc8amtx7s7ur", + "botAddress": "secret1e44rluarkdw56dy2turnwjtvtg4wqvs0swpd2e", "runTime": "21:00", "minimumReward": 100000 }, From a4940278544f004da6a14127a939ee247930af73 Mon Sep 17 00:00:00 2001 From: windpowerstake <93733899+windpowerstake@users.noreply.github.com> Date: Thu, 17 Mar 2022 18:27:35 +0100 Subject: [PATCH 186/862] updating juno restake to every 12 hours windpowerstake --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 654dacc76..cef3b4445 100644 --- a/src/networks.json +++ b/src/networks.json @@ -292,7 +292,7 @@ { "address": "junovaloper1am80fauk3gnzcr76ylg0yju4f0chen5x6zmq7q", "botAddress": "juno1hpwxnnrtsc6zjx4glup77tfe5z54lmtqnygwzm", - "runTime": "10:30", + "runTime": "every 12 hours", "minimumReward": 10000 }, { From 05b3377090e1855f517aa6b8573c542c0e2abcec Mon Sep 17 00:00:00 2001 From: NvztCRK Date: Thu, 17 Mar 2022 20:53:24 +0300 Subject: [PATCH 187/862] Goatlabs V chihuahua configs are changed. --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 833296d9f..420e2ab7e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -541,8 +541,8 @@ { "address": "chihuahuavaloper1m4jcru5nsmtzgt4ha8fj4egau3w89cqzdrngnh", "botAddress": "chihuahua1uxwnhyjd2m5y6mpqsdg95y4ty0a5jtfrd9m05a", - "runTime": "19:00", - "minimumReward": 1000 + "runTime": "every 10 minutes", + "minimumReward": 10000000 }, { "address": "chihuahuavaloper1tzk3kzjy7fj4w37hm5v48c2t8vvgukzcqgkz8k", From 5cc054f4364aebd1c8c43c7059178ff98e528568 Mon Sep 17 00:00:00 2001 From: OrochiOrochi <91251550+OrochiOrochi@users.noreply.github.com> Date: Fri, 18 Mar 2022 01:03:12 +0700 Subject: [PATCH 188/862] Restake Cerberus: Update networks.json IONode for --- src/networks.json | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/networks.json b/src/networks.json index d157a33a6..3d127d38c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2273,14 +2273,12 @@ "runTime": "every 15 minutes", "minimumReward": 100000000 }, - -{ - "address": "cerberusvaloper1d0pk9sx7gfthuwmcepvuq9wcreqdr5cxmaj6et", - "botAddress": "cerberus1usypwpuwuvwmks4ymqz5s4csya00xttuqf4fcr", - "runTime": ["09:00", "21:00"], - "minimumReward": 100000000 -}, - + { + "address": "cerberusvaloper1d0pk9sx7gfthuwmcepvuq9wcreqdr5cxmaj6et", + "botAddress": "cerberus1usypwpuwuvwmks4ymqz5s4csya00xttuqf4fcr", + "runTime": "every 1 hour", + "minimumReward": 100000000 + }, { "address": "cerberusvaloper1m5dzr80ag23sqmw2cnzelj73xmsd62qtnswujc", "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", From 7d719c05db7bec8b5fe2d472fdb11c3d0eac1cb6 Mon Sep 17 00:00:00 2001 From: chillyvee Date: Thu, 17 Mar 2022 20:44:46 +0000 Subject: [PATCH 189/862] Add Chill Validation Secret --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 654dacc76..fa66c10b3 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2498,6 +2498,12 @@ "botAddress": "secret1mjq48r6435aewerpruwc8up3tz3rzan2wu27uf", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "secretvaloper14mwwdad00y7lwwmmk3yw2l2qhn6jzjpy28fmfw", + "botAddress": "secret13x5lkafjz2x7ue468cqc65ea6uajq7uctnadgl", + "runTime": "15:00", + "minimumReward": 100000 } ], "authzSupport": true From a88faef5a0938f68b48faeafb0a502fe7503e3d3 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 17 Mar 2022 20:49:48 +0000 Subject: [PATCH 190/862] Disable Kava APY --- src/networks.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/networks.json b/src/networks.json index 654dacc76..22413c4c0 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1130,6 +1130,7 @@ }, { "name": "kava", + "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/kava", "https://api.data.kava.io/" From 7750e30434631c99c1271072944957d1dddba9df Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 17 Mar 2022 20:50:26 +0000 Subject: [PATCH 191/862] Disable Evmos --- src/networks.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/networks.json b/src/networks.json index 22413c4c0..7279b4d60 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1301,6 +1301,7 @@ }, { "name": "evmos", + "enabled": false, "apyEnabled": false, "restUrl": [ "https://rest.cosmos.directory/evmos", From 56d6e22e8740e786af15ed419168350907cbbcf4 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 17 Mar 2022 20:52:55 +0000 Subject: [PATCH 192/862] Remove flakey Juno nodes --- src/networks.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/networks.json b/src/networks.json index 7279b4d60..d622d4cf0 100644 --- a/src/networks.json +++ b/src/networks.json @@ -180,12 +180,10 @@ "name": "juno", "apyEnabled": false, "restUrl": [ - "https://rest.cosmos.directory/juno", - "https://lcd-juno.itastakers.com" + "https://rest.cosmos.directory/juno" ], "rpcUrl": [ - "https://rpc.cosmos.directory/juno", - "https://rpc-juno.itastakers.com" + "https://rpc.cosmos.directory/juno" ], "gasPrice": "0.0025ujuno", "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", From fd008c0e9aedeee9db8ac5f52a65074b0ed70ddc Mon Sep 17 00:00:00 2001 From: AgoraNodes <98248458+AgoraNodes@users.noreply.github.com> Date: Thu, 17 Mar 2022 16:54:30 -0400 Subject: [PATCH 193/862] Update networks.json add AgoraNodes for Chihuahua, Cerberus and Konstellation --- src/networks.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/networks.json b/src/networks.json index 654dacc76..e2e44ed1c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -703,7 +703,14 @@ "botAddress": "chihuahua1cvj2yu8fmtr2my4a7y4r5rnt69flqvcvfxl4yy", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "chihuahuavaloper17h628d2wtlw6844nzv0ktnq9qtm5qm3pym0k44", + "botAddress": "chihuahua1uc877qk45e23hxxx364dxsp8rehu5addswfj06", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } + ], "authzSupport": false }, @@ -2135,7 +2142,14 @@ "botAddress": "darc1mjq48r6435aewerpruwc8up3tz3rzan2nc2xvz", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "darcvaloper17h628d2wtlw6844nzv0ktnq9qtm5qm3p6uxnx5", + "botAddress": "darc1uc877qk45e23hxxx364dxsp8rehu5addv6sdr0", + "runTime": "every 15 minutes", + "minimumReward": 100000 } + ], "authzSupport": false }, @@ -2447,6 +2461,12 @@ "botAddress": "cerberus1w3pe97xn72djdhdvmk7wm3ylzht7z3kcdk7j0s", "runTime": "every 15 minutes", "minimumReward": 50000000 + }, + { + "address": "cerberusvaloper1wmanxw8y2s4cauegx5lagq5gxgqzpuuccwascc", + "botAddress": "cerberus1uc877qk45e23hxxx364dxsp8rehu5add0uy4mn", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From 5bbe5b1fe84bdd1fe8478bc7bf24f3e9c6155b9b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 17 Mar 2022 20:53:04 +0000 Subject: [PATCH 194/862] Don't check APY if disabled --- src/components/Delegations.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index c55008099..e6269ff40 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -93,6 +93,8 @@ class Delegations extends React.Component { } async calculateApy() { + if(!this.props.network.apyEnabled) return + this.props.network.getApy( this.props.validators, this.props.operators From 0b95e01c40507564c360ffb48e141a6792a3304b Mon Sep 17 00:00:00 2001 From: Songhua Hu Date: Thu, 17 Mar 2022 14:33:30 -0700 Subject: [PATCH 195/862] Add polkachu's additional accounts: akash, bitcanna, chihuahua, comdex, kava, ki, konstellation, and sifchain --- src/networks.json | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/networks.json b/src/networks.json index 654dacc76..a4c9cd7d1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -533,6 +533,12 @@ "botAddress": "akash1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9j3tp00", "runTime": "20:00", "minimumReward": 1000000 + }, + { + "address": "akashvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pz5w0ry", + "botAddress": "akash1xrrfek3qpz4ja8ccrjklfy064asyqq9k7fmgd4", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": false @@ -703,6 +709,12 @@ "botAddress": "chihuahua1cvj2yu8fmtr2my4a7y4r5rnt69flqvcvfxl4yy", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "chihuahuavaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4p40qac2", + "botAddress": "chihuahua1xrrfek3qpz4ja8ccrjklfy064asyqq9ks8mp4d", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": false @@ -1043,6 +1055,12 @@ "botAddress": "bcna195mm9y35sekrjk73anw2az9lv9xs5mztqxl859", "runTime": "every 1hour", "minimumReward": 10000 + }, + { + "address": "bcnavaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pxqv0cj", + "botAddress": "bcna1xrrfek3qpz4ja8ccrjklfy064asyqq9kfzxwua", + "runTime": "every 1 hour", + "minimumReward": 10000000 } ], "authzSupport": false @@ -1166,6 +1184,12 @@ "botAddress": "kava1mjq48r6435aewerpruwc8up3tz3rzan2sv22hj", "runTime": "19:00", "minimumReward": 10000 + }, + { + "address": "kavavaloper125s8t5c6ypwee7ytun90lnhgpls2zl3vta43aj", + "botAddress": "kava103yfkaqzwl0me0znucd25lrn66hzh0zf4hrpek", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": false @@ -1455,6 +1479,12 @@ "botAddress": "sif1hl0jgxre5z0nkyjj07c5vfdy44yk44atuzd6qk", "runTime": "22:30", "minimumReward": 100000 + }, + { + "address": "sifvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pfj2j90", + "botAddress": "sif1xrrfek3qpz4ja8ccrjklfy064asyqq9kk0eemy", + "runTime": "every 1 hour", + "minimumReward": 10000000 } ], "authzSupport": false @@ -1773,6 +1803,12 @@ "botAddress": "comdex1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh3wefn8", "runTime": "every 1 hour", "minimumReward": 100000 + }, + { + "address": "comdexvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4p3447dz", + "botAddress": "comdex1xrrfek3qpz4ja8ccrjklfy064asyqq9k5a5ddc", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": false @@ -2079,6 +2115,12 @@ "botAddress": "ki1mjq48r6435aewerpruwc8up3tz3rzan2a50c9p", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "kivaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pq8jud7", + "botAddress": "ki1xrrfek3qpz4ja8ccrjklfy064asyqq9kzl8qsm", + "runTime": "every 1 hour", + "minimumReward": 5000000 } ], "authzSupport": false @@ -2135,6 +2177,12 @@ "botAddress": "darc1mjq48r6435aewerpruwc8up3tz3rzan2nc2xvz", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "darcvaloper1jt9w26mpxxjsk63mvd4m2ynj0af09csldy6sgw", + "botAddress": "darc1xrrfek3qpz4ja8ccrjklfy064asyqq9kvnz7ec", + "runTime": "every 1 hour", + "minimumReward": 10000000 } ], "authzSupport": false From 467978b98ca0e3fb39ddd7c099fecd9b8945d78d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 17 Mar 2022 23:50:40 +0000 Subject: [PATCH 196/862] Fix apyEnabled check --- src/components/Delegations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index e6269ff40..cdc937815 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -93,7 +93,7 @@ class Delegations extends React.Component { } async calculateApy() { - if(!this.props.network.apyEnabled) return + if(this.props.network.apyEnabled === false) return this.props.network.getApy( this.props.validators, @@ -391,7 +391,7 @@ class Delegations extends React.Component {
    )} - {this.props.network.data.apyEnabled !== false && ( + {this.props.network.apyEnabled !== false && ( {Object.keys(this.state.validatorApy).length > 0 ? this.state.validatorApy[validatorAddress] From 6fff17f0c7869c93ae08e592244c2b75ec5a12c4 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 17 Mar 2022 23:50:52 +0000 Subject: [PATCH 197/862] Remove non-cosmos directory URLs --- src/networks.json | 166 ++++++++++++++-------------------------------- 1 file changed, 49 insertions(+), 117 deletions(-) diff --git a/src/networks.json b/src/networks.json index d622d4cf0..25fc81706 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2,13 +2,10 @@ { "name": "osmosis", "restUrl": [ - "https://rest.cosmos.directory/osmosis", - "https://lcd-osmosis.blockapsis.com" + "https://rest.cosmos.directory/osmosis" ], "rpcUrl": [ - "https://rpc.cosmos.directory/osmosis", - "https://osmosis.validator.network", - "https://rpc-osmosis.blockapsis.com" + "https://rpc.cosmos.directory/osmosis" ], "gasPrice": "0uosmo", "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", @@ -365,13 +362,10 @@ { "name": "cosmoshub", "restUrl": [ - "https://rest.cosmos.directory/cosmoshub", - "https://lcd-cosmoshub.blockapsis.com" + "https://rest.cosmos.directory/cosmoshub" ], "rpcUrl": [ - "https://rpc.cosmos.directory/cosmoshub", - "https://cosmoshub.validator.network", - "https://rpc-cosmoshub.blockapsis.com" + "https://rpc.cosmos.directory/cosmoshub" ], "gasPrice": "0.0025uatom", "testAddress": "cosmos1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz3k8ynk", @@ -457,8 +451,7 @@ "https://rest.cosmos.directory/akash" ], "rpcUrl": [ - "https://rpc.cosmos.directory/akash", - "https://akash.technofractal.com:443" + "https://rpc.cosmos.directory/akash" ], "testAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", @@ -538,12 +531,10 @@ { "name": "chihuahua", "restUrl": [ - "https://rest.cosmos.directory/chihuahua", - "https://api.chihuahua.wtf/" + "https://rest.cosmos.directory/chihuahua" ], "rpcUrl": [ - "https://rpc.cosmos.directory/chihuahua", - "https://rpc.chihuahua.wtf" + "https://rpc.cosmos.directory/chihuahua" ], "gasPrice": "0.025uhuahua", "testAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", @@ -708,12 +699,10 @@ { "name": "gravitybridge", "restUrl": [ - "https://rest.cosmos.directory/gravitybridge", - "https://gravitychain.io:1317" + "https://rest.cosmos.directory/gravitybridge" ], "rpcUrl": [ - "https://rpc.cosmos.directory/gravitybridge", - "https://gravitychain.io:26657" + "https://rpc.cosmos.directory/gravitybridge" ], "testAddress": "gravity1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz4x4uk7", "gasPrice": "0.025ugraviton", @@ -861,12 +850,10 @@ { "name": "sentinel", "restUrl": [ - "https://rest.cosmos.directory/sentinel", - "https://lcd-sentinel.keplr.app" + "https://rest.cosmos.directory/sentinel" ], "rpcUrl": [ - "https://rpc.cosmos.directory/sentinel", - "https://rpc-sentinel.keplr.app" + "https://rpc.cosmos.directory/sentinel" ], "gasPrice": "0.02udvpn", "testAddress": "sent1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz2d3ahe", @@ -919,12 +906,10 @@ { "name": "dig", "restUrl": [ - "https://rest.cosmos.directory/dig", - "https://api-1-dig.notional.ventures" + "https://rest.cosmos.directory/dig" ], "rpcUrl": [ - "https://rpc.cosmos.directory/dig", - "https://rpc-1-dig.notional.ventures" + "https://rpc.cosmos.directory/dig" ], "gasPrice": "0.0025udig", "testAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", @@ -999,13 +984,10 @@ { "name": "bitcanna", "restUrl": [ - "https://rest.cosmos.directory/bitcanna", - "https://lcd.bitcanna.io" + "https://rest.cosmos.directory/bitcanna" ], "rpcUrl": [ - "https://rpc.cosmos.directory/bitcanna", - "https://rpc.bitcanna.io", - "https://rpc-bitcanna.itastakers.com" + "https://rpc.cosmos.directory/bitcanna" ], "gasPrice": "0.001ubcna", "operators": [ @@ -1049,14 +1031,10 @@ "name": "emoney", "apyEnabled": false, "restUrl": [ - "https://rest.cosmos.directory/emoney", - "https://lcd-emoney.keplr.app", - "https://emoney.validator.network/api/" + "https://rest.cosmos.directory/emoney" ], "rpcUrl": [ - "https://rpc.cosmos.directory/emoney", - "https://rpc-emoney.keplr.app", - "https://emoney.validator.network" + "https://rpc.cosmos.directory/emoney" ], "gasPrice": "0.08ungm", "operators": [ @@ -1130,12 +1108,10 @@ "name": "kava", "apyEnabled": false, "restUrl": [ - "https://rest.cosmos.directory/kava", - "https://api.data.kava.io/" + "https://rest.cosmos.directory/kava" ], "rpcUrl": [ - "https://rpc.cosmos.directory/kava", - "https://rpc.kava.io" + "https://rpc.cosmos.directory/kava" ], "gasPrice": "0.00008ukava", "operators": [ @@ -1173,12 +1149,10 @@ "name": "desmos", "apyEnabled": false, "restUrl": [ - "https://rest.cosmos.directory/desmos", - "https://api.mainnet.desmos.network" + "https://rest.cosmos.directory/desmos" ], "rpcUrl": [ - "https://rpc.cosmos.directory/desmos", - "https://rpc.mainnet.desmos.network" + "https://rpc.cosmos.directory/desmos" ], "gasPrice": "0.001udsm", "operators": [ @@ -1272,13 +1246,10 @@ { "name": "cryptoorgchain", "restUrl": [ - "https://rest.cosmos.directory/cryptoorgchain", - "https://lcd-crypto-org.keplr.app/" + "https://rest.cosmos.directory/cryptoorgchain" ], "rpcUrl": [ - "https://rpc.cosmos.directory/cryptoorgchain", - "https://mainnet.crypto.org", - "https://rpc-crypto-org.keplr.app" + "https://rpc.cosmos.directory/cryptoorgchain" ], "gasPrice": "0.025basecro", "operators": [ @@ -1302,12 +1273,10 @@ "enabled": false, "apyEnabled": false, "restUrl": [ - "https://rest.cosmos.directory/evmos", - "https://rest.bd.evmos.org:1317" + "https://rest.cosmos.directory/evmos" ], "rpcUrl": [ - "https://rpc.cosmos.directory/evmos", - "https://tendermint.bd.evmos.org:26657" + "https://rpc.cosmos.directory/evmos" ], "operators": [ { @@ -1406,12 +1375,10 @@ { "name": "sifchain", "restUrl": [ - "https://rest.cosmos.directory/sifchain", - "https://api.sifchain.finance" + "https://rest.cosmos.directory/sifchain" ], "rpcUrl": [ - "https://rpc.cosmos.directory/sifchain", - "https://rpc.sifchain.finance" + "https://rpc.cosmos.directory/sifchain" ], "operators": [ { @@ -1462,12 +1429,10 @@ { "name": "lumnetwork", "restUrl": [ - "https://rest.cosmos.directory/lumnetwork", - "https://node0.mainnet.lum.network/rest" + "https://rest.cosmos.directory/lumnetwork" ], "rpcUrl": [ - "https://rpc.cosmos.directory/lumnetwork", - "https://node0.mainnet.lum.network/rpc" + "https://rpc.cosmos.directory/lumnetwork" ], "operators": [ { @@ -1597,14 +1562,10 @@ "name": "stargaze", "apyEnabled": false, "restUrl": [ - "https://rest.cosmos.directory/stargaze", - "https://rest.stargaze-apis.com", - "https://api.stargaze.ezstaking.io" + "https://rest.cosmos.directory/stargaze" ], "rpcUrl": [ - "https://rpc.cosmos.directory/stargaze", - "https://rpc.stargaze-apis.com", - "https://rpc.stargaze.ezstaking.io" + "https://rpc.cosmos.directory/stargaze" ], "operators": [ { @@ -1701,14 +1662,10 @@ "name": "comdex", "apyEnabled": false, "restUrl": [ - "https://rest.cosmos.directory/comdex", - "https://api.comdex.audit.one/rest", - "https://rest.comdex.one" + "https://rest.cosmos.directory/comdex" ], "rpcUrl": [ - "https://rpc.cosmos.directory/comdex", - "https://api.comdex.audit.one/rpc/", - "https://rpc.comdex.one" + "https://rpc.cosmos.directory/comdex" ], "operators": [ { @@ -1780,12 +1737,10 @@ { "name": "cheqd", "restUrl": [ - "https://rest.cosmos.directory/cheqd", - "https://api.cheqd.net" + "https://rest.cosmos.directory/cheqd" ], "rpcUrl": [ - "https://rpc.cosmos.directory/cheqd", - "https://rpc.cheqd.net" + "https://rpc.cosmos.directory/cheqd" ], "gasPrice": "25ncheq", "operators": [ @@ -1813,22 +1768,10 @@ { "name": "umee", "restUrl": [ - "https://rest.cosmos.directory/umee", - "https://api.aphrodite.main.network.umee.cc", - "https://api.apollo.main.network.umee.cc", - "https://api.artemis.main.network.umee.cc", - "https://api.athena.main.network.umee.cc", - "https://api.beaked.main.network.umee.cc", - "https://api.bottlenose.main.network.umee.cc" + "https://rest.cosmos.directory/umee" ], "rpcUrl": [ - "https://rpc.cosmos.directory/umee", - "https://rpc.aphrodite.main.network.umee.cc", - "https://rpc.apollo.main.network.umee.cc", - "https://rpc.artemis.main.network.umee.cc", - "https://rpc.athena.main.network.umee.cc", - "https://rpc.beaked.main.network.umee.cc", - "https://rpc.bottlenose.main.network.umee.cc" + "https://rpc.cosmos.directory/umee" ], "operators": [ { @@ -1867,12 +1810,10 @@ { "name": "bitsong", "restUrl": [ - "https://rest.cosmos.directory/bitsong", - "https://lcd-bitsong.itastakers.com" + "https://rest.cosmos.directory/bitsong" ], "rpcUrl": [ - "https://rpc.cosmos.directory/bitsong", - "https://rpc-bitsong.itastakers.com" + "https://rpc.cosmos.directory/bitsong" ], "operators": [ { @@ -1941,12 +1882,10 @@ { "name": "persistence", "restUrl": [ - "https://rest.cosmos.directory/persistence", - "https://rest.core.persistence.one" + "https://rest.cosmos.directory/persistence" ], "rpcUrl": [ - "https://rpc.cosmos.directory/persistence", - "https://rpc.core.persistence.one" + "https://rpc.cosmos.directory/persistence" ], "operators": [ { @@ -2010,15 +1949,10 @@ { "name": "impacthub", "restUrl": [ - "https://rest.cosmos.directory/impacthub", - "https://lcd-impacthub.keplr.app", - "https://proxies.sifchain.finance/api/impacthub-3/rest", - "https://impacthub.ixo.world/rest/" + "https://rest.cosmos.directory/impacthub" ], "rpcUrl": [ - "https://rpc.cosmos.directory/impacthub", - "https://rpc-impacthub.keplr.app", - "https://proxies.sifchain.finance/api/impacthub-3/rpc" + "https://rpc.cosmos.directory/impacthub" ], "gasPrice": "0.025uixo", "operators": [ @@ -2086,12 +2020,10 @@ { "name": "sommelier", "restUrl": [ - "https://rest.cosmos.directory/sommelier", - "https://lcd-sommelier.keplr.app" + "https://rest.cosmos.directory/sommelier" ], "rpcUrl": [ - "https://rpc.cosmos.directory/sommelier", - "https://rpc-sommelier.keplr.app" + "https://rpc.cosmos.directory/sommelier" ], "operators": [ { @@ -2105,10 +2037,10 @@ { "name": "konstellation", "restUrl": [ - "https://node1.konstellation.tech:1318" + "https://rest.cosmos.directory/konstellation" ], "rpcUrl": [ - "https://node1.konstellation.tech:26657" + "https://rpc.cosmos.directory/konstellation" ], "image": "https://raw.githubusercontent.com/Konstellation/DARC_token/main/darctoken.svg", "operators": [ @@ -2142,10 +2074,10 @@ { "name": "fetchhub", "restUrl": [ - "https://rest-fetchhub.fetch.ai" + "https://rest.cosmos.directory/fetchhub" ], "rpcUrl": [ - "https://rpc-fetchhub.fetch.ai:443" + "https://rpc.cosmos.directory/fetchhub" ], "operators": [ { From f8dc74100f1019d557d77fc8a567d60699a11c24 Mon Sep 17 00:00:00 2001 From: poorbuthappy <97994242+poorbuthappy@users.noreply.github.com> Date: Fri, 18 Mar 2022 09:58:35 +0800 Subject: [PATCH 198/862] Update networks.json Remove LUM PoorButHappy Node , it seem crashed the scripts didn't run rest of it. --- src/networks.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/networks.json b/src/networks.json index e57df6869..b092a1291 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1577,12 +1577,6 @@ "botAddress": "lum195mm9y35sekrjk73anw2az9lv9xs5mzt0uj0fr", "runTime": "every 1 hour", "minimumReward": 10000 - }, - { - "address": "lumvaloper1lnqradz7whff07t60pzetvkhgqamt9xu9f2j6k", - "botAddress": "lum14a78h6202u4xg6phjnd8rmr5fq98zzdlsmdrw5", - "runTime": "every 4 hour", - "minimumReward": 10000 } ], "authzSupport": true From 4a7a05822300a1019a0cb3ce51a2e16fca1812c3 Mon Sep 17 00:00:00 2001 From: ardapda Date: Fri, 18 Mar 2022 03:20:09 +0100 Subject: [PATCH 199/862] Update networks.json Validator.run --- src/networks.json | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..b5cb3e2bc 100644 --- a/src/networks.json +++ b/src/networks.json @@ -355,6 +355,12 @@ "botAddress": "juno1ka6r58g5h27x6gyyjkzxtpdk95k2yurf4ge0fl", "runTime": "01:00", "minimumReward": 10000 + }, + { + "address": "junovaloper1fj75m0v53ezdd8mlknzgdg4l3hvwh0h20sg4a8", + "botAddress": "juno1zks5qjck40m827v2m83la5r3y6lkudw8nsa0y6", + "runTime": "08:00", + "minimumReward": 10000 } ], "authzSupport": true @@ -790,6 +796,12 @@ "botAddress": "gravity1kxd2f28ugzwyv9twexw88kpajvgf94xgeghxtr", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "gravityvaloper1xf869399xq4pnxsllzvnwdcra8ly3huj5cnwh5", + "botAddress": "gravity1zks5qjck40m827v2m83la5r3y6lkudw8pjvvxw", + "runTime": "08:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -822,6 +834,12 @@ "botAddress": "regen1hl0jgxre5z0nkyjj07c5vfdy44yk44atxafsee", "runTime": "22:30", "minimumReward": 2000000 + }, + { + "address": "regenvaloper1vnxgn4uwtr0ug8z3u07c26chxn5a95lggwxtzv", + "botAddress": "regen1zks5qjck40m827v2m83la5r3y6lkudw86q4g4z", + "runTime": "08:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1047,6 +1065,12 @@ "botAddress": "bcna1xrrfek3qpz4ja8ccrjklfy064asyqq9kfzxwua", "runTime": "every 1 hour", "minimumReward": 10000000 + }, + { + "address": "bcnavaloper1t5rvn85jhmul9rxn3qryhrd99fqcddkz5357rz", + "botAddress": "bcna1zks5qjck40m827v2m83la5r3y6lkudw8ljw4t5", + "runTime": "08:00", + "minimumReward": 10000000 } ], "authzSupport": false @@ -1269,6 +1293,12 @@ "botAddress": "desmos14a78h6202u4xg6phjnd8rmr5fq98zzdl3fa6vc", "runTime": "every 4 hour", "minimumReward": 10000 + }, + { + "address": "desmosvaloper1sx0gd08yfjl76knllyqs7vgc8mvcqg88uhspvd", + "botAddress": "desmos1yqx9sggaw3evt6pwxajf8lh0q7lxlp3yfyd6x4", + "runTime": "08:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1590,6 +1620,12 @@ "botAddress": "lum14a78h6202u4xg6phjnd8rmr5fq98zzdlsmdrw5", "runTime": "every 4 hour", "minimumReward": 10000 + }, + { + "address": "lumvaloper1g239l2skjnz3dszek5s5npquza5gvypdrv6w88", + "botAddress": "lum1zks5qjck40m827v2m83la5r3y6lkudw8sgrakj", + "runTime": "08:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1690,6 +1726,12 @@ "botAddress": "stars1kxd2f28ugzwyv9twexw88kpajvgf94xgfyjr96", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "starsvaloper162ty3nf7wd5tvqhsu486fexh832fyham0cvphg", + "botAddress": "stars1zks5qjck40m827v2m83la5r3y6lkudw837ffgh", + "runTime": "08:00", + "minimumReward": 1000000 } ], "authzSupport": true From a247e73b6f49c71d94659c433d8b086d28dceae5 Mon Sep 17 00:00:00 2001 From: nullmames <85548668+nullmames@users.noreply.github.com> Date: Fri, 18 Mar 2022 14:44:30 +1100 Subject: [PATCH 200/862] Update kingnodes runtimes --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..e0e022f4e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -221,7 +221,7 @@ { "address": "junovaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qdelfp7r", "botAddress": "juno1xs023g3yj2mavwn3cjclgsh8mlk0kgsjsvdd2e", - "runTime": "00:00", + "runTime": ["00:00","12:00"}, "minimumReward": 10000 }, { @@ -1616,7 +1616,7 @@ { "address": "starsvaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qd0jluj0", "botAddress": "stars1xs023g3yj2mavwn3cjclgsh8mlk0kgsjjzetx5", - "runTime": "00:00", + "runTime": ["00:00","12:00"], "minimumReward": 10000 }, { From 492cc93ee0eb9b9e6bddc9affbd75a4d73944d53 Mon Sep 17 00:00:00 2001 From: nullmames <85548668+nullmames@users.noreply.github.com> Date: Fri, 18 Mar 2022 14:45:27 +1100 Subject: [PATCH 201/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index e0e022f4e..76e595e1b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -221,7 +221,7 @@ { "address": "junovaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qdelfp7r", "botAddress": "juno1xs023g3yj2mavwn3cjclgsh8mlk0kgsjsvdd2e", - "runTime": ["00:00","12:00"}, + "runTime": ["00:00","12:00"], "minimumReward": 10000 }, { From 112712003e55e3d2a612c8bea65c619e66d58768 Mon Sep 17 00:00:00 2001 From: freemanhk <82752002+freemanhk@users.noreply.github.com> Date: Fri, 18 Mar 2022 11:48:33 +0800 Subject: [PATCH 202/862] Update networks.json --- src/networks.json | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..8d486eb6b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -236,6 +236,12 @@ "runTime": "21:00", "minimumReward": 10000 }, + { + "address": "junovaloper1gwd9l44t22e3nteu68akfgykuxu6lq6m6u786d", + "botAddress": "juno1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zhjavf8", + "runTime": "every 1 hour", + "minimumReward": 10000 + }, { "address": "junovaloper10y7ucn6jhjtakwchgpw32y0tgaku6yn255z7gm", "botAddress": "juno106n92ygqknaw5wcgl2e9v0duxxux6nttqd24vh", @@ -773,6 +779,12 @@ "runTime": "20:00", "minimumReward": 1000000 }, + { + "address": "gravityvaloper122mak6mduuzyww573jlpgv57qhk96aqgu7sd8g", + "botAddress": "gravity1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97z9sv0tn", + "runTime": "every 1 hour", + "minimumReward": 100000 + }, { "address": "gravityvaloper1epfpvqsc34sfserdx8x4t3aszdkar3w684fwr6", "botAddress": "gravity1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjyt59pa", @@ -1024,6 +1036,12 @@ "runTime": "19:00", "minimumReward": 100000 }, + { + "address": "bcnavaloper1s43h79qx6j85whr2p6060wvxjuyq9yqdak39dj", + "botAddress": "bcna1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zmswkxf", + "runTime": "every 1 hour", + "minimumReward": 100000 + }, { "address": "bcnavaloper12wskdt5tcc9yx5ajdx8xjjujuc0fgs5p54j6ug", "botAddress": "bcna1m5hgzum68rjf4c7zhezgkj8hlnmr0kghv3t2zz", @@ -1234,6 +1252,12 @@ "runTime": "21:00", "minimumReward": 10000 }, + { + "address": "desmosvaloper1d6xe3ldswgaurszrp3emspvhspvu7hxm4ty8mv", + "botAddress": "desmos1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97z4cn8er", + "runTime": "every 1 hour", + "minimumReward": 1000000 + }, { "address": "desmosvaloper19c6nnp6afxj82ehxtlejyvg6zh58050jcgpdj6", "botAddress": "desmos1m5hgzum68rjf4c7zhezgkj8hlnmr0kghzekmag", @@ -1417,6 +1441,12 @@ "runTime": "every 1 hour", "minimumReward": 100000 }, + { + "address": "sifvaloper1gstrmcv3tqrskdj7uqu4x4tkh8nfw4qp9f85pm", + "botAddress": "sif1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zya3pps", + "runTime": "every 1 hour", + "minimumReward": 100000000 + }, { "address": "sifvaloper1lnhxf6war6qlldemkqzp0t3g57hpe9a6nh3tyv", "botAddress": "sif194ytn50yhh67rdha8akhs7c6zulnz4n2v2x4rg", @@ -1902,7 +1932,7 @@ }, { "address": "bitsongvaloper1pqlxe507avzg2hq8ylcju037ywyh3myfsv9wln", - "botAddress": "bitsong1nyq65uezlhy9qpyfd4uqrtahhy70wtc6v4yzg8", + "botAddress": "bitsong1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zdfvlv5", "runTime": "every 1 hour", "minimumReward": 100000 }, @@ -2056,6 +2086,12 @@ "runTime": "21:00", "minimumReward": 100000 }, + { + "address": "kivaloper14932eu6d398f0ycqj6xtmy9xhaewzegkhz2eap", + "botAddress": "ki1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zsd0c20", + "runTime": "every 1 hour", + "minimumReward": 1000000 + } { "address": "kivaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pq8jud7", "botAddress": "ki1xrrfek3qpz4ja8ccrjklfy064asyqq9kzl8qsm", From 193903d34392ebb3211652c3b50da7985524bc2c Mon Sep 17 00:00:00 2001 From: zdeadex <51711900+zdeadex@users.noreply.github.com> Date: Fri, 18 Mar 2022 04:59:37 +0100 Subject: [PATCH 203/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..75719fbec 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1917,6 +1917,12 @@ "botAddress": "bitsong14a78h6202u4xg6phjnd8rmr5fq98zzdlfczze0", "runTime": "every 4 hour", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper18wf0w252jxk3kgl5vlst8ttat8xzfnvejuftk2", + "botAddress": "bitsong1mjq48r6435aewerpruwc8up3tz3rzan2qsvlr6", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true From c0d3d45dd615d9e59f5a467ee84894761a423e23 Mon Sep 17 00:00:00 2001 From: Songhua Hu Date: Thu, 17 Mar 2022 21:14:39 -0700 Subject: [PATCH 204/862] Add polkachu's evmos validator --- src/networks.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..1c0ff6b37 100644 --- a/src/networks.json +++ b/src/networks.json @@ -699,7 +699,7 @@ "runTime": "every 15 minutes", "minimumReward": 100000000 }, - { + { "address": "chihuahuavaloper17h628d2wtlw6844nzv0ktnq9qtm5qm3pym0k44", "botAddress": "chihuahua1uc877qk45e23hxxx364dxsp8rehu5addswfj06", "runTime": "every 15 minutes", @@ -1398,6 +1398,12 @@ "botAddress": "evmos13craf042e76qasxu6f8vl9ds7vtcajyxmr9asz", "runTime": "22:30", "minimumReward": 1000000 + }, + { + "address": "evmosvaloper125fkz3mq6qxxpkmphdl3ep92t0d3y969xmt8hz", + "botAddress": "evmos1spdlljxpvgzn52qky7hafsdqxpchpydqfg403n", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2116,7 +2122,7 @@ "runTime": "21:00", "minimumReward": 100000 }, - { + { "address": "darcvaloper17h628d2wtlw6844nzv0ktnq9qtm5qm3p6uxnx5", "botAddress": "darc1uc877qk45e23hxxx364dxsp8rehu5addv6sdr0", "runTime": "every 15 minutes", From e1351020641161f8b6bcbd5f6b7f34a314034269 Mon Sep 17 00:00:00 2001 From: MTnode <86652312+mt2721@users.noreply.github.com> Date: Fri, 18 Mar 2022 11:21:37 +0700 Subject: [PATCH 205/862] Add umee + gravitybridge + stargaze --- src/networks.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..d27a2bbef 100644 --- a/src/networks.json +++ b/src/networks.json @@ -790,6 +790,12 @@ "botAddress": "gravity1kxd2f28ugzwyv9twexw88kpajvgf94xgeghxtr", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "gravityvaloper1pv7ae4kvlyme7r443xl07e2mjuulqr3cs0v89w", + "botAddress": "gravity1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wqewxt0r", + "runTime": "every 1 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -1690,6 +1696,12 @@ "botAddress": "stars1kxd2f28ugzwyv9twexw88kpajvgf94xgfyjr96", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "starsvaloper1jnum75gr8jsn4xn9u48kc9ma5c4mrmvzsvad6j", + "botAddress": "stars1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wqfzrwp6", + "runTime": "every 1 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -1845,6 +1857,12 @@ "botAddress": "umee1kxd2f28ugzwyv9twexw88kpajvgf94xg0wcp2e", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "umeevaloper1sex685w68vvv563nsyv8nezawq4hhg7qjkyhzg", + "botAddress": "umee1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wq0gfvwe", + "runTime": "every 1 hour", + "minimumReward": 10000 } ], "authzSupport": true From 76f37862d4e5266bdf0e7030ee6c66875149c190 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 18 Mar 2022 14:19:06 +0800 Subject: [PATCH 206/862] Add UD validator --- src/networks.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..bfd24a15a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1269,6 +1269,12 @@ "botAddress": "desmos14a78h6202u4xg6phjnd8rmr5fq98zzdl3fa6vc", "runTime": "every 4 hour", "minimumReward": 10000 + }, + { + "address": "desmosvaloper1mzar2v3xh3at49t2azg9qnkuc9nfvyurqmatuv", + "botAddress": "desmos1g4zywrluj6ac0xpu6f6jqul52rv0am66r8qy5h", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true @@ -1590,6 +1596,12 @@ "botAddress": "lum14a78h6202u4xg6phjnd8rmr5fq98zzdlsmdrw5", "runTime": "every 4 hour", "minimumReward": 10000 + }, + { + "address": "lumvaloper1qckuz6ks3kgjjnp26gtw3ya3e2l7tdp4hcvyem", + "botAddress": "lum1g4zywrluj6ac0xpu6f6jqul52rv0am66z4sakm", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true @@ -1917,6 +1929,12 @@ "botAddress": "bitsong14a78h6202u4xg6phjnd8rmr5fq98zzdlfczze0", "runTime": "every 4 hour", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper1vtc6xlwpeuf9r5ee5hxq40hcllxcad9pnl99xn", + "botAddress": "bitsong1g4zywrluj6ac0xpu6f6jqul52rv0am66mklupq", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true From a9f14aeb22d6f3408c4077cc4fbc6b51a8f87d0d Mon Sep 17 00:00:00 2001 From: CatsNetwork Date: Fri, 18 Mar 2022 07:13:37 +0000 Subject: [PATCH 207/862] CATS - update networks.json --- src/networks.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..848014bd6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1188,7 +1188,7 @@ "operators": [ { "address": "desmosvaloper1xk2kv26fjd9zrg4l3qc7lwxv03w4ydeeppqkkp", - "botAddress": "desmos1dpcdg5qr033gdzlde5qqgd9ysssfcjhq6w9fjw", + "botAddress": "desmos1js340eq8082essggjtv8lf9df7qlnny8xuj9ul", "runTime": [ "09:00", "21:00" @@ -1860,7 +1860,7 @@ "operators": [ { "address": "bitsongvaloper10uv3t6yru5dryz2yy9em2pzmqezyhsp0gkkxd2", - "botAddress": "bitsong1xkst6jjmtrd7v9wahqazguxuy04msfuy5ljm09", + "botAddress": "bitsong1js340eq8082essggjtv8lf9df7qlnny87ddafg", "runTime": [ "09:00", "21:00" @@ -2182,7 +2182,7 @@ { "address": "cerberusvaloper1xkv494sduqkpadwesqlsp6069yepsj58axja5l", "botAddress": "cerberus1js340eq8082essggjtv8lf9df7qlnny8wrlu7v", - "runTime": "every 2 hour", + "runTime": "every 1 hour", "minimumReward": 100000000 }, { From 0441ce2fd8e20b70e304e2289134dfdd908ce65f Mon Sep 17 00:00:00 2001 From: Jason <33862521+JasonBonnici@users.noreply.github.com> Date: Fri, 18 Mar 2022 08:32:36 +0100 Subject: [PATCH 208/862] [bitponder] New BotAddress BitSong swapping address due to custom derivation path bug for BitSong --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..513e2db37 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1908,7 +1908,7 @@ }, { "address": "bitsongvaloper1ech3swu5jeuenxzgd0rq9xz6yz3yn6t0v2x0tw", - "botAddress": "bitsong1m52073hvnza73uwrf45avgdwljyqx7p04qh3yw", + "botAddress": "bitsong18fsnw6a3yrletj5rtkp2577yq8aptsdkuwuwcl", "runTime": "21:00", "minimumReward": 100000 }, From 812b3c775bdc241652a91125d6b19770beeac64f Mon Sep 17 00:00:00 2001 From: Jason <33862521+JasonBonnici@users.noreply.github.com> Date: Fri, 18 Mar 2022 08:41:31 +0100 Subject: [PATCH 209/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 513e2db37..bb8bfddd3 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1909,7 +1909,7 @@ { "address": "bitsongvaloper1ech3swu5jeuenxzgd0rq9xz6yz3yn6t0v2x0tw", "botAddress": "bitsong18fsnw6a3yrletj5rtkp2577yq8aptsdkuwuwcl", - "runTime": "21:00", + "runTime": "every 1 hour", "minimumReward": 100000 }, { From b11521e50abfb5201cf93369d0c9d408adfe4891 Mon Sep 17 00:00:00 2001 From: DerFredy Date: Fri, 18 Mar 2022 10:28:16 +0100 Subject: [PATCH 210/862] Add DragonStake to Evmos --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..7faa92fb9 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1398,6 +1398,12 @@ "botAddress": "evmos13craf042e76qasxu6f8vl9ds7vtcajyxmr9asz", "runTime": "22:30", "minimumReward": 1000000 + }, + { + "address": "evmosvaloper10qpycc2egucukw8afcz4us7xlxxmfwh6rscvjz", + "botAddress": "evmos1uzjr9euyy4dflxta40lar6w5jm8jdm8gpeszf0", + "runTime": "20:00", + "minimumReward": 1000000000000000 } ], "authzSupport": true From ed0ab1131f497f0b4d3f62654ad3c4822a8056e7 Mon Sep 17 00:00:00 2001 From: azstake <75195495+nhhtrung@users.noreply.github.com> Date: Fri, 18 Mar 2022 18:48:26 +0700 Subject: [PATCH 211/862] azstake update evmos --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..4cb199197 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1398,6 +1398,12 @@ "botAddress": "evmos13craf042e76qasxu6f8vl9ds7vtcajyxmr9asz", "runTime": "22:30", "minimumReward": 1000000 + }, + { + "address": "evmosvaloper1r4egnngxqudu7nsxa6uygcygn36j30v7w3luh6", + "botAddress": "evmos1czfankzgd7p2245kyszuse70jy7sjzmnz5qmul", + "runTime": "every 1 hour", + "minimumReward": 1000000000000000 } ], "authzSupport": true From 07fcd681918559b29dfa9525375adeb69783e549 Mon Sep 17 00:00:00 2001 From: Paul Lovette Date: Fri, 18 Mar 2022 10:22:35 -0700 Subject: [PATCH 212/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..63e9297ed 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1816,6 +1816,12 @@ "https://rpc.cosmos.directory/umee" ], "operators": [ + { + "address": "umeevaloper18s567a3dxf09q7erpcp8q20dpauugxwx8xsrcc", + "botAddress": "umee1upe9xslwkqmmd8m7f7nuyhedhjc6saqhy4pldd", + "runTime": "every 15 minutes", + "minimumReward": 100000 + }, { "address": "umeevaloper14vmuhgm04ffvzts9jxm2wy5d538g96ru9fsg5v", "botAddress": "umee1m5hgzum68rjf4c7zhezgkj8hlnmr0kghyhx5wz", From c0d77da18db19ea27e617d56279fb5827fc29b34 Mon Sep 17 00:00:00 2001 From: AxLSTU Date: Sat, 19 Mar 2022 00:24:45 +0300 Subject: [PATCH 213/862] Update networks.json Add Stakewolle.com --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 9ffd13c51..c098e3787 100644 --- a/src/networks.json +++ b/src/networks.json @@ -11,6 +11,12 @@ "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "operators": [ + { + "address": "osmovaloper122yaxffys6rmv03nwwkmn3rvr5skzxl9lry2a5", + "botAddress": "osmo1m7xlymnpxl2d4llu67v08e8xs67dj2fz0wqmqw", + "runTime": ["09:00", "21:00"], + "minimumReward": 10 + }, { "address": "osmovaloper17mggn4znyeyg25wd7498qxl7r2jhgue8td054x", "botAddress": "osmo1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5p5wwvc", From 91be4c4653392cd6c1ceed4d7109eb1d92a462b6 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 18 Mar 2022 23:29:01 +0000 Subject: [PATCH 214/862] Add timestamps --- scripts/autostake.mjs | 62 +++++++++++++++++++++---------------------- src/utils/Helpers.mjs | 4 +++ 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index b97c503b4..c545205aa 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -1,6 +1,6 @@ import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import Network from '../src/utils/Network.mjs' -import {mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' +import {timeStamp, mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' import { coin @@ -17,7 +17,7 @@ class Autostake { constructor(){ this.mnemonic = process.env.MNEMONIC if(!this.mnemonic){ - console.log('Please provide a MNEMONIC environment variable') + timeStamp('Please provide a MNEMONIC environment variable') process.exit() } } @@ -32,20 +32,20 @@ class Autostake { try { client = await this.getClient(data) } catch (error) { - return console.log('Failed to connect', error.message) + return timeStamp('Failed to connect', error.message) } - if(!client) return console.log('Skipping') + if(!client) return timeStamp('Skipping') - console.log('Using REST URL', client.network.restUrl) - console.log('Using RPC URL', client.network.rpcUrl) + timeStamp('Using REST URL', client.network.restUrl) + timeStamp('Using RPC URL', client.network.rpcUrl) - if(!data.overriden) console.log('You are using public nodes, script may fail with many delegations. Check the README to use your own') + if(!data.overriden) timeStamp('You are using public nodes, script may fail with many delegations. Check the README to use your own') - console.log('Running autostake') + timeStamp('Running autostake') await this.checkBalance(client) - console.log('Finding delegators...') + timeStamp('Finding delegators...') let delegations const addresses = await this.getDelegations(client).then(delegations => { return delegations.map(delegation => { @@ -55,16 +55,16 @@ class Autostake { }) }) - console.log("Checking", addresses.length, "delegators for grants...") + timeStamp("Checking", addresses.length, "delegators for grants...") let grantedAddresses = await this.getGrantedAddresses(client, addresses) - console.log("Found", grantedAddresses.length, "delegators with valid grants...") + timeStamp("Found", grantedAddresses.length, "delegators with valid grants...") let calls = _.compact(grantedAddresses).map(item => { return async () => { try { await this.autostake(client, item, [client.operator.address]) } catch (error) { - console.log(item, 'ERROR: Skipping this run -', error.message) + timeStamp(item, 'ERROR: Skipping this run -', error.message) } } }) @@ -84,16 +84,16 @@ class Autostake { const accounts = await wallet.getAccounts() const botAddress = accounts[0].address - console.log(network.prettyName, 'bot address is', botAddress) + timeStamp(network.prettyName, 'bot address is', botAddress) const operatorData = data.operators.find(el => el.botAddress === botAddress) - if(!operatorData) return console.log('Not an operator') - if(!network.authzSupport) return console.log('No Authz support') + if(!operatorData) return timeStamp('Not an operator') + if(!network.authzSupport) return timeStamp('No Authz support') network = await Network(data) - if(!network.rpcUrl) return console.log('Could not connect to RPC API') - if(!network.restUrl) return console.log('Could not connect to REST API') + if(!network.rpcUrl) return timeStamp('Could not connect to RPC API') + if(!network.restUrl) return timeStamp('Could not connect to REST API') const client = await network.signingClient(wallet) client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) @@ -114,14 +114,14 @@ class Autostake { return client.queryClient.getBalance(client.operator.botAddress, client.network.denom) .then( (balance) => { - console.log("Bot balance is", balance.amount, balance.denom) + timeStamp("Bot balance is", balance.amount, balance.denom) if(balance.amount < 1_000){ - console.log('Bot balance is too low') + timeStamp('Bot balance is too low') process.exit() } }, (error) => { - console.log("ERROR:", error.message || error) + timeStamp("ERROR:", error.message || error) process.exit() } ) @@ -129,9 +129,9 @@ class Autostake { getDelegations(client) { return client.queryClient.getAllValidatorDelegations(client.operator.address, 100, (pages) => { - console.log("...batch", pages.length) + timeStamp("...batch", pages.length) }).catch(error => { - console.log("ERROR:", error.message || error) + timeStamp("ERROR:", error.message || error) process.exit() }) } @@ -143,12 +143,12 @@ class Autostake { const validators = await this.getGrantValidators(client, item) return validators ? item : undefined } catch (error) { - console.log(item, 'Failed to get address') + timeStamp(item, 'Failed to get address') } } }) let grantedAddresses = await mapSync(grantCalls, 100, (batch, index) => { - console.log('...batch', index + 1) + timeStamp('...batch', index + 1) }) return _.compact(grantedAddresses.flat()) } @@ -160,7 +160,7 @@ class Autostake { if(result.claimGrant && result.stakeGrant){ const grantValidators = result.stakeGrant.authorization.allow_list.address if(!grantValidators.includes(client.operator.address)){ - console.log(delegatorAddress, "Not autostaking for this validator, skipping") + timeStamp(delegatorAddress, "Not autostaking for this validator, skipping") return } @@ -168,7 +168,7 @@ class Autostake { } }, (error) => { - console.log(delegatorAddress, "ERROR skipping this run:", error.message || error) + timeStamp(delegatorAddress, "ERROR skipping this run:", error.message || error) } ) } @@ -179,11 +179,11 @@ class Autostake { const perValidatorReward = parseInt(totalRewards / validators.length) if(perValidatorReward < client.operator.data.minimumReward){ - console.log(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') + timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') return } - console.log(address, "Autostaking", perValidatorReward, client.network.denom, validators.length > 1 ? "per validator" : '') + timeStamp(address, "Autostaking", perValidatorReward, client.network.denom, validators.length > 1 ? "per validator" : '') let messages = validators.map(el => { return this.buildRestakeMessage(address, el, perValidatorReward, client.network.denom) @@ -193,9 +193,9 @@ class Autostake { const memo = 'REStaked by ' + client.operator.moniker return client.signingClient.signAndBroadcast(client.operator.botAddress, [execMsg], undefined, memo).then((result) => { - console.log(address, "Successfully broadcasted"); + timeStamp(address, "Successfully broadcasted"); }, (error) => { - console.log(address, 'Failed to broadcast:', error.message) + timeStamp(address, 'Failed to broadcast:', error.message) // Skip on failure // process.exit() }) @@ -242,7 +242,7 @@ class Autostake { return total }, (error) => { - console.log(address, "ERROR skipping this run:", error.message || error) + timeStamp(address, "ERROR skipping this run:", error.message || error) return 0 } ) diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index e11a88e1d..28a92d7ca 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -1,5 +1,9 @@ import _ from 'lodash' +export function timeStamp(...args) { + console.log('[' + new Date().toISOString().substring(11, 23) + ']', ...args); +} + export function overrideNetworks(networks, overrides){ return networks.map(network => { let override = overrides[network.name] From b99c2301d638579d175873d0b0aa48369305a554 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 01:10:39 +0000 Subject: [PATCH 215/862] Send MsgExec txs in batches of 100 --- scripts/autostake.mjs | 69 +++++++++++++++++++++++++-------------- src/utils/QueryClient.mjs | 8 ++--- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index c545205aa..d9de9b642 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -46,7 +46,6 @@ class Autostake { await this.checkBalance(client) timeStamp('Finding delegators...') - let delegations const addresses = await this.getDelegations(client).then(delegations => { return delegations.map(delegation => { if(delegation.balance.amount === 0) return @@ -59,16 +58,9 @@ class Autostake { let grantedAddresses = await this.getGrantedAddresses(client, addresses) timeStamp("Found", grantedAddresses.length, "delegators with valid grants...") - let calls = _.compact(grantedAddresses).map(item => { - return async () => { - try { - await this.autostake(client, item, [client.operator.address]) - } catch (error) { - timeStamp(item, 'ERROR: Skipping this run -', error.message) - } - } - }) - await executeSync(calls, 1) + + let grantMessages = await this.getAutostakeMessages(client, grantedAddresses, [client.operator.address]) + await this.autostake(client, grantMessages) } }) await executeSync(calls, 1) @@ -147,14 +139,14 @@ class Autostake { } } }) - let grantedAddresses = await mapSync(grantCalls, 100, (batch, index) => { + let grantedAddresses = await mapSync(grantCalls, 50, (batch, index) => { timeStamp('...batch', index + 1) }) return _.compact(grantedAddresses.flat()) } getGrantValidators(client, delegatorAddress) { - return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress) + return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress, {timeout: 5000}) .then( (result) => { if(result.claimGrant && result.stakeGrant){ @@ -173,7 +165,23 @@ class Autostake { ) } - async autostake(client, address, validators){ + async getAutostakeMessages(client, addresses, validators){ + let calls = addresses.map(item => { + return async () => { + try { + return await this.getAutostakeMessage(client, item, validators) + } catch (error) { + timeStamp(item, 'Failed to get address') + } + } + }) + let messages = await mapSync(calls, 50, (batch, index) => { + // timeStamp('...batch', index + 1) + }) + return _.compact(messages.flat()) + } + + async getAutostakeMessage(client, address, validators){ const totalRewards = await this.totalRewards(client, address, validators) const perValidatorReward = parseInt(totalRewards / validators.length) @@ -183,22 +191,35 @@ class Autostake { return } - timeStamp(address, "Autostaking", perValidatorReward, client.network.denom, validators.length > 1 ? "per validator" : '') + timeStamp(address, "Can autostake", perValidatorReward, client.network.denom, validators.length > 1 ? "per validator" : '') let messages = validators.map(el => { return this.buildRestakeMessage(address, el, perValidatorReward, client.network.denom) }).flat() - let execMsg = this.buildExecMessage(client.operator.botAddress, messages) + return this.buildExecMessage(client.operator.botAddress, messages) + } - const memo = 'REStaked by ' + client.operator.moniker - return client.signingClient.signAndBroadcast(client.operator.botAddress, [execMsg], undefined, memo).then((result) => { - timeStamp(address, "Successfully broadcasted"); - }, (error) => { - timeStamp(address, 'Failed to broadcast:', error.message) - // Skip on failure - // process.exit() + async autostake(client, messages){ + let batchSize = 100 + let batches = _.chunk(_.compact(messages), batchSize) + timeStamp('Sending', messages.length, 'messages in', batches.length, 'batches of', batchSize) + let calls = batches.map((batch, index) => { + return async () => { + try { + timeStamp('...batch', index + 1) + const memo = 'REStaked by ' + client.operator.moniker + await client.signingClient.signAndBroadcast(client.operator.botAddress, batch, undefined, memo).then((result) => { + timeStamp("Successfully broadcasted"); + }, (error) => { + timeStamp('Failed to broadcast:', error.message) + }) + } catch (error) { + timeStamp('ERROR: Skipping batch:', error.message) + } + } }) + await executeSync(calls, 1) } buildExecMessage(botAddress, messages){ @@ -229,7 +250,7 @@ class Autostake { } totalRewards(client, address, validators){ - return client.queryClient.getRewards(address) + return client.queryClient.getRewards(address, {timeout: 5000}) .then( (rewards) => { const total = Object.values(rewards).reduce((sum, item) => { diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index f8ebbfa29..8bd816aaa 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -65,13 +65,13 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { }); }; - const getRewards = (address) => { + const getRewards = (address, opts) => { return axios .get( restUrl + "/cosmos/distribution/v1beta1/delegators/" + address + - "/rewards" + "/rewards", opts ) .then((res) => res.data) .then((result) => { @@ -83,13 +83,13 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { }); }; - const getGrants = (botAddress, address) => { + const getGrants = (botAddress, address, opts) => { const searchParams = new URLSearchParams(); searchParams.append("grantee", botAddress); searchParams.append("granter", address); // searchParams.append("msg_type_url", "/cosmos.staking.v1beta1.MsgDelegate"); return axios - .get(restUrl + "/cosmos/authz/v1beta1/grants?" + searchParams.toString()) + .get(restUrl + "/cosmos/authz/v1beta1/grants?" + searchParams.toString(), opts) .then((res) => res.data) .then((result) => { const claimGrant = result.grants.find((el) => { From b0563487a52710f619b312c84643d6950c284d4c Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 01:15:11 +0000 Subject: [PATCH 216/862] Add log on finish --- scripts/autostake.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index d9de9b642..b1a2d79da 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -61,6 +61,7 @@ class Autostake { let grantMessages = await this.getAutostakeMessages(client, grantedAddresses, [client.operator.address]) await this.autostake(client, grantMessages) + timeStamp(client.network.prettyName, "finished") } }) await executeSync(calls, 1) From a5277b8da423580bc274174a315d539a1afed555 Mon Sep 17 00:00:00 2001 From: El-Jay_Crypto Date: Sat, 19 Mar 2022 02:20:38 +0100 Subject: [PATCH 217/862] Add NewBalance node to networks.json --- src/networks.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/networks.json b/src/networks.json index 3d42d432d..788169f31 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1047,6 +1047,12 @@ "botAddress": "bcna1xrrfek3qpz4ja8ccrjklfy064asyqq9kfzxwua", "runTime": "every 1 hour", "minimumReward": 10000000 + }, + { + "address": "bcnavaloper1ttkg00prt6w9a9zsgh9ywvftmqm22n4z5xezma", + "botAddress": "bcna15enapnszjg5en949x326ae3deu35dmw5hjed0z", + "runTime": "every 30 minutes", + "minimumReward": 1000000 } ], "authzSupport": false @@ -1584,6 +1590,12 @@ "botAddress": "lum195mm9y35sekrjk73anw2az9lv9xs5mzt0uj0fr", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "lumvaloper1lnqradz7whff07t60pzetvkhgqamt9xu9f2j6k", + "botAddress": "lum14a78h6202u4xg6phjnd8rmr5fq98zzdlsmdrw5", + "runTime": "every 4 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -2439,6 +2451,12 @@ "botAddress": "cerberus1uc877qk45e23hxxx364dxsp8rehu5add0uy4mn", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "cerberusvaloper1n507lrafyk934tuyu903pxyrqedzencv2kvqu7", + "botAddress": "cerberus15enapnszjg5en949x326ae3deu35dmw539f9jm", + "runTime": "every 30 minutes", + "minimumReward": 10000000 } ], "authzSupport": true From 728c008464a926b9ec508043ce49d89e58c388a7 Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Fri, 18 Mar 2022 23:15:07 -0400 Subject: [PATCH 218/862] Updated Whisper + Infinity CRBRUS --- src/networks.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/networks.json b/src/networks.json index 4c734b786..0653b9f28 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2392,14 +2392,14 @@ { "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", "botAddress": "cerberus1h43hjfxlz8t6y5wewma63jz7melv46gjcaue9a", - "runTime": "every 15 minutes", - "minimumReward": 100000000 + "runTime": "every 3 minutes", + "minimumReward": 1000 }, { "address": "cerberusvaloper1g3d36rfxfqtlnz3hzd05cs2wrjgykcz2g649a8", "botAddress": "cerberus1e44rluarkdw56dy2turnwjtvtg4wqvs0wv4dzw", - "runTime": "every 15 minutes", - "minimumReward": 100000000 + "runTime": "every 3 minutes", + "minimumReward": 1000 }, { "address": "cerberusvaloper1td92z5qwl39f407gxf6lu2x80enat7t3jfefnf", @@ -2677,4 +2677,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From e782071f9c2fa7d70d1ac6ee4831af8a5f07a6cd Mon Sep 17 00:00:00 2001 From: Golden Ratio Staking <95374052+Golden-Ratio-Staking@users.noreply.github.com> Date: Fri, 18 Mar 2022 20:58:10 -0700 Subject: [PATCH 219/862] Update Min Reward to 100 for Golden Ratio --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 4c734b786..17fbe1d0c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2495,7 +2495,7 @@ "address": "cerberusvaloper13uz8x0rgm950639yklqq5tnjh6gwfpfhu67yyk", "botAddress": "cerberus106n92ygqknaw5wcgl2e9v0duxxux6ntt2cf87q", "runTime": "every 1 hour", - "minimumReward": 1000000 + "minimumReward": 100000000 }, { "address": "cerberusvaloper1j80fpcsumfkxypvydvtwtz3j4sdwr8c2x9fw5y", @@ -2677,4 +2677,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 14d9ae095fa3e43db554c68da545990e49022582 Mon Sep 17 00:00:00 2001 From: creeea Date: Sat, 19 Mar 2022 10:52:14 +0100 Subject: [PATCH 220/862] swiss staking operator --- src/networks.json | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/networks.json b/src/networks.json index 4c734b786..7da204ad1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -175,6 +175,12 @@ "botAddress": "osmo1mmg6pqqnu6ktl4yp0afwzz6g33w8kpw35sufdn", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "osmovaloper1y0us8xvsvfvqkk9c6nt5cfyu5au5tww24nrlnx", + "botAddress": "osmo1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv56exja", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": true @@ -376,7 +382,15 @@ "botAddress": "juno1zks5qjck40m827v2m83la5r3y6lkudw8nsa0y6", "runTime": "08:00", "minimumReward": 10000 + }, + + { + "address": "junovaloper1y0us8xvsvfvqkk9c6nt5cfyu5au5tww2wsdcwk", + "botAddress": "juno1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv2nfdrn", + "runTime": "21:00", + "minimumReward": 10000 } + ], "authzSupport": true }, @@ -462,7 +476,15 @@ "botAddress": "cosmos1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjqmxay4", "runTime": "every 1 hour", "minimumReward": 10000 + }, + + { + "address": "cosmosvaloper1y0us8xvsvfvqkk9c6nt5cfyu5au5tww2ztve7q", + "botAddress": "cosmos1rjvyczwh5akp2ylg06dr4s2vlx0jmdwvup2ky0", + "runTime": "21:00", + "minimumReward": 10000 } + ], "authzSupport": true }, @@ -829,6 +851,12 @@ "botAddress": "gravity1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wqewxt0r", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "gravityvaloper1z5nxs28a8krdhv6z6jw7gs5k6qr43xycr8wc6k", + "botAddress": "gravity1rjvyczwh5akp2ylg06dr4s2vlx0jmdwvc3cwp8", + "runTime": "21:00", + "minimumReward": 10000 } ], "authzSupport": true @@ -1497,6 +1525,13 @@ "botAddress": "evmos1czfankzgd7p2245kyszuse70jy7sjzmnz5qmul", "runTime": "every 1 hour", "minimumReward": 1000000000000000 + }, + + { + "address": "evmosvaloper1vw76ju5rwt9us7kz8tg42t6yfmq2jftydu74u6", + "botAddress": "evmos14q8vu02v3npvnrxkt7te05tx6ral53rx4hh80f", + "runTime": "21:00", + "minimumReward": 1000000000000000 } ], "authzSupport": true @@ -1989,7 +2024,15 @@ "botAddress": "umee1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wq0gfvwe", "runTime": "every 1 hour", "minimumReward": 10000 + }, + + { + "address": "umeevaloper19yy0u0ttam2p9k874vsh2u4tt7e6cmq8h2w9nv", + "botAddress": "umee1rjvyczwh5akp2ylg06dr4s2vlx0jmdwvwhhfqa", + "runTime": "21:00", + "minimumReward": 10000 } + ], "authzSupport": true }, From cf657c5e749c6243bcbfa822c7d561907eac1a6e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 11:51:15 +0000 Subject: [PATCH 221/862] Decrease autostake batch size --- scripts/autostake.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index b1a2d79da..06a98c3c8 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -202,7 +202,7 @@ class Autostake { } async autostake(client, messages){ - let batchSize = 100 + let batchSize = 50 let batches = _.chunk(_.compact(messages), batchSize) timeStamp('Sending', messages.length, 'messages in', batches.length, 'batches of', batchSize) let calls = batches.map((batch, index) => { From 33b64de493adb02598cdf5c33b5a43a4087bea97 Mon Sep 17 00:00:00 2001 From: Joe Abbey Date: Sat, 19 Mar 2022 08:20:48 -0400 Subject: [PATCH 222/862] Updating jabbey networks --- src/networks.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/networks.json b/src/networks.json index 4c734b786..1c97ce38b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -77,7 +77,7 @@ { "address": "osmovaloper1xwazl8ftks4gn00y5x3c47auquc62ssuh8af89", "botAddress": "osmo195mm9y35sekrjk73anw2az9lv9xs5mztjduk29", - "runTime": "every 1 hour", + "runTime": "17:30", "minimumReward": 10000 }, { @@ -1934,7 +1934,7 @@ "address": "cheqdvaloper1xwazl8ftks4gn00y5x3c47auquc62ssu5r5utr", "botAddress": "cheqd195mm9y35sekrjk73anw2az9lv9xs5mzt55rxhx", "runTime": "every 1 hour", - "minimumReward": 1000 + "minimumReward": 10000000 } ], "authzSupport": true @@ -2470,7 +2470,7 @@ { "address": "cerberusvaloper1xwazl8ftks4gn00y5x3c47auquc62ssuvav5f9", "botAddress": "cerberus195mm9y35sekrjk73anw2az9lv9xs5mztx300fu", - "runTime": "every 1 hour", + "runTime": "every 15 minutes", "minimumReward": 1000000 }, { @@ -2677,4 +2677,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 2c4958b8bea8ede07c04212b822f3380d2766bf9 Mon Sep 17 00:00:00 2001 From: fxpy <58055522+fxpy@users.noreply.github.com> Date: Sat, 19 Mar 2022 20:26:16 +0800 Subject: [PATCH 223/862] Add MMS Add MMS validator for Evmos, Gravity-Bridge, Konstellation, Umee --- src/networks.json | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 4c734b786..abb895d3f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -829,6 +829,12 @@ "botAddress": "gravity1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wqewxt0r", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "gravityvaloper14vaak0zdjtnvh92hw9ypwxj76093yrgzmuguar", + "botAddress": "gravity1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9m657na", + "runTime": "every 3 hours", + "minimumReward": 100000 } ], "authzSupport": true @@ -1497,6 +1503,12 @@ "botAddress": "evmos1czfankzgd7p2245kyszuse70jy7sjzmnz5qmul", "runTime": "every 1 hour", "minimumReward": 1000000000000000 + }, + { + "address": "evmosvaloper1rhddecqpwfjdkwmu6lmfzekh936kcu6wxs7jxa", + "botAddress": "evmos19mgc0j2m7pfzuc9lthqa6r24laxk094d3a979q", + "runTime": "every 3 hours", + "minimumReward": 1000000000000000 } ], "authzSupport": true @@ -1989,6 +2001,12 @@ "botAddress": "umee1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wq0gfvwe", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "umeevaloper1sm5a00zlr5wkhr2259shqk9lk3w307jazmf0e5", + "botAddress": "umee1m2g72wffh3l65sjlpxvsgmns3y7z58dyurdzyd", + "runTime": "every 3 hours", + "minimumReward": 50000 } ], "authzSupport": true @@ -2295,6 +2313,12 @@ "botAddress": "darc1xrrfek3qpz4ja8ccrjklfy064asyqq9kvnz7ec", "runTime": "every 1 hour", "minimumReward": 10000000 + }, + { + "address": "darcvaloper17w25mz7gvf8f84fevy3akzhaugjewp4dd8f5rj", + "botAddress": "darc1vkayq65xn0tguetzm6r2ejal7zmlwgemf8ncwq", + "runTime": "every 3 hours", + "minimumReward": 1000000 } ], "authzSupport": false @@ -2677,4 +2701,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From b409cde7de27ff807ed54d47f0636a95b0707aa1 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 12:30:12 +0000 Subject: [PATCH 224/862] Allow batch size override --- scripts/autostake.mjs | 2 +- src/networks.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 06a98c3c8..b2501b308 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -202,7 +202,7 @@ class Autostake { } async autostake(client, messages){ - let batchSize = 50 + let batchSize = client.network.data.autostake?.batchTxs || 50 let batches = _.chunk(_.compact(messages), batchSize) timeStamp('Sending', messages.length, 'messages in', batches.length, 'batches of', batchSize) let calls = batches.map((batch, index) => { diff --git a/src/networks.json b/src/networks.json index 4c734b786..9d1d061a0 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2344,6 +2344,9 @@ "https://rpc.cosmos.directory/cerberus" ], "gasPrice": "0.025ucrbrus", + "autostake": { + "batchTxs": 100 + }, "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ From 476d51534cd43d0e001a5556c6f20c7a0c25fd9f Mon Sep 17 00:00:00 2001 From: zdeadex <51711900+zdeadex@users.noreply.github.com> Date: Sat, 19 Mar 2022 14:07:11 +0100 Subject: [PATCH 225/862] Update networks.json --- src/networks.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/networks.json b/src/networks.json index 4c734b786..9e037e3a2 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2062,12 +2062,6 @@ "runTime": "every 4 hour", "minimumReward": 100000 }, - { - "address": "bitsongvaloper18wf0w252jxk3kgl5vlst8ttat8xzfnvejuftk2", - "botAddress": "bitsong1mjq48r6435aewerpruwc8up3tz3rzan2qsvlr6", - "runTime": "21:00", - "minimumReward": 100000 - }, { "address": "bitsongvaloper1vtc6xlwpeuf9r5ee5hxq40hcllxcad9pnl99xn", "botAddress": "bitsong1g4zywrluj6ac0xpu6f6jqul52rv0am66mklupq", @@ -2677,4 +2671,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 6c245ae522a64adc29afad17c0e29ffd2b0bbac5 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 13:33:09 +0000 Subject: [PATCH 226/862] Handle network error and skip --- scripts/autostake.mjs | 86 +++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index b2501b308..8d191b56e 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -42,32 +42,40 @@ class Autostake { if(!data.overriden) timeStamp('You are using public nodes, script may fail with many delegations. Check the README to use your own') - timeStamp('Running autostake') - await this.checkBalance(client) + try { + await this.runNetwork(client) + } catch (error) { + return timeStamp('Autostake failed, skipping network', error.message) + } + } + }) + await executeSync(calls, 1) + } - timeStamp('Finding delegators...') - const addresses = await this.getDelegations(client).then(delegations => { - return delegations.map(delegation => { - if(delegation.balance.amount === 0) return + async runNetwork(client){ + timeStamp('Running autostake') + await this.checkBalance(client) - return delegation.delegation.delegator_address - }) - }) + timeStamp('Finding delegators...') + const addresses = await this.getDelegations(client).then(delegations => { + return delegations.map(delegation => { + if (delegation.balance.amount === 0) return - timeStamp("Checking", addresses.length, "delegators for grants...") - let grantedAddresses = await this.getGrantedAddresses(client, addresses) + return delegation.delegation.delegator_address + }) + }) - timeStamp("Found", grantedAddresses.length, "delegators with valid grants...") + timeStamp("Checking", addresses.length, "delegators for grants...") + let grantedAddresses = await this.getGrantedAddresses(client, addresses) - let grantMessages = await this.getAutostakeMessages(client, grantedAddresses, [client.operator.address]) - await this.autostake(client, grantMessages) - timeStamp(client.network.prettyName, "finished") - } - }) - await executeSync(calls, 1) + timeStamp("Found", grantedAddresses.length, "delegators with valid grants...") + + let grantMessages = await this.getAutostakeMessages(client, grantedAddresses, [client.operator.address]) + await this.autostake(client, grantMessages) + timeStamp(client.network.prettyName, "finished") } - async getClient(data){ + async getClient(data) { let network = await Network(data, true) const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.mnemonic, { @@ -81,12 +89,12 @@ class Autostake { const operatorData = data.operators.find(el => el.botAddress === botAddress) - if(!operatorData) return timeStamp('Not an operator') - if(!network.authzSupport) return timeStamp('No Authz support') + if (!operatorData) return timeStamp('Not an operator') + if (!network.authzSupport) return timeStamp('No Authz support') network = await Network(data) - if(!network.rpcUrl) return timeStamp('Could not connect to RPC API') - if(!network.restUrl) return timeStamp('Could not connect to REST API') + if (!network.rpcUrl) return timeStamp('Could not connect to RPC API') + if (!network.restUrl) return timeStamp('Could not connect to REST API') const client = await network.signingClient(wallet) client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) @@ -95,7 +103,7 @@ class Autostake { const operators = network.getOperators(validators) const operator = network.getOperatorByBotAddress(operators, botAddress) - return{ + return { network: network, operator: operator, signingClient: client, @@ -108,7 +116,7 @@ class Autostake { .then( (balance) => { timeStamp("Bot balance is", balance.amount, balance.denom) - if(balance.amount < 1_000){ + if (balance.amount < 1_000) { timeStamp('Bot balance is too low') process.exit() } @@ -129,7 +137,7 @@ class Autostake { }) } - async getGrantedAddresses(client, addresses){ + async getGrantedAddresses(client, addresses) { let grantCalls = addresses.map(item => { return async () => { try { @@ -147,12 +155,12 @@ class Autostake { } getGrantValidators(client, delegatorAddress) { - return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress, {timeout: 5000}) + return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress, { timeout: 5000 }) .then( (result) => { - if(result.claimGrant && result.stakeGrant){ + if (result.claimGrant && result.stakeGrant) { const grantValidators = result.stakeGrant.authorization.allow_list.address - if(!grantValidators.includes(client.operator.address)){ + if (!grantValidators.includes(client.operator.address)) { timeStamp(delegatorAddress, "Not autostaking for this validator, skipping") return } @@ -166,7 +174,7 @@ class Autostake { ) } - async getAutostakeMessages(client, addresses, validators){ + async getAutostakeMessages(client, addresses, validators) { let calls = addresses.map(item => { return async () => { try { @@ -182,12 +190,12 @@ class Autostake { return _.compact(messages.flat()) } - async getAutostakeMessage(client, address, validators){ + async getAutostakeMessage(client, address, validators) { const totalRewards = await this.totalRewards(client, address, validators) const perValidatorReward = parseInt(totalRewards / validators.length) - if(perValidatorReward < client.operator.data.minimumReward){ + if (perValidatorReward < client.operator.data.minimumReward) { timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') return } @@ -201,7 +209,7 @@ class Autostake { return this.buildExecMessage(client.operator.botAddress, messages) } - async autostake(client, messages){ + async autostake(client, messages) { let batchSize = client.network.data.autostake?.batchTxs || 50 let batches = _.chunk(_.compact(messages), batchSize) timeStamp('Sending', messages.length, 'messages in', batches.length, 'batches of', batchSize) @@ -223,7 +231,7 @@ class Autostake { await executeSync(calls, 1) } - buildExecMessage(botAddress, messages){ + buildExecMessage(botAddress, messages) { return { typeUrl: "/cosmos.authz.v1beta1.MsgExec", value: { @@ -233,7 +241,7 @@ class Autostake { } } - buildRestakeMessage(address, validatorAddress, amount, denom){ + buildRestakeMessage(address, validatorAddress, amount, denom) { return [{ typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", value: MsgWithdrawDelegatorReward.encode(MsgWithdrawDelegatorReward.fromPartial({ @@ -250,13 +258,13 @@ class Autostake { }] } - totalRewards(client, address, validators){ - return client.queryClient.getRewards(address, {timeout: 5000}) + totalRewards(client, address, validators) { + return client.queryClient.getRewards(address, { timeout: 5000 }) .then( (rewards) => { const total = Object.values(rewards).reduce((sum, item) => { const reward = item.reward.find(el => el.denom === client.network.denom) - if(reward && validators.includes(item.validator_address)){ + if (reward && validators.includes(item.validator_address)) { return sum + parseInt(reward.amount) } return sum @@ -270,7 +278,7 @@ class Autostake { ) } - getNetworksData(){ + getNetworksData() { const networksData = fs.readFileSync('src/networks.json'); const networks = JSON.parse(networksData); try { From 35a78320e16c07a49729cc821f2c8426c5bd87c0 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 13:49:00 +0000 Subject: [PATCH 227/862] Allow custom slip44 path for autostaking --- README.md | 49 ++++++++++++++++++++++++++++++------------- scripts/autostake.mjs | 19 ++++++++++++++++- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index fa61e0e94..70ea82416 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Try it out at [restake.app](https://restake.app). ## How it works / Authz -Authz is a new feature for Tendermint chains which lets you grant permission to another wallet to carry out certain transactions for you. These transactions are sent by the grantee on behalf of the granter, meaning the validator will send and pay for the TX, but actions will affect your wallet (such as claiming rewards). +Authz is a new feature for Tendermint chains which lets you grant permission to another wallet to carry out certain transactions for you. These transactions are sent by the grantee on behalf of the granter, meaning the validator will send and pay for the TX, but actions will affect your wallet (such as claiming rewards). REStake specifically lets you grant a validator permission to send `WithdrawDelegatorReward` and `Delegate` transactions for their validator only (note `WithdrawDelegatorReward` is technically not restricted to a single validator). The validator cannot send any other transaction types, and has no other access to your wallet. You authorize this using Keplr as normal. @@ -30,11 +30,31 @@ Becoming an operator is extremely easy. You need to do three things: Generate a new hot wallet you will use to automatically carry out the staking transactions. The mnemonic will need to be provided to the script so **use a dedicated wallet and only keep enough funds for transaction fees**. The ONLY menmonic required here is for the hot wallet, do not put your validator operator mnemonic anywhere. -You only need a single mnemonic for multiple Cosmos chains, and the script will check each network in the [networks.json](./src/networks.json) file for a matching bot address. +You only need a single mnemonic for multiple Cosmos chains, and the script will check each network in the [networks.json](./src/networks.json) file for a matching bot address. + +#### Derivation Paths (IMPORTANT) + +Right now, the REStake autostaking script uses the standard 118 derivation path by default. Some networks prefer a different path and apps like Keplr will honour this. *The address the autostake script uses might not match Keplr*. + +As there are existing operators using the 118 path, operators will need to opt in to the correct path when they want to upgrade. *New operators should use the correct path before they get grants*. + +The correct path can be set in one of two ways, using a [config override](#overriding-networks-config-locallyuse-your-own-node) file. You should use `"correctSlip44": true` if possible. + +```json +{ + "desmos": { + "prettyName": "Desmos 852", + "autostake": { + "correctSlip44": true, // Use the correct slip44 path from chain-registry + "slip44": 852 // Set a specific slip44 path + } + } +} +``` ### Setup the autostaking script and run daily -You can run the autostaking script using `docker-compose` or using `npm` directly. In both cases you will need to provide your mnemonic in a `MNEMONIC` environment variable. +You can run the autostaking script using `docker-compose` or using `npm` directly. In both cases you will need to provide your mnemonic in a `MNEMONIC` environment variable. Instructions are provided for Docker Compose and will be expanded later. @@ -106,12 +126,12 @@ crontab -e Systemd-timer allow to run a one-off service with specified rules. -##### Create a systemd unit file: +##### Create a systemd unit file The unit file describe the application to run. We define a dependency with the timer with the `Wants` statement. ```bash -$ sudo vim /etc/systemd/system/restake.service +sudo vim /etc/systemd/system/restake.service ``` ```bash @@ -130,12 +150,12 @@ ExecStart=/usr/bin/docker-compose run --rm app npm run autostake WantedBy=multi-user.target ``` -##### Create a systemd timer file: +##### Create a systemd timer file -The timer file defines the rules for running the restake service every day. All rules are described in the [systemd documentation](https://www.freedesktop.org/software/systemd/man/systemd.timer.html). +The timer file defines the rules for running the restake service every day. All rules are described in the [systemd documentation](https://www.freedesktop.org/software/systemd/man/systemd.timer.html). ```bash -$ sudo vim /etc/systemd/system/restake.timer +sudo vim /etc/systemd/system/restake.timer ``` ```bash @@ -153,9 +173,9 @@ WantedBy=timers.target ##### Enable and start everything ```bash -$ systemctl enable restake.service -$ systemctl enable restake.timer -$ systemctl start restake.timer +systemctl enable restake.service +systemctl enable restake.timer +systemctl start restake.timer ``` ##### Check your timer @@ -215,7 +235,7 @@ You now need to update the [networks.json](./src/networks.json) file at `./src/n }, ``` -`address` is your validator's address, and `botAddress` is the address from your new hot wallet you generated earlier. +`address` is your validator's address, and `botAddress` is the address from your new hot wallet you generated earlier. `runTime` is the time *in UTC* that you intend to run your bot, and there are a few options. Pass a single time, e.g. `09:00` to specify a single run at 9am UTC. Use an array for multiple specified times, e.g. `["09:00", "21:00"]`. Use an interval string for multiple times per hour/day, e.g. `"every 15 minutes"`. @@ -267,9 +287,9 @@ Alternative run from source using `docker-compose up` or `npm start`. ## Ethos -The REStake UI is both validator and network agnostic. Any validator can be added as an operator and run this tool to provide an auto-compounding service to their delegators, but they can also run their own UI if they choose and adjust the branding to suit themselves. +The REStake UI is both validator and network agnostic. Any validator can be added as an operator and run this tool to provide an auto-compounding service to their delegators, but they can also run their own UI if they choose and adjust the branding to suit themselves. -For this to work, we need a common source of chain information, and a common source of 'operator' information. Chain information is sourced from the [Chain Registry](https://github.com/cosmos/chain-registry), via an API provided by [cosmos.directory](https://registry.cosmos.directory). Operator information currently lives in the [networks.json](./src/networks.json) file in this repository. +For this to work, we need a common source of chain information, and a common source of 'operator' information. Chain information is sourced from the [Chain Registry](https://github.com/cosmos/chain-registry), via an API provided by [cosmos.directory](https://registry.cosmos.directory). Operator information currently lives in the [networks.json](./src/networks.json) file in this repository. If you fork this repository to provide your own UI, please keep up to date with the upstream to ensure you have the latest [networks.json](./src/networks.json) to include all operators. Some honesty is needed until we have a more decentralised solution. @@ -280,4 +300,3 @@ The initial version of REStake was built quickly to take advantage of the new au ## ECO Stake 🌱 ECO Stake is a climate positive validator, but we care about the Cosmos ecosystem too. We built REStake to make it easy for all validators to run an autocompounder with Authz, and it's one of many projects we work on in the ecosystem. [Delegate with us](https://ecostake.com) to support more projects like this. - diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 8d191b56e..8ad66eb88 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -1,4 +1,5 @@ import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; +import { Slip10RawIndex, pathToString } from "@cosmjs/crypto"; import Network from '../src/utils/Network.mjs' import {timeStamp, mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' @@ -77,9 +78,25 @@ class Autostake { async getClient(data) { let network = await Network(data, true) + let slip44 + + if(network.data.autostake?.correctSlip44){ + slip44 = network.slip44 || 118 + }else{ + slip44 = network.data.autostake?.slip44 || 118 + } + let hdPath = [ + Slip10RawIndex.hardened(44), + Slip10RawIndex.hardened(slip44), + Slip10RawIndex.hardened(0), + Slip10RawIndex.normal(0), + Slip10RawIndex.normal(0), + ]; + slip44 != 118 && timeStamp('Using HD Path', pathToString(hdPath)) const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.mnemonic, { - prefix: network.prefix + prefix: network.prefix, + hdPaths: [hdPath] }); const accounts = await wallet.getAccounts() From d5bb4d4f8d2e6a6e9116c84500f2cc20ddc3c771 Mon Sep 17 00:00:00 2001 From: "FullStake.li" Date: Sat, 19 Mar 2022 15:00:17 +0100 Subject: [PATCH 228/862] Update Kleomedes Validators --- src/networks.json | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/networks.json b/src/networks.json index 4c734b786..375ff7542 100644 --- a/src/networks.json +++ b/src/networks.json @@ -221,8 +221,8 @@ { "address": "junovaloper1x8u2ypdr35802tjyjqyxan8x85fzxe6sk0qmh8", "botAddress": "juno1r8egcurpwxftegr07gjv9gwffw4fk009a4v3ll", - "runTime": "21:00", - "minimumReward": 10000 + "runTime": "every 1 hour", + "minimumReward": 20000 }, { "address": "junovaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qdelfp7r", @@ -582,7 +582,7 @@ { "address": "chihuahuavaloper1tzk3kzjy7fj4w37hm5v48c2t8vvgukzcqgkz8k", "botAddress": "chihuahua1r8egcurpwxftegr07gjv9gwffw4fk009gjzyep", - "runTime": "21:00", + "runTime": "every 15 minutes", "minimumReward": 1000 }, { @@ -1273,6 +1273,12 @@ "runTime": "every 1 hour", "minimumReward": 1000 }, + { + "address": "desmosvaloper1np3qcmwdju3rtc4a5mavfyflum5gqsljy2vucp", + "botAddress": "desmos1v80dwktqstytcnz5esa0hxxam3d83q4xfedj4d", + "runTime": "every 1 hour", + "minimumReward": 10000 + }, { "address": "desmosvaloper17ue85ck027c4grv7nuks7k7p4fqnlc55uqhskj", "botAddress": "desmos1z0xzvw999jh7ekce7eqnex2fj7cnn6xw6rhufu", @@ -1606,6 +1612,12 @@ "runTime": "23:00", "minimumReward": 1001 }, + { + "address": "lumvaloper16xvwljz2vqpsdzuskdlqt25e8gxmlpnfq3dcxh", + "botAddress": "lum1r8egcurpwxftegr07gjv9gwffw4fk0097djrdh", + "runTime": "every 1 hour", + "minimumReward": 1001 + }, { "address": "lumvaloper1txgtzgdj90mqddhyu5gcpkkdeu9g2fu22d85d7", "botAddress": "lum1e44rluarkdw56dy2turnwjtvtg4wqvs08pgdz3", @@ -2677,4 +2689,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From f2c038dd824fd6414060c7a9cb90c374f1b4c736 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 14:14:29 +0000 Subject: [PATCH 229/862] Deprecation warning and logging tweaks --- README.md | 6 ++++-- scripts/autostake.mjs | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 70ea82416..9ebdbc902 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Right now, the REStake autostaking script uses the standard 118 derivation path As there are existing operators using the 118 path, operators will need to opt in to the correct path when they want to upgrade. *New operators should use the correct path before they get grants*. -The correct path can be set in one of two ways, using a [config override](#overriding-networks-config-locallyuse-your-own-node) file. You should use `"correctSlip44": true` if possible. +The correct path can be set in one of two ways using a [config override](#overriding-networks-config-locallyuse-your-own-node) file. You should use `"correctSlip44": true` if possible. ```json { @@ -46,12 +46,14 @@ The correct path can be set in one of two ways, using a [config override](#overr "prettyName": "Desmos 852", "autostake": { "correctSlip44": true, // Use the correct slip44 path from chain-registry - "slip44": 852 // Set a specific slip44 path + "slip44": 852 // Alternatively set a specific slip44 path } } } ``` +In the future, `correctSlip44` will become the default and you will need to set `slip44` explicitely if you want to use the 118 path. + ### Setup the autostaking script and run daily You can run the autostaking script using `docker-compose` or using `npm` directly. In both cases you will need to provide your mnemonic in a `MNEMONIC` environment variable. diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 8ad66eb88..6db42e72e 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -80,6 +80,9 @@ class Autostake { let network = await Network(data, true) let slip44 + timeStamp('⚛') + timeStamp('Starting', network.prettyName) + if(network.data.autostake?.correctSlip44){ slip44 = network.slip44 || 118 }else{ @@ -102,7 +105,12 @@ class Autostake { const accounts = await wallet.getAccounts() const botAddress = accounts[0].address - timeStamp(network.prettyName, 'bot address is', botAddress) + timeStamp('Bot address is', botAddress) + + if (network.slip44 && network.slip44 !== slip44) { + timeStamp("!! You are not using the preferred derivation path !!") + timeStamp("!! You should switch to the correct path unless you have grants. Check the README !!") + } const operatorData = data.operators.find(el => el.botAddress === botAddress) @@ -121,8 +129,8 @@ class Autostake { const operator = network.getOperatorByBotAddress(operators, botAddress) return { - network: network, - operator: operator, + network, + operator, signingClient: client, queryClient: network.queryClient } @@ -229,7 +237,9 @@ class Autostake { async autostake(client, messages) { let batchSize = client.network.data.autostake?.batchTxs || 50 let batches = _.chunk(_.compact(messages), batchSize) - timeStamp('Sending', messages.length, 'messages in', batches.length, 'batches of', batchSize) + if(batches.length){ + timeStamp('Sending', messages.length, 'messages in', batches.length, 'batches of', batchSize) + } let calls = batches.map((batch, index) => { return async () => { try { From 9aeeba3d2c775892dab3597e9032c98b68c9ac95 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 14:16:42 +0000 Subject: [PATCH 230/862] Fix readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9ebdbc902..7c3a60c97 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,13 @@ You only need a single mnemonic for multiple Cosmos chains, and the script will #### Derivation Paths (IMPORTANT) -Right now, the REStake autostaking script uses the standard 118 derivation path by default. Some networks prefer a different path and apps like Keplr will honour this. *The address the autostake script uses might not match Keplr*. +Right now, the REStake autostaking script uses the standard 118 derivation path by default. Some networks prefer a different path and apps like Keplr will honour this. **The address the autostake script uses might not match Keplr**. -As there are existing operators using the 118 path, operators will need to opt in to the correct path when they want to upgrade. *New operators should use the correct path before they get grants*. +As there are existing operators using the 118 path, operators will need to opt in to the correct path when they want to upgrade. **New operators should use the correct path before they get grants**. The correct path can be set in one of two ways using a [config override](#overriding-networks-config-locallyuse-your-own-node) file. You should use `"correctSlip44": true` if possible. -```json +```jsonc { "desmos": { "prettyName": "Desmos 852", From 3fccce1027ab88c16fcb0149c3b3de98643fdea5 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 14:26:10 +0000 Subject: [PATCH 231/862] Expand example override further --- README.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7c3a60c97..e1bfe887e 100644 --- a/README.md +++ b/README.md @@ -209,18 +209,33 @@ Create a `src/networks.local.json` file with JSON in the following format: ```json { "osmosis": { - "prettyName": "Foobar", + "prettyName": "Osmosis with Fees", "restUrl": [ "https://rest.validator.com/osmosis" ], "rpcUrl": [ "https://rpc.validator.com/osmosis" - ] + ], + "gasPrice": "0.001uosmo", + "autostake": { + "batchTxs": 69 + } + }, + "desmos": { + "prettyName": "Desmos 118", + "autostake": { + "correctSlip44": true + } + }, + "cosmoshub": { + "enabled": false } } ``` -Any values you specify will override the `networks.json` file. Arrays will not be merged. The file is `.gitignore`'d so it won't affect updates. +Any values you specify will override the `networks.json` file. These are examples, you can override as much or little as you need. + +Arrays will be replaced and not merged. The file is `.gitignore`'d so it won't affect upstream updates. ## Submiting your operator From e9c31d3917c09901a1cfa803fcf5f5d4a3c6e6a2 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 19 Mar 2022 16:12:13 +0000 Subject: [PATCH 232/862] Ignore delegators with different withdraw address --- scripts/autostake.mjs | 10 +++++-- src/utils/QueryClient.mjs | 59 ++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 6db42e72e..eb154b34e 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -169,7 +169,7 @@ class Autostake { const validators = await this.getGrantValidators(client, item) return validators ? item : undefined } catch (error) { - timeStamp(item, 'Failed to get address') + timeStamp(item, 'Failed to get address', error.message) } } }) @@ -205,7 +205,7 @@ class Autostake { try { return await this.getAutostakeMessage(client, item, validators) } catch (error) { - timeStamp(item, 'Failed to get address') + timeStamp(item, 'Failed to get address', error.message) } } }) @@ -225,6 +225,12 @@ class Autostake { return } + const withdrawAddress = await client.queryClient.getWithdrawAddress(address, {timeout: 5000}) + if(withdrawAddress && withdrawAddress !== address){ + timeStamp(address, 'has a different withdraw address:', withdrawAddress) + return + } + timeStamp(address, "Can autostake", perValidatorReward, client.network.denom, validators.length > 1 ? "per validator" : '') let messages = validators.map(el => { diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 8bd816aaa..3f096d67d 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -40,6 +40,34 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { .then((res) => res.data); }; + const getAllValidatorDelegations = ( + validatorAddress, + pageSize, + pageCallback + ) => { + return getAllPages((nextKey) => { + return getValidatorDelegations(validatorAddress, pageSize, nextKey); + }, pageCallback).then((pages) => { + return pages.map((el) => el.delegation_responses).flat(); + }); + }; + + const getValidatorDelegations = (validatorAddress, pageSize, nextKey) => { + const searchParams = new URLSearchParams(); + if (pageSize) searchParams.append("pagination.limit", pageSize); + if (nextKey) searchParams.append("pagination.key", nextKey); + + return axios + .get( + restUrl + + "/cosmos/staking/v1beta1/validators/" + + validatorAddress + + "/delegations?" + + searchParams.toString() + ) + .then((res) => res.data); + }; + const getBalance = (address, denom) => { return axios .get(restUrl + "/cosmos/bank/v1beta1/balances/" + address) @@ -121,32 +149,18 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { }); }; - const getAllValidatorDelegations = ( - validatorAddress, - pageSize, - pageCallback - ) => { - return getAllPages((nextKey) => { - return getValidatorDelegations(validatorAddress, pageSize, nextKey); - }, pageCallback).then((pages) => { - return pages.map((el) => el.delegation_responses).flat(); - }); - }; - - const getValidatorDelegations = (validatorAddress, pageSize, nextKey) => { - const searchParams = new URLSearchParams(); - if (pageSize) searchParams.append("pagination.limit", pageSize); - if (nextKey) searchParams.append("pagination.key", nextKey); - + const getWithdrawAddress = (address, opts) => { return axios .get( restUrl + - "/cosmos/staking/v1beta1/validators/" + - validatorAddress + - "/delegations?" + - searchParams.toString() + "/cosmos/distribution/v1beta1/delegators/" + + address + + "/withdraw_address", opts ) - .then((res) => res.data); + .then((res) => res.data) + .then((result) => { + return result.withdraw_address + }); }; const getAllPages = async (getPage, pageCallback) => { @@ -191,6 +205,7 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { getDelegations, getRewards, getGrants, + getWithdrawAddress }; }; From b4feab9be5ffc43130c599c0be396f1097b96b60 Mon Sep 17 00:00:00 2001 From: Xiphiar Date: Sat, 19 Mar 2022 18:22:08 -0400 Subject: [PATCH 233/862] add Trivium and MTC to secret operators --- src/networks.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..fa85182d7 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2745,6 +2745,18 @@ "botAddress": "secret13x5lkafjz2x7ue468cqc65ea6uajq7uctnadgl", "runTime": "15:00", "minimumReward": 100000 + }, + { + "address": "secretvaloper1ahawe276d250zpxt0xgpfg63ymmu63a0svuvgw", + "botAddress": "secret1cpzlkfep9z8wjthvk5csxfkmjhmqncsl963pqg", + "runTime": "21:00", + "minimumReward": 100000 + }, + { + "address": "secretvaloper17m3c795fz4f36zjgjhr3vkf7e9pn3xxuryeww9", + "botAddress": "secret14hqn2mr82kyacele3nmaw8hmfczfpy37kjpqun", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true From b0a7e6538aef01d0de1fa8d983a5c1d39975a705 Mon Sep 17 00:00:00 2001 From: CatsNetwork Date: Sun, 20 Mar 2022 05:41:16 +0000 Subject: [PATCH 234/862] CATS | networks.json update --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..81af16c90 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2422,8 +2422,8 @@ { "address": "cerberusvaloper1xkv494sduqkpadwesqlsp6069yepsj58axja5l", "botAddress": "cerberus1js340eq8082essggjtv8lf9df7qlnny8wrlu7v", - "runTime": "every 1 hour", - "minimumReward": 100000000 + "runTime": "every 5 minutes", + "minimumReward": 50000000 }, { "address": "cerberusvaloper1ga0xxzst2hjyjtfqnffz2mapn8vk8ufysqsn30", From e4df9fc0d7ba89e143b8a223718b3a8d28390fd3 Mon Sep 17 00:00:00 2001 From: jjangg96 Date: Sun, 20 Mar 2022 15:13:00 +0900 Subject: [PATCH 235/862] add 0base.vc cerberus --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..e38024be4 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2688,6 +2688,12 @@ "botAddress": "cerberus15enapnszjg5en949x326ae3deu35dmw539f9jm", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper1pzqjgfd25qsyfdtmx9elrqx6zjjvnc9stqd7h0", + "botAddress": "cerberus1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyty62ml", + "runTime": "every 1 hour", + "minimumReward": 100000000 } ], "authzSupport": true From 94d57efa856b89e955a9673d27e49ac277c65916 Mon Sep 17 00:00:00 2001 From: Jason <33862521+JasonBonnici@users.noreply.github.com> Date: Sun, 20 Mar 2022 09:12:45 +0100 Subject: [PATCH 236/862] [bitponder] converge to correctSlip44 BotWallet address correction --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..7459dec07 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2121,7 +2121,7 @@ }, { "address": "bitsongvaloper1ech3swu5jeuenxzgd0rq9xz6yz3yn6t0v2x0tw", - "botAddress": "bitsong18fsnw6a3yrletj5rtkp2577yq8aptsdkuwuwcl", + "botAddress": "bitsong1m52073hvnza73uwrf45avgdwljyqx7p04qh3yw", "runTime": "every 1 hour", "minimumReward": 100000 }, From bddb4683b729cd0ea8e072612ab27e3e6a9b3314 Mon Sep 17 00:00:00 2001 From: nodestake Date: Sun, 20 Mar 2022 09:01:58 +0000 Subject: [PATCH 237/862] update NodeStake.top networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..a2ab27a5f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1992,6 +1992,12 @@ "botAddress": "cheqd195mm9y35sekrjk73anw2az9lv9xs5mzt55rxhx", "runTime": "every 1 hour", "minimumReward": 10000000 + }, + { + "address": "cheqdvaloper19f0w9svr905fhefusyx4z8sf83j6et0g3rjhkl", + "botAddress": "cheqd1etsdevgm5ps240lrw9mg3p2j32tj96ktqafpeu", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true From 6d8facb00267a76e5cf644a43513e53389ec36b3 Mon Sep 17 00:00:00 2001 From: h3rt <94856309+SecretSaturn@users.noreply.github.com> Date: Sun, 20 Mar 2022 10:57:14 +0100 Subject: [PATCH 238/862] Add Secret Saturn validator to Restake --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..fdfe6572e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2745,6 +2745,12 @@ "botAddress": "secret13x5lkafjz2x7ue468cqc65ea6uajq7uctnadgl", "runTime": "15:00", "minimumReward": 100000 + }, + { + "address": "secretvaloper1q0rth4fu4svxnw63vjd7w74nadzsdp0fmkhj3d", + "botAddress": "secret1zpmzxljkuzjqg20dvfwf9lnuk3pcxme5rkup7h", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true From 0f643825d9cf430a980496fa953d65df557fdd45 Mon Sep 17 00:00:00 2001 From: jjangg96 Date: Sun, 20 Mar 2022 20:51:38 +0900 Subject: [PATCH 239/862] add 0base.vc bots --- src/networks.json | 86 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index e38024be4..d3235b372 100644 --- a/src/networks.json +++ b/src/networks.json @@ -184,6 +184,12 @@ "botAddress": "osmo1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv56exja", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "osmovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4d3kvts", + "botAddress": "osmo1jrjdqzxchlt09gj6qvn4ds8suxh9aqwylcfncx", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -391,6 +397,12 @@ "botAddress": "juno1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv2nfdrn", "runTime": "21:00", "minimumReward": 10000 + }, + { + "address": "junovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4kjctkq", + "botAddress": "juno1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyp3ecfg", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -483,6 +495,12 @@ "botAddress": "cosmos1rjvyczwh5akp2ylg06dr4s2vlx0jmdwvup2ky0", "runTime": "21:00", "minimumReward": 10000 + }, + { + "address": "cosmosvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q46fe2xk", + "botAddress": "cosmos1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyhr6rw5", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -572,6 +590,12 @@ "botAddress": "akash1xrrfek3qpz4ja8ccrjklfy064asyqq9k7fmgd4", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "akashvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4cyw7r4", + "botAddress": "akash1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy6chyhw", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": false @@ -752,6 +776,12 @@ "botAddress": "chihuahua1xrrfek3qpz4ja8ccrjklfy064asyqq9ks8mp4d", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "chihuahuavaloper1pzqjgfd25qsyfdtmx9elrqx6zjjvnc9sj52r2y", + "botAddress": "chihuahua1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy5khd0k", + "runTime": "every 1 hour", + "minimumReward": 100000000 } ], "authzSupport": false @@ -862,6 +892,12 @@ "botAddress": "gravity1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9m657na", "runTime": "every 3 hours", "minimumReward": 100000 + }, + { + "address": "gravityvaloper1h4w306x3tm39gas6evu326c9ycxwrvk4drnv5t", + "botAddress": "gravity1jrjdqzxchlt09gj6qvn4ds8suxh9aqwynngmtu", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -900,6 +936,12 @@ "botAddress": "regen1zks5qjck40m827v2m83la5r3y6lkudw86q4g4z", "runTime": "08:00", "minimumReward": 1000000 + }, + { + "address": "regenvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4ucmxys", + "botAddress": "regen1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygp3lcs", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -939,6 +981,12 @@ "botAddress": "terra1hz2dutqm7e0t7q3h5cm9d8lchztskm8pekadus", "runTime": "17:28", "minimumReward": 50000 + }, + { + "address": "terravaloper12079m57cew2v02zs624zvyed5479an9wxh03fy", + "botAddress": "terra1knhcrmhcm4d6apcj4vglelax7sz4fm8759s6mj", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1548,6 +1596,12 @@ "botAddress": "evmos19mgc0j2m7pfzuc9lthqa6r24laxk094d3a979q", "runTime": "every 3 hours", "minimumReward": 1000000000000000 + }, + { + "address": "evmosvaloper1rh9uz23gmhr6q92a45qrath4fr9ffefz4eas0z", + "botAddress": "evmos1w2v6ztzta7hra7q3j3g6g363tqnh02th0mauj3", + "runTime": "every 1 hour", + "minimumReward": 1000000000000000000 } ], "authzSupport": true @@ -1614,6 +1668,12 @@ "botAddress": "sif1xrrfek3qpz4ja8ccrjklfy064asyqq9kk0eemy", "runTime": "every 1 hour", "minimumReward": 10000000 + }, + { + "address": "sifvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4nz2r97", + "botAddress": "sif1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyj744pl", + "runTime": "every 1 hour", + "minimumReward": 10000000 } ], "authzSupport": false @@ -1879,6 +1939,12 @@ "botAddress": "stars1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wqfzrwp6", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "starsvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4qlwk6v", + "botAddress": "stars1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyrld799", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2058,6 +2124,12 @@ "botAddress": "umee1m2g72wffh3l65sjlpxvsgmns3y7z58dyurdzyd", "runTime": "every 3 hours", "minimumReward": 50000 + }, + { + "address": "umeevaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4d0h0la", + "botAddress": "umee1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy948u2x", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2203,6 +2275,12 @@ "botAddress": "agoric1hl0jgxre5z0nkyjj07c5vfdy44yk44attzqnlt", "runTime": "22:30", "minimumReward": 100000 + }, + { + "address": "agoricvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4acufxj", + "botAddress": "agoric1cwe4fgnmvdzx6kefxs8alldtwhywctnxt7mdr5", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2234,6 +2312,12 @@ "botAddress": "ixo1mjq48r6435aewerpruwc8up3tz3rzan2nvq99x", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "ixovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q42frukn", + "botAddress": "ixo1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygky328", + "runTime": "every 1 hour", + "minimumReward": 100000 } ], "authzSupport": false @@ -2693,7 +2777,7 @@ "address": "cerberusvaloper1pzqjgfd25qsyfdtmx9elrqx6zjjvnc9stqd7h0", "botAddress": "cerberus1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyty62ml", "runTime": "every 1 hour", - "minimumReward": 100000000 + "minimumReward": 10000000 } ], "authzSupport": true From 00360af0918fe33c1dd7ed10a3320b1da29c09b6 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Sun, 20 Mar 2022 09:08:18 -0700 Subject: [PATCH 240/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..0d8c7dbc6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2724,7 +2724,7 @@ }, { "address": "secretvaloper1t5wtcuwjkdct9qkw2h6m48zu2hectpd6ulmekk", - "botAddress": "secret12xpq68caw2aqdu70jm4k792g0v9prhrpzxjday", + "botAddress": "secret18zzklau04kf3qtwrnnzlf20xrxpx3a89dqu42m", "runTime": "21:00", "minimumReward": 100000 }, From 71348e10cbec11834119bf72b9989eb1b7f5a58f Mon Sep 17 00:00:00 2001 From: AxLSTU Date: Sun, 20 Mar 2022 21:41:58 +0300 Subject: [PATCH 241/862] Update networks.json Add Stakewolle.com --- src/networks.json | 147 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..d457e9611 100644 --- a/src/networks.json +++ b/src/networks.json @@ -200,6 +200,15 @@ "gasPrice": "0.0025ujuno", "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", "operators": [ + { + "address": "junovaloper1qr78535yz60zemv82sww948c6azx40gnawnh6r", + "botAddress": "juno1m7xlymnpxl2d4llu67v08e8xs67dj2fz38ss3q", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "junovaloper1ty96k6wly7k9ualepv2hs258u29esn6mq02yd4", "botAddress": "juno15xa7vst3jca9qjkngmlysrdfur8gsex9y0qwal", @@ -406,6 +415,12 @@ "gasPrice": "0.0025uatom", "testAddress": "cosmos1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz3k8ynk", "operators": [ + { + "address": "cosmosvaloper1gf4wlkutql95j7wwsxz490s6fahlvk2s9xpwax", + "botAddress": "cosmos1m7xlymnpxl2d4llu67v08e8xs67dj2fz84ntku", + "runTime": "18:00", + "minimumReward": 1000 + }, { "address": "cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq", "botAddress": "cosmos1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5f0a762", @@ -498,6 +513,15 @@ "testAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", "operators": [ + { + "address": "akashvaloper1ksu368eh54vcqzruqk42dsnx88hvk8px09ws7q", + "botAddress": "akash1m7xlymnpxl2d4llu67v08e8xs67dj2fz2w7v0x", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "akashvaloper1strxz39h5fapp7zvn5mvf8xm0ea9aajqjstv6g", "botAddress": "akash194ytn50yhh67rdha8akhs7c6zulnz4n2yvyy4e", @@ -588,6 +612,15 @@ "testAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", "operators": [ + { + "address": "chihuahuavaloper1ju6mkyxpjsdcmrw0pnx6sd4zu4wywkmhtgnpr5", + "botAddress": "chihuahua1m7xlymnpxl2d4llu67v08e8xs67dj2fzyq79h7", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "chihuahuavaloper1wnu86qppv7gwtgz8xul5grs80s3vd6uqzr6jzq", "botAddress": "chihuahua15xa7vst3jca9qjkngmlysrdfur8gsex93gwmmp", @@ -767,6 +800,12 @@ "testAddress": "gravity1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz4x4uk7", "gasPrice": "0.025ugraviton", "operators": [ + { + "address": "gravityvaloper1vdvdl3krc9s4plnnvfk58k83lx5v38s6yse5ny", + "botAddress": "gravity1m7xlymnpxl2d4llu67v08e8xs67dj2fzr9pnn5", + "runTime": "23:00", + "minimumReward": 1000 + }, { "address": "gravityvaloper187reejg4et24jpggqt55vnc47l926w0p8w70mh", "botAddress": "gravity1v9vae554q55xa4pcfdm0p3kv8nxn50jk7u42gp", @@ -954,6 +993,12 @@ "gasPrice": "0.02udvpn", "testAddress": "sent1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz2d3ahe", "operators": [ + { + "address": "sentvaloper1hms0qpe27uulkllw96tmeqnl4hvxsf4vwp729v", + "botAddress": "sent1m7xlymnpxl2d4llu67v08e8xs67dj2fzuw9jjn", + "runTime": "21:00", + "minimumReward": 100000 + }, { "address": "sentvaloper138dhtmfslt0ls4ec0jumj8tyajry4dfuv27jay", "botAddress": "sent194ytn50yhh67rdha8akhs7c6zulnz4n2jvl6gv", @@ -1011,6 +1056,15 @@ "testAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", "ownerAddress": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", "operators": [ + { + "address": "digvaloper1vm6m78glgfjaepaxwcdg6py380q7yasds2k2t8", + "botAddress": "dig1m7xlymnpxl2d4llu67v08e8xs67dj2fzlp6q58", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", "botAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", @@ -1087,6 +1141,15 @@ ], "gasPrice": "0.001ubcna", "operators": [ + { + "address": "bcnavaloper1e8twck404wzkfm5sd064dl0ssse3haxau0nqf6", + "botAddress": "bcna1m7xlymnpxl2d4llu67v08e8xs67dj2fza9r27w", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "bcnavaloper1fghw3chlyavtdtlns22ysza77fwygtmrhwwf6m", "botAddress": "bcna15xa7vst3jca9qjkngmlysrdfur8gsex9gdn5j3", @@ -1158,6 +1221,15 @@ ], "gasPrice": "0.08ungm", "operators": [ + { + "address": "emoneyvaloper1039h52c70xlqwfqh29gmqq70zwwf8xrs3t9mps", + "botAddress": "emoney1m7xlymnpxl2d4llu67v08e8xs67dj2fzgkflpp", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 100000 + }, { "address": "emoneyvaloper1xpyajvwsrt6kg68g0uwvy00v9fa7v5f0ygljdp", "botAddress": "emoney15xa7vst3jca9qjkngmlysrdfur8gsex9a7epd7", @@ -1282,6 +1354,15 @@ ], "gasPrice": "0.001udsm", "operators": [ + { + "address": "desmosvaloper1zngdx77g9ywnwmwpwvj9w2eqcs6fhw78gn02d8", + "botAddress": "desmos1pu0pmlvakgdn7lzrwx2rgj6xjvcyln0l4cdfl2", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000000 + }, { "address": "desmosvaloper1xk2kv26fjd9zrg4l3qc7lwxv03w4ydeeppqkkp", "botAddress": "desmos1js340eq8082essggjtv8lf9df7qlnny8xuj9ul", @@ -1561,6 +1642,12 @@ "https://rpc.cosmos.directory/sifchain" ], "operators": [ + { + "address": "sifvaloper1ecv3sdx8h54ckv8q8u67cyk0nj37rffwpj7cdx", + "botAddress": "sif1m7xlymnpxl2d4llu67v08e8xs67dj2fzzguaeh", + "runTime": "21:00", + "minimumReward": 1000 + }, { "address": "sifvaloper1esnwgt6g6x2d3m37pw9lkgsxgzenc36n3a4n0f", "botAddress": "sif15xa7vst3jca9qjkngmlysrdfur8gsex9hqvr4g", @@ -1627,6 +1714,15 @@ "https://rpc.cosmos.directory/lumnetwork" ], "operators": [ + { + "address": "lumvaloper1mpywgfyk6ku9uwfw4t6xhn050cjluhmjck27nx", + "botAddress": "lum1m7xlymnpxl2d4llu67v08e8xs67dj2fzjlwzrg", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000000 + }, { "address": "lumvaloper1xkv494sduqkpadwesqlsp6069yepsj587dvf46", "botAddress": "lum1js340eq8082essggjtv8lf9df7qlnny88wzu7n", @@ -1893,6 +1989,15 @@ "https://rpc.cosmos.directory/comdex" ], "operators": [ + { + "address": "comdexvaloper15xevj3n6eq36t62sjjrsvf6hkuryg8w3pmpqy8", + "botAddress": "comdex1m7xlymnpxl2d4llu67v08e8xs67dj2fzq63f0t", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "comdexvaloper1flh26y7f2vsam4a6snwgrqaxkhe0g2yljstdqm", "botAddress": "comdex15xa7vst3jca9qjkngmlysrdfur8gsex94jphr5", @@ -1975,6 +2080,12 @@ ], "gasPrice": "25ncheq", "operators": [ + { + "address": "cheqdvaloper1ny8gd9tsyqhm56agp4vkpa6fldr2n4guthcvfj", + "botAddress": "cheqd1m7xlymnpxl2d4llu67v08e8xs67dj2fzfhltad", + "runTime": "21:00", + "minimumReward": 1000 + }, { "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", "botAddress": "cheqd1ypntyymvjg90ntxwes3hxtmuvvh4fcr8w082sd", @@ -2071,6 +2182,15 @@ "https://rpc.cosmos.directory/bitsong" ], "operators": [ + { + "address": "bitsongvaloper1pn6mhrwq8vdhns366jwxkggmhmxypuhrrc4huu", + "botAddress": "bitsong1vn2fc2cdt2r5kseptl86rstqudxly2c3jxw4x7", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "bitsongvaloper10uv3t6yru5dryz2yy9em2pzmqezyhsp0gkkxd2", "botAddress": "bitsong1js340eq8082essggjtv8lf9df7qlnny87ddafg", @@ -2149,6 +2269,12 @@ "https://rpc.cosmos.directory/persistence" ], "operators": [ + { + "address": "persistencevaloper1rxjmg3l0myaz6fzzvkum82lzyqz5acyxqqc08l", + "botAddress": "persistence1qzvhepyemfctq3wmc50f2ps06ldx3fnpsf03zp", + "runTime": "20:00", + "minimumReward": 10000 + }, { "address": "persistencevaloper1pdse5rr5njkkka6qeu5m8u704h6z67w5r700vn", "botAddress": "persistence1atgss2zpwl9m8fmm32gzrkjtqw3tdawguqw6tk", @@ -2248,6 +2374,15 @@ ], "gasPrice": "0.025uxki", "operators": [ + { + "address": "kivaloper19seaxuh9wp3zum42w6flrjsr5raptxhy3l8qvw", + "botAddress": "ki1m7xlymnpxl2d4llu67v08e8xs67dj2fzkczyjg", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "kivaloper183ak9rcvfazp5rtljve6v06tyue3dfrglvpfux", "botAddress": "ki15xa7vst3jca9qjkngmlysrdfur8gsex9rsj67h", @@ -2323,6 +2458,12 @@ ], "image": "https://raw.githubusercontent.com/Konstellation/DARC_token/main/darctoken.svg", "operators": [ + { + "address": "darcvaloper1txzc42vl5qel54tyuck8z8pfg979hwk867z30z", + "botAddress": "darc1m7xlymnpxl2d4llu67v08e8xs67dj2fzc586mt", + "runTime": "21:00", + "minimumReward": 100000 + }, { "address": "darcvaloper14kuh954mw4xp7gz5kvr3vtul0lpz5xt3tecpep", "botAddress": "darc194ytn50yhh67rdha8akhs7c6zulnz4n2kkajp5", @@ -2419,6 +2560,12 @@ "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ + { + "address": "cerberusvaloper1xjgspyv73d3k3ewygu0v2gcwwplwxkxg03reqy", + "botAddress": "cerberus1m7xlymnpxl2d4llu67v08e8xs67dj2fzmjnzrh", + "runTime": "21:00", + "minimumReward": 10000 + }, { "address": "cerberusvaloper1xkv494sduqkpadwesqlsp6069yepsj58axja5l", "botAddress": "cerberus1js340eq8082essggjtv8lf9df7qlnny8wrlu7v", From 8a006858dc640489ccbe0b1e527f818537c65043 Mon Sep 17 00:00:00 2001 From: LPX Date: Mon, 21 Mar 2022 05:01:32 +0900 Subject: [PATCH 242/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..13fac0216 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2688,6 +2688,12 @@ "botAddress": "cerberus15enapnszjg5en949x326ae3deu35dmw539f9jm", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper1jkf678dyg8k6ewkfu6xwvtl2sz68gfh3ys4s2h", + "botAddress": "cerberus1k4hh4mjvtrwq79vhmlrltdsyq08kec7j8ptm3s", + "runTime": "every 30 minutes", + "minimumReward": 10000000 } ], "authzSupport": true From c06dc6b9f7f73f52e31eb3c63473e91b5293bee3 Mon Sep 17 00:00:00 2001 From: LPX Date: Mon, 21 Mar 2022 05:05:31 +0900 Subject: [PATCH 243/862] Update networks.json --- src/networks.json | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..20d030792 100644 --- a/src/networks.json +++ b/src/networks.json @@ -184,6 +184,12 @@ "botAddress": "osmo1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv56exja", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "osmovaloper1vkfmegxrsveefn2wmudh7apxuzu2n77654ad62", + "botAddress": "osmo1k4hh4mjvtrwq79vhmlrltdsyq08kec7jnaczjf", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": true @@ -752,6 +758,12 @@ "botAddress": "chihuahua1xrrfek3qpz4ja8ccrjklfy064asyqq9ks8mp4d", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "chihuahuavaloper1pqsdyhefm99ag3a45rkqnc5907yksqvz0tqpzg", + "botAddress": "chihuahua1k4hh4mjvtrwq79vhmlrltdsyq08kec7jcnxu9e", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": false @@ -1764,6 +1776,12 @@ "botAddress": "lum14a78h6202u4xg6phjnd8rmr5fq98zzdlsmdrw5", "runTime": "every 4 hour", "minimumReward": 10000 + }, + { + "address": "lumvaloper1p4qz6gqr3esugvm9n49jl2rz0zhup7ww286t65", + "botAddress": "lum1k4hh4mjvtrwq79vhmlrltdsyq08kec7jwvkm30", + "runTime": "every 4 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -2688,6 +2706,12 @@ "botAddress": "cerberus15enapnszjg5en949x326ae3deu35dmw539f9jm", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper1jkf678dyg8k6ewkfu6xwvtl2sz68gfh3ys4s2h", + "botAddress": "cerberus1k4hh4mjvtrwq79vhmlrltdsyq08kec7j8ptm3s", + "runTime": "every 30 minutes", + "minimumReward": 10000000 } ], "authzSupport": true From 8c71bd12a29774fb8ae25559c0b191f1e8fff424 Mon Sep 17 00:00:00 2001 From: Eric Carlson Date: Sun, 20 Mar 2022 20:36:28 +0000 Subject: [PATCH 244/862] RHINO adding Cerberus for NOTs.Art --- src/networks.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..c89e5a546 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2688,6 +2688,11 @@ "botAddress": "cerberus15enapnszjg5en949x326ae3deu35dmw539f9jm", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + "address": "cerberusvaloper1tksh2f6fgwdpn2p73e8wvc2zkl9ey6e95vgvwd", + "botAddress": "cerberus1v4xvyv2yar2ytrj7d78dk9e253s9g7n07qyew3", + "runTime": "every 15 minutes", + "minimumReward": 10000000 } ], "authzSupport": true From 855c120d088138dad2c6d9484b44f94b811997f5 Mon Sep 17 00:00:00 2001 From: Eric Carlson Date: Sun, 20 Mar 2022 20:39:07 +0000 Subject: [PATCH 245/862] RHINO adding Cerberus for NOTs.Art, typo --- src/networks.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/networks.json b/src/networks.json index c89e5a546..9581be96a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2689,6 +2689,7 @@ "runTime": "every 30 minutes", "minimumReward": 10000000 }, + { "address": "cerberusvaloper1tksh2f6fgwdpn2p73e8wvc2zkl9ey6e95vgvwd", "botAddress": "cerberus1v4xvyv2yar2ytrj7d78dk9e253s9g7n07qyew3", "runTime": "every 15 minutes", From 14d0ca876c3a472f4c11928dfeece071646e4192 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 21 Mar 2022 09:25:37 +0800 Subject: [PATCH 246/862] update decentralizehk validator --- src/networks.json | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..1f559467a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -11,6 +11,12 @@ "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "operators": [ + { + "address": "osmovaloper1m73mgwn3cm2e8x9a9axa0kw8nqz8a492vg4hp4", + "botAddress": "osmo197yc3g8drkf8fyv6fr4ynddy2n22kauuhqkprd", + "runTime": "22:00", + "minimumReward": 50000 + }, { "address": "osmovaloper122yaxffys6rmv03nwwkmn3rvr5skzxl9lry2a5", "botAddress": "osmo1m7xlymnpxl2d4llu67v08e8xs67dj2fz0wqmqw", @@ -200,6 +206,12 @@ "gasPrice": "0.0025ujuno", "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", "operators": [ + { + "address": "junovaloper18eancryfzd7n0ag9xy3azqvxgkjla2mrs2uznx", + "botAddress": "juno197yc3g8drkf8fyv6fr4ynddy2n22kauuffx2jr", + "runTime": "22:00", + "minimumReward": 100000 + }, { "address": "junovaloper1ty96k6wly7k9ualepv2hs258u29esn6mq02yd4", "botAddress": "juno15xa7vst3jca9qjkngmlysrdfur8gsex9y0qwal", @@ -406,6 +418,12 @@ "gasPrice": "0.0025uatom", "testAddress": "cosmos1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz3k8ynk", "operators": [ + { + "address": "cosmosvaloper1m73mgwn3cm2e8x9a9axa0kw8nqz8a492ms63vn", + "botAddress": "cosmos197yc3g8drkf8fyv6fr4ynddy2n22kauulm934l", + "runTime": "22:00", + "minimumReward": 100000 + }, { "address": "cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq", "botAddress": "cosmos1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5f0a762", @@ -1627,6 +1645,12 @@ "https://rpc.cosmos.directory/lumnetwork" ], "operators": [ + { + "address": "lumvaloper1sgeam9kyphrzrcau8l5an729mtngxahte76zkn", + "botAddress": "lum197yc3g8drkf8fyv6fr4ynddy2n22kauu23ccqt", + "runTime": "22:00", + "minimumReward": 100000 + }, { "address": "lumvaloper1xkv494sduqkpadwesqlsp6069yepsj587dvf46", "botAddress": "lum1js340eq8082essggjtv8lf9df7qlnny88wzu7n", @@ -2136,6 +2160,12 @@ "botAddress": "bitsong1g4zywrluj6ac0xpu6f6jqul52rv0am66mklupq", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper1ltu429emv4tkn34n7795dxmeagl78ffqazwf9c", + "botAddress": "bitsong197yc3g8drkf8fyv6fr4ynddy2n22kauunjhehs", + "runTime": "22:00", + "minimumReward": 100000 } ], "authzSupport": true @@ -2688,7 +2718,13 @@ "botAddress": "cerberus15enapnszjg5en949x326ae3deu35dmw539f9jm", "runTime": "every 30 minutes", "minimumReward": 10000000 - } + }, + { + "address": "cerberusvaloper14vq30fa9mkw00vhnvmtlqxwwnxucl7may9ran2", + "botAddress": "cerberus197yc3g8drkf8fyv6fr4ynddy2n22kauuru9cq5", + "runTime": "22:00", + "minimumReward": 100000 + } ], "authzSupport": true }, From d75c6fd9f49a01506c4f43e86a1a352018894bbf Mon Sep 17 00:00:00 2001 From: CatsNetwork <87469541+CatsNetwork@users.noreply.github.com> Date: Mon, 21 Mar 2022 10:21:16 +0800 Subject: [PATCH 247/862] Update networks.json From 905b4067ff37b7574b980e155212ccfbd9f10342 Mon Sep 17 00:00:00 2001 From: Giancarlos Salas Date: Sun, 20 Mar 2022 21:54:57 -0600 Subject: [PATCH 248/862] Add disperze (juno) --- src/networks.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/networks.json b/src/networks.json index 2c9ea1659..27eebb5c5 100644 --- a/src/networks.json +++ b/src/networks.json @@ -391,6 +391,15 @@ "botAddress": "juno1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv2nfdrn", "runTime": "21:00", "minimumReward": 10000 + }, + { + "address": "junovaloper194v8uwee2fvs2s8fa5k7j03ktwc87h5ym39jfv", + "botAddress": "juno179w4ead6r2xf3uam3pp7kg55tsl5eycyta60qh", + "runTime": [ + "00:00", + "12:00" + ], + "minimumReward": 10000 } ], "authzSupport": true From b155e29e1416195bee7c37ffd4399cec279c2713 Mon Sep 17 00:00:00 2001 From: Gjermund Bjaanes Date: Mon, 21 Mar 2022 17:08:20 +0100 Subject: [PATCH 249/862] Refresh balance after claim, delegate and redelegate --- src/components/App.js | 2 ++ src/components/Delegations.js | 1 + src/components/Wallet.js | 1 + 3 files changed, 4 insertions(+) diff --git a/src/components/App.js b/src/components/App.js index c0ef0f1f1..ab56b1f13 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -31,6 +31,7 @@ class App extends React.Component { this.showNetworkSelect = this.showNetworkSelect.bind(this); this.getValidatorImage = this.getValidatorImage.bind(this); this.loadValidatorImages = this.loadValidatorImages.bind(this); + this.getBalance = this.getBalance.bind(this); } async componentDidMount() { @@ -300,6 +301,7 @@ class App extends React.Component { operators={this.props.operators} validators={this.props.validators} balance={this.state.balance} + onBalanceChange={this.getBalance} getValidatorImage={this.getValidatorImage} queryClient={this.state.queryClient} stargateClient={this.state.stargateClient} /> diff --git a/src/components/Delegations.js b/src/components/Delegations.js index cdc937815..48c85d617 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -196,6 +196,7 @@ class Delegations extends React.Component { onClaimRewards() { this.setState({ claimLoading: false, validatorLoading: {}, error: null }); + this.props.onBalanceChange(); setTimeout(() => { this.props.getDelegations(); this.getRewards(); diff --git a/src/components/Wallet.js b/src/components/Wallet.js index 710b81a9a..621f911f6 100644 --- a/src/components/Wallet.js +++ b/src/components/Wallet.js @@ -95,6 +95,7 @@ class Wallet extends React.Component { operators={this.props.operators} validators={this.props.validators} getValidatorImage={this.props.getValidatorImage} + onBalanceChange={this.props.onBalanceChange} delegations={this.state.delegations} queryClient={this.props.queryClient} stargateClient={this.props.stargateClient} From 552a9fc1f9fc3e902aaf61d0d5d03fbbe8c6528f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 21 Mar 2022 20:00:47 +0000 Subject: [PATCH 250/862] Match existing prop naming and add to refresh --- src/components/App.js | 2 +- src/components/Delegations.js | 3 ++- src/components/Wallet.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index ab56b1f13..5008ea413 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -301,7 +301,7 @@ class App extends React.Component { operators={this.props.operators} validators={this.props.validators} balance={this.state.balance} - onBalanceChange={this.getBalance} + getBalance={this.getBalance} getValidatorImage={this.getValidatorImage} queryClient={this.state.queryClient} stargateClient={this.state.stargateClient} /> diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 48c85d617..e2f18e782 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -69,6 +69,7 @@ class Delegations extends React.Component { refreshInterval() { const interval = setInterval(() => { + this.props.getBalance(); this.getRewards(true); }, 15_000); this.setState({ refreshInterval: interval }); @@ -196,8 +197,8 @@ class Delegations extends React.Component { onClaimRewards() { this.setState({ claimLoading: false, validatorLoading: {}, error: null }); - this.props.onBalanceChange(); setTimeout(() => { + this.props.getBalance(); this.props.getDelegations(); this.getRewards(); }, 6_000); diff --git a/src/components/Wallet.js b/src/components/Wallet.js index 621f911f6..350ea7ea2 100644 --- a/src/components/Wallet.js +++ b/src/components/Wallet.js @@ -95,7 +95,7 @@ class Wallet extends React.Component { operators={this.props.operators} validators={this.props.validators} getValidatorImage={this.props.getValidatorImage} - onBalanceChange={this.props.onBalanceChange} + getBalance={this.props.getBalance} delegations={this.state.delegations} queryClient={this.props.queryClient} stargateClient={this.props.stargateClient} From f15183d06344ad32f717a22155cf3d00211d8b54 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 21 Mar 2022 20:01:09 +0000 Subject: [PATCH 251/862] Clear balance when network changes --- src/components/App.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/App.js b/src/components/App.js index 5008ea413..39fd37061 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -57,6 +57,7 @@ class App extends React.Component { this.connect() } if(this.props.network !== prevProps.network){ + this.setState({ balance: undefined }) this.connect() await this.setNetwork() } From c113c6a7d74945261eb19304abb49cc862958328 Mon Sep 17 00:00:00 2001 From: Artemis Date: Mon, 21 Mar 2022 18:27:31 -0400 Subject: [PATCH 252/862] Update networks.json --- src/networks.json | 352 ++-------------------------------------------- 1 file changed, 11 insertions(+), 341 deletions(-) diff --git a/src/networks.json b/src/networks.json index 17874c43e..2b16d9137 100644 --- a/src/networks.json +++ b/src/networks.json @@ -11,12 +11,6 @@ "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "operators": [ - { - "address": "osmovaloper1m73mgwn3cm2e8x9a9axa0kw8nqz8a492vg4hp4", - "botAddress": "osmo197yc3g8drkf8fyv6fr4ynddy2n22kauuhqkprd", - "runTime": "22:00", - "minimumReward": 50000 - }, { "address": "osmovaloper122yaxffys6rmv03nwwkmn3rvr5skzxl9lry2a5", "botAddress": "osmo1m7xlymnpxl2d4llu67v08e8xs67dj2fz0wqmqw", @@ -190,18 +184,6 @@ "botAddress": "osmo1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv56exja", "runTime": "21:00", "minimumReward": 1000 - }, - { - "address": "osmovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4d3kvts", - "botAddress": "osmo1jrjdqzxchlt09gj6qvn4ds8suxh9aqwylcfncx", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "osmovaloper1vkfmegxrsveefn2wmudh7apxuzu2n77654ad62", - "botAddress": "osmo1k4hh4mjvtrwq79vhmlrltdsyq08kec7jnaczjf", - "runTime": "21:00", - "minimumReward": 1000 } ], "authzSupport": true @@ -218,21 +200,6 @@ "gasPrice": "0.0025ujuno", "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", "operators": [ - { - "address": "junovaloper1qr78535yz60zemv82sww948c6azx40gnawnh6r", - "botAddress": "juno1m7xlymnpxl2d4llu67v08e8xs67dj2fz38ss3q", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "junovaloper18eancryfzd7n0ag9xy3azqvxgkjla2mrs2uznx", - "botAddress": "juno197yc3g8drkf8fyv6fr4ynddy2n22kauuffx2jr", - "runTime": "22:00", - "minimumReward": 100000 - }, { "address": "junovaloper1ty96k6wly7k9ualepv2hs258u29esn6mq02yd4", "botAddress": "juno15xa7vst3jca9qjkngmlysrdfur8gsex9y0qwal", @@ -424,21 +391,6 @@ "botAddress": "juno1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv2nfdrn", "runTime": "21:00", "minimumReward": 10000 - }, - { - "address": "junovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4kjctkq", - "botAddress": "juno1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyp3ecfg", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "junovaloper194v8uwee2fvs2s8fa5k7j03ktwc87h5ym39jfv", - "botAddress": "juno179w4ead6r2xf3uam3pp7kg55tsl5eycyta60qh", - "runTime": [ - "00:00", - "12:00" - ], - "minimumReward": 10000 } ], "authzSupport": true @@ -454,18 +406,6 @@ "gasPrice": "0.0025uatom", "testAddress": "cosmos1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz3k8ynk", "operators": [ - { - "address": "cosmosvaloper1gf4wlkutql95j7wwsxz490s6fahlvk2s9xpwax", - "botAddress": "cosmos1m7xlymnpxl2d4llu67v08e8xs67dj2fz84ntku", - "runTime": "18:00", - "minimumReward": 1000 - }, - { - "address": "cosmosvaloper1m73mgwn3cm2e8x9a9axa0kw8nqz8a492ms63vn", - "botAddress": "cosmos197yc3g8drkf8fyv6fr4ynddy2n22kauulm934l", - "runTime": "22:00", - "minimumReward": 100000 - }, { "address": "cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq", "botAddress": "cosmos1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5f0a762", @@ -543,12 +483,6 @@ "botAddress": "cosmos1rjvyczwh5akp2ylg06dr4s2vlx0jmdwvup2ky0", "runTime": "21:00", "minimumReward": 10000 - }, - { - "address": "cosmosvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q46fe2xk", - "botAddress": "cosmos1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyhr6rw5", - "runTime": "every 1 hour", - "minimumReward": 1000000 } ], "authzSupport": true @@ -564,15 +498,6 @@ "testAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", "operators": [ - { - "address": "akashvaloper1ksu368eh54vcqzruqk42dsnx88hvk8px09ws7q", - "botAddress": "akash1m7xlymnpxl2d4llu67v08e8xs67dj2fz2w7v0x", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, { "address": "akashvaloper1strxz39h5fapp7zvn5mvf8xm0ea9aajqjstv6g", "botAddress": "akash194ytn50yhh67rdha8akhs7c6zulnz4n2yvyy4e", @@ -647,12 +572,6 @@ "botAddress": "akash1xrrfek3qpz4ja8ccrjklfy064asyqq9k7fmgd4", "runTime": "every 1 hour", "minimumReward": 1000000 - }, - { - "address": "akashvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4cyw7r4", - "botAddress": "akash1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy6chyhw", - "runTime": "every 1 hour", - "minimumReward": 1000000 } ], "authzSupport": false @@ -669,15 +588,6 @@ "testAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", "operators": [ - { - "address": "chihuahuavaloper1ju6mkyxpjsdcmrw0pnx6sd4zu4wywkmhtgnpr5", - "botAddress": "chihuahua1m7xlymnpxl2d4llu67v08e8xs67dj2fzyq79h7", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, { "address": "chihuahuavaloper1wnu86qppv7gwtgz8xul5grs80s3vd6uqzr6jzq", "botAddress": "chihuahua15xa7vst3jca9qjkngmlysrdfur8gsex93gwmmp", @@ -842,18 +752,6 @@ "botAddress": "chihuahua1xrrfek3qpz4ja8ccrjklfy064asyqq9ks8mp4d", "runTime": "every 15 minutes", "minimumReward": 100000000 - }, - { - "address": "chihuahuavaloper1pzqjgfd25qsyfdtmx9elrqx6zjjvnc9sj52r2y", - "botAddress": "chihuahua1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy5khd0k", - "runTime": "every 1 hour", - "minimumReward": 100000000 - }, - { - "address": "chihuahuavaloper1pqsdyhefm99ag3a45rkqnc5907yksqvz0tqpzg", - "botAddress": "chihuahua1k4hh4mjvtrwq79vhmlrltdsyq08kec7jcnxu9e", - "runTime": "every 15 minutes", - "minimumReward": 100000000 } ], "authzSupport": false @@ -869,12 +767,6 @@ "testAddress": "gravity1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz4x4uk7", "gasPrice": "0.025ugraviton", "operators": [ - { - "address": "gravityvaloper1vdvdl3krc9s4plnnvfk58k83lx5v38s6yse5ny", - "botAddress": "gravity1m7xlymnpxl2d4llu67v08e8xs67dj2fzr9pnn5", - "runTime": "23:00", - "minimumReward": 1000 - }, { "address": "gravityvaloper187reejg4et24jpggqt55vnc47l926w0p8w70mh", "botAddress": "gravity1v9vae554q55xa4pcfdm0p3kv8nxn50jk7u42gp", @@ -970,12 +862,6 @@ "botAddress": "gravity1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9m657na", "runTime": "every 3 hours", "minimumReward": 100000 - }, - { - "address": "gravityvaloper1h4w306x3tm39gas6evu326c9ycxwrvk4drnv5t", - "botAddress": "gravity1jrjdqzxchlt09gj6qvn4ds8suxh9aqwynngmtu", - "runTime": "every 1 hour", - "minimumReward": 1000000 } ], "authzSupport": true @@ -1014,12 +900,6 @@ "botAddress": "regen1zks5qjck40m827v2m83la5r3y6lkudw86q4g4z", "runTime": "08:00", "minimumReward": 1000000 - }, - { - "address": "regenvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4ucmxys", - "botAddress": "regen1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygp3lcs", - "runTime": "every 1 hour", - "minimumReward": 1000000 } ], "authzSupport": true @@ -1059,12 +939,6 @@ "botAddress": "terra1hz2dutqm7e0t7q3h5cm9d8lchztskm8pekadus", "runTime": "17:28", "minimumReward": 50000 - }, - { - "address": "terravaloper12079m57cew2v02zs624zvyed5479an9wxh03fy", - "botAddress": "terra1knhcrmhcm4d6apcj4vglelax7sz4fm8759s6mj", - "runTime": "every 1 hour", - "minimumReward": 1000000 } ], "authzSupport": true @@ -1080,12 +954,6 @@ "gasPrice": "0.02udvpn", "testAddress": "sent1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz2d3ahe", "operators": [ - { - "address": "sentvaloper1hms0qpe27uulkllw96tmeqnl4hvxsf4vwp729v", - "botAddress": "sent1m7xlymnpxl2d4llu67v08e8xs67dj2fzuw9jjn", - "runTime": "21:00", - "minimumReward": 100000 - }, { "address": "sentvaloper138dhtmfslt0ls4ec0jumj8tyajry4dfuv27jay", "botAddress": "sent194ytn50yhh67rdha8akhs7c6zulnz4n2jvl6gv", @@ -1143,15 +1011,6 @@ "testAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", "ownerAddress": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", "operators": [ - { - "address": "digvaloper1vm6m78glgfjaepaxwcdg6py380q7yasds2k2t8", - "botAddress": "dig1m7xlymnpxl2d4llu67v08e8xs67dj2fzlp6q58", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, { "address": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", "botAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", @@ -1228,15 +1087,6 @@ ], "gasPrice": "0.001ubcna", "operators": [ - { - "address": "bcnavaloper1e8twck404wzkfm5sd064dl0ssse3haxau0nqf6", - "botAddress": "bcna1m7xlymnpxl2d4llu67v08e8xs67dj2fza9r27w", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, { "address": "bcnavaloper1fghw3chlyavtdtlns22ysza77fwygtmrhwwf6m", "botAddress": "bcna15xa7vst3jca9qjkngmlysrdfur8gsex9gdn5j3", @@ -1308,15 +1158,6 @@ ], "gasPrice": "0.08ungm", "operators": [ - { - "address": "emoneyvaloper1039h52c70xlqwfqh29gmqq70zwwf8xrs3t9mps", - "botAddress": "emoney1m7xlymnpxl2d4llu67v08e8xs67dj2fzgkflpp", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 100000 - }, { "address": "emoneyvaloper1xpyajvwsrt6kg68g0uwvy00v9fa7v5f0ygljdp", "botAddress": "emoney15xa7vst3jca9qjkngmlysrdfur8gsex9a7epd7", @@ -1441,15 +1282,6 @@ ], "gasPrice": "0.001udsm", "operators": [ - { - "address": "desmosvaloper1zngdx77g9ywnwmwpwvj9w2eqcs6fhw78gn02d8", - "botAddress": "desmos1pu0pmlvakgdn7lzrwx2rgj6xjvcyln0l4cdfl2", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000000 - }, { "address": "desmosvaloper1xk2kv26fjd9zrg4l3qc7lwxv03w4ydeeppqkkp", "botAddress": "desmos1js340eq8082essggjtv8lf9df7qlnny8xuj9ul", @@ -1716,12 +1548,6 @@ "botAddress": "evmos19mgc0j2m7pfzuc9lthqa6r24laxk094d3a979q", "runTime": "every 3 hours", "minimumReward": 1000000000000000 - }, - { - "address": "evmosvaloper1rh9uz23gmhr6q92a45qrath4fr9ffefz4eas0z", - "botAddress": "evmos1w2v6ztzta7hra7q3j3g6g363tqnh02th0mauj3", - "runTime": "every 1 hour", - "minimumReward": 1000000000000000000 } ], "authzSupport": true @@ -1735,12 +1561,6 @@ "https://rpc.cosmos.directory/sifchain" ], "operators": [ - { - "address": "sifvaloper1ecv3sdx8h54ckv8q8u67cyk0nj37rffwpj7cdx", - "botAddress": "sif1m7xlymnpxl2d4llu67v08e8xs67dj2fzzguaeh", - "runTime": "21:00", - "minimumReward": 1000 - }, { "address": "sifvaloper1esnwgt6g6x2d3m37pw9lkgsxgzenc36n3a4n0f", "botAddress": "sif15xa7vst3jca9qjkngmlysrdfur8gsex9hqvr4g", @@ -1794,12 +1614,6 @@ "botAddress": "sif1xrrfek3qpz4ja8ccrjklfy064asyqq9kk0eemy", "runTime": "every 1 hour", "minimumReward": 10000000 - }, - { - "address": "sifvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4nz2r97", - "botAddress": "sif1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyj744pl", - "runTime": "every 1 hour", - "minimumReward": 10000000 } ], "authzSupport": false @@ -1813,21 +1627,6 @@ "https://rpc.cosmos.directory/lumnetwork" ], "operators": [ - { - "address": "lumvaloper1mpywgfyk6ku9uwfw4t6xhn050cjluhmjck27nx", - "botAddress": "lum1m7xlymnpxl2d4llu67v08e8xs67dj2fzjlwzrg", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000000 - }, - { - "address": "lumvaloper1sgeam9kyphrzrcau8l5an729mtngxahte76zkn", - "botAddress": "lum197yc3g8drkf8fyv6fr4ynddy2n22kauu23ccqt", - "runTime": "22:00", - "minimumReward": 100000 - }, { "address": "lumvaloper1xkv494sduqkpadwesqlsp6069yepsj587dvf46", "botAddress": "lum1js340eq8082essggjtv8lf9df7qlnny88wzu7n", @@ -1965,12 +1764,6 @@ "botAddress": "lum14a78h6202u4xg6phjnd8rmr5fq98zzdlsmdrw5", "runTime": "every 4 hour", "minimumReward": 10000 - }, - { - "address": "lumvaloper1p4qz6gqr3esugvm9n49jl2rz0zhup7ww286t65", - "botAddress": "lum1k4hh4mjvtrwq79vhmlrltdsyq08kec7jwvkm30", - "runTime": "every 4 hour", - "minimumReward": 10000 } ], "authzSupport": true @@ -2086,12 +1879,6 @@ "botAddress": "stars1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wqfzrwp6", "runTime": "every 1 hour", "minimumReward": 10000 - }, - { - "address": "starsvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4qlwk6v", - "botAddress": "stars1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyrld799", - "runTime": "every 1 hour", - "minimumReward": 1000000 } ], "authzSupport": true @@ -2106,15 +1893,6 @@ "https://rpc.cosmos.directory/comdex" ], "operators": [ - { - "address": "comdexvaloper15xevj3n6eq36t62sjjrsvf6hkuryg8w3pmpqy8", - "botAddress": "comdex1m7xlymnpxl2d4llu67v08e8xs67dj2fzq63f0t", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, { "address": "comdexvaloper1flh26y7f2vsam4a6snwgrqaxkhe0g2yljstdqm", "botAddress": "comdex15xa7vst3jca9qjkngmlysrdfur8gsex94jphr5", @@ -2197,12 +1975,6 @@ ], "gasPrice": "25ncheq", "operators": [ - { - "address": "cheqdvaloper1ny8gd9tsyqhm56agp4vkpa6fldr2n4guthcvfj", - "botAddress": "cheqd1m7xlymnpxl2d4llu67v08e8xs67dj2fzfhltad", - "runTime": "21:00", - "minimumReward": 1000 - }, { "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", "botAddress": "cheqd1ypntyymvjg90ntxwes3hxtmuvvh4fcr8w082sd", @@ -2220,12 +1992,6 @@ "botAddress": "cheqd195mm9y35sekrjk73anw2az9lv9xs5mzt55rxhx", "runTime": "every 1 hour", "minimumReward": 10000000 - }, - { - "address": "cheqdvaloper19f0w9svr905fhefusyx4z8sf83j6et0g3rjhkl", - "botAddress": "cheqd1etsdevgm5ps240lrw9mg3p2j32tj96ktqafpeu", - "runTime": "every 1 hour", - "minimumReward": 1000000 } ], "authzSupport": true @@ -2292,12 +2058,6 @@ "botAddress": "umee1m2g72wffh3l65sjlpxvsgmns3y7z58dyurdzyd", "runTime": "every 3 hours", "minimumReward": 50000 - }, - { - "address": "umeevaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4d0h0la", - "botAddress": "umee1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy948u2x", - "runTime": "every 1 hour", - "minimumReward": 1000000 } ], "authzSupport": true @@ -2311,15 +2071,6 @@ "https://rpc.cosmos.directory/bitsong" ], "operators": [ - { - "address": "bitsongvaloper1pn6mhrwq8vdhns366jwxkggmhmxypuhrrc4huu", - "botAddress": "bitsong1vn2fc2cdt2r5kseptl86rstqudxly2c3jxw4x7", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, { "address": "bitsongvaloper10uv3t6yru5dryz2yy9em2pzmqezyhsp0gkkxd2", "botAddress": "bitsong1js340eq8082essggjtv8lf9df7qlnny87ddafg", @@ -2370,7 +2121,7 @@ }, { "address": "bitsongvaloper1ech3swu5jeuenxzgd0rq9xz6yz3yn6t0v2x0tw", - "botAddress": "bitsong1m52073hvnza73uwrf45avgdwljyqx7p04qh3yw", + "botAddress": "bitsong18fsnw6a3yrletj5rtkp2577yq8aptsdkuwuwcl", "runTime": "every 1 hour", "minimumReward": 100000 }, @@ -2385,12 +2136,6 @@ "botAddress": "bitsong1g4zywrluj6ac0xpu6f6jqul52rv0am66mklupq", "runTime": "21:00", "minimumReward": 100000 - }, - { - "address": "bitsongvaloper1ltu429emv4tkn34n7795dxmeagl78ffqazwf9c", - "botAddress": "bitsong197yc3g8drkf8fyv6fr4ynddy2n22kauunjhehs", - "runTime": "22:00", - "minimumReward": 100000 } ], "authzSupport": true @@ -2404,12 +2149,6 @@ "https://rpc.cosmos.directory/persistence" ], "operators": [ - { - "address": "persistencevaloper1rxjmg3l0myaz6fzzvkum82lzyqz5acyxqqc08l", - "botAddress": "persistence1qzvhepyemfctq3wmc50f2ps06ldx3fnpsf03zp", - "runTime": "20:00", - "minimumReward": 10000 - }, { "address": "persistencevaloper1pdse5rr5njkkka6qeu5m8u704h6z67w5r700vn", "botAddress": "persistence1atgss2zpwl9m8fmm32gzrkjtqw3tdawguqw6tk", @@ -2464,12 +2203,6 @@ "botAddress": "agoric1hl0jgxre5z0nkyjj07c5vfdy44yk44attzqnlt", "runTime": "22:30", "minimumReward": 100000 - }, - { - "address": "agoricvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4acufxj", - "botAddress": "agoric1cwe4fgnmvdzx6kefxs8alldtwhywctnxt7mdr5", - "runTime": "every 1 hour", - "minimumReward": 1000000 } ], "authzSupport": true @@ -2501,12 +2234,6 @@ "botAddress": "ixo1mjq48r6435aewerpruwc8up3tz3rzan2nvq99x", "runTime": "21:00", "minimumReward": 100000 - }, - { - "address": "ixovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q42frukn", - "botAddress": "ixo1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygky328", - "runTime": "every 1 hour", - "minimumReward": 100000 } ], "authzSupport": false @@ -2521,15 +2248,6 @@ ], "gasPrice": "0.025uxki", "operators": [ - { - "address": "kivaloper19seaxuh9wp3zum42w6flrjsr5raptxhy3l8qvw", - "botAddress": "ki1m7xlymnpxl2d4llu67v08e8xs67dj2fzkczyjg", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, { "address": "kivaloper183ak9rcvfazp5rtljve6v06tyue3dfrglvpfux", "botAddress": "ki15xa7vst3jca9qjkngmlysrdfur8gsex9rsj67h", @@ -2605,12 +2323,6 @@ ], "image": "https://raw.githubusercontent.com/Konstellation/DARC_token/main/darctoken.svg", "operators": [ - { - "address": "darcvaloper1txzc42vl5qel54tyuck8z8pfg979hwk867z30z", - "botAddress": "darc1m7xlymnpxl2d4llu67v08e8xs67dj2fzc586mt", - "runTime": "21:00", - "minimumReward": 100000 - }, { "address": "darcvaloper14kuh954mw4xp7gz5kvr3vtul0lpz5xt3tecpep", "botAddress": "darc194ytn50yhh67rdha8akhs7c6zulnz4n2kkajp5", @@ -2707,17 +2419,11 @@ "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ - { - "address": "cerberusvaloper1xjgspyv73d3k3ewygu0v2gcwwplwxkxg03reqy", - "botAddress": "cerberus1m7xlymnpxl2d4llu67v08e8xs67dj2fzmjnzrh", - "runTime": "21:00", - "minimumReward": 10000 - }, { "address": "cerberusvaloper1xkv494sduqkpadwesqlsp6069yepsj58axja5l", "botAddress": "cerberus1js340eq8082essggjtv8lf9df7qlnny8wrlu7v", - "runTime": "every 5 minutes", - "minimumReward": 50000000 + "runTime": "every 1 hour", + "minimumReward": 100000000 }, { "address": "cerberusvaloper1ga0xxzst2hjyjtfqnffz2mapn8vk8ufysqsn30", @@ -2982,30 +2688,6 @@ "botAddress": "cerberus15enapnszjg5en949x326ae3deu35dmw539f9jm", "runTime": "every 30 minutes", "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper1pzqjgfd25qsyfdtmx9elrqx6zjjvnc9stqd7h0", - "botAddress": "cerberus1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyty62ml", - "runTime": "every 1 hour", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper1jkf678dyg8k6ewkfu6xwvtl2sz68gfh3ys4s2h", - "botAddress": "cerberus1k4hh4mjvtrwq79vhmlrltdsyq08kec7j8ptm3s", - "runTime": "every 30 minutes", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper1tksh2f6fgwdpn2p73e8wvc2zkl9ey6e95vgvwd", - "botAddress": "cerberus1v4xvyv2yar2ytrj7d78dk9e253s9g7n07qyew3", - "runTime": "every 15 minutes", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper14vq30fa9mkw00vhnvmtlqxwwnxucl7may9ran2", - "botAddress": "cerberus197yc3g8drkf8fyv6fr4ynddy2n22kauuru9cq5", - "runTime": "22:00", - "minimumReward": 100000 } ], "authzSupport": true @@ -3022,6 +2704,12 @@ "testAddress": "secret1mpf7w0yyz9n3dh42un2zqu2dmsqv7dhkdlh78e", "ownerAddress": "", "operators": [ + { + "address": "secretvaloper1jjlfeshltdy6ngnf5tg6aeaygsvf0kxg8wck04", + "botAddress": "secret185yhf2a4a94en9t8qzwp8qkwx3dfx48m4k3lk7", + "runTime": "21:00", + "minimumReward": 100000 + }, { "address": "secretvaloper16k26akna7h295rfjx3278s7xusnt736vy437y8", "botAddress": "secret1e44rluarkdw56dy2turnwjtvtg4wqvs0swpd2e", @@ -3042,7 +2730,7 @@ }, { "address": "secretvaloper1t5wtcuwjkdct9qkw2h6m48zu2hectpd6ulmekk", - "botAddress": "secret18zzklau04kf3qtwrnnzlf20xrxpx3a89dqu42m", + "botAddress": "secret12xpq68caw2aqdu70jm4k792g0v9prhrpzxjday", "runTime": "21:00", "minimumReward": 100000 }, @@ -3063,26 +2751,8 @@ "botAddress": "secret13x5lkafjz2x7ue468cqc65ea6uajq7uctnadgl", "runTime": "15:00", "minimumReward": 100000 - }, - { - "address": "secretvaloper1ahawe276d250zpxt0xgpfg63ymmu63a0svuvgw", - "botAddress": "secret1cpzlkfep9z8wjthvk5csxfkmjhmqncsl963pqg", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "secretvaloper17m3c795fz4f36zjgjhr3vkf7e9pn3xxuryeww9", - "botAddress": "secret14hqn2mr82kyacele3nmaw8hmfczfpy37kjpqun", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "secretvaloper1q0rth4fu4svxnw63vjd7w74nadzsdp0fmkhj3d", - "botAddress": "secret1zpmzxljkuzjqg20dvfwf9lnuk3pcxme5rkup7h", - "runTime": "21:00", - "minimumReward": 100000 } ], "authzSupport": true } -] \ No newline at end of file +] From 3397776d6e55afa4153c6710d7eed648ec92d66d Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Tue, 22 Mar 2022 02:58:58 -0400 Subject: [PATCH 253/862] Update WhisperNode networks --- src/networks.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/networks.json b/src/networks.json index 17874c43e..96978ecc3 100644 --- a/src/networks.json +++ b/src/networks.json @@ -62,8 +62,8 @@ { "address": "osmovaloper1md9f5524vtmrn64lyv2pdfn7cnkjkklf44vtjz", "botAddress": "osmo1e44rluarkdw56dy2turnwjtvtg4wqvs06sx5ph", - "runTime": "21:00", - "minimumReward": 10000 + "runTime": "every 1 hour", + "minimumReward": 1 }, { "address": "osmovaloper1t8qckan2yrygq7kl9apwhzfalwzgc2429p8f0s", @@ -2759,13 +2759,13 @@ "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", "botAddress": "cerberus1h43hjfxlz8t6y5wewma63jz7melv46gjcaue9a", "runTime": "every 3 minutes", - "minimumReward": 1000 + "minimumReward": 10000 }, { "address": "cerberusvaloper1g3d36rfxfqtlnz3hzd05cs2wrjgykcz2g649a8", "botAddress": "cerberus1e44rluarkdw56dy2turnwjtvtg4wqvs0wv4dzw", "runTime": "every 3 minutes", - "minimumReward": 1000 + "minimumReward": 10000 }, { "address": "cerberusvaloper1td92z5qwl39f407gxf6lu2x80enat7t3jfefnf", @@ -3025,7 +3025,7 @@ { "address": "secretvaloper16k26akna7h295rfjx3278s7xusnt736vy437y8", "botAddress": "secret1e44rluarkdw56dy2turnwjtvtg4wqvs0swpd2e", - "runTime": "21:00", + "runTime": "every 1 hour", "minimumReward": 100000 }, { @@ -3085,4 +3085,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 659c2439d3b47c993365295ffb9fc27bc58a9ba9 Mon Sep 17 00:00:00 2001 From: Edouard Lavidalle <34841632+EdouardLvdl@users.noreply.github.com> Date: Tue, 22 Mar 2022 10:50:07 +0100 Subject: [PATCH 254/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 17874c43e..89cad60de 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2239,6 +2239,12 @@ "https://rpc.cosmos.directory/umee" ], "operators": [ + { + "address": "umeevaloper10nsytn9zmtfnhk37hqfe2h9v599ld3h5zs9fqj", + "botAddress": "umee1yq67lj5nnkcc2jk9ngfe3v6mt5ra0n9d45v3nc", + "runTime": "every 1 hour", + "minimumReward": 100000 + }, { "address": "umeevaloper18s567a3dxf09q7erpcp8q20dpauugxwx8xsrcc", "botAddress": "umee1upe9xslwkqmmd8m7f7nuyhedhjc6saqhy4pldd", From e61218729880b1b63091f7232527cec4f656c223 Mon Sep 17 00:00:00 2001 From: Zenon-provalidator <74173934+Zenon-provalidator@users.noreply.github.com> Date: Tue, 22 Mar 2022 20:12:38 +0900 Subject: [PATCH 255/862] Add Provalidator Networks --- src/networks.json | 62 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 17874c43e..59062ce67 100644 --- a/src/networks.json +++ b/src/networks.json @@ -202,6 +202,12 @@ "botAddress": "osmo1k4hh4mjvtrwq79vhmlrltdsyq08kec7jnaczjf", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "osmovaloper1gy0nyn2hscxxayj2pdyu8axmfvv75nnvhc079s", + "botAddress": "osmo1330aewfwzu30vznhxw7aevaz0kn35fdykydajm", + "runTime": "every 30 minutes", + "minimumReward": 1000000 } ], "authzSupport": true @@ -439,6 +445,12 @@ "12:00" ], "minimumReward": 10000 + }, + { + "address": "junovaloper1jc9fr7s5kal8878trt86ne0k353r4yd6a60zak", + "botAddress": "juno1330aewfwzu30vznhxw7aevaz0kn35fdygdakr4", + "runTime": "every 30 minutes", + "minimumReward": 1000000 } ], "authzSupport": true @@ -549,6 +561,12 @@ "botAddress": "cosmos1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyhr6rw5", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "cosmosvaloper1g48268mu5vfp4wk7dk89r0wdrakm9p5xk0q50k", + "botAddress": "cosmos1330aewfwzu30vznhxw7aevaz0kn35fdy7l7dyf", + "runTime": "every 30 minutes", + "minimumReward": 1000000 } ], "authzSupport": true @@ -854,6 +872,12 @@ "botAddress": "chihuahua1k4hh4mjvtrwq79vhmlrltdsyq08kec7jcnxu9e", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "chihuahuavaloper1az42d6p4m277f305q65lsm4kxk5vzg4jzpzgxv", + "botAddress": "chihuahua1330aewfwzu30vznhxw7aevaz0kn35fdya2nr9t", + "runTime": "every 30 minutes", + "minimumReward": 100000000 } ], "authzSupport": false @@ -1020,6 +1044,12 @@ "botAddress": "regen1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygp3lcs", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "regenvaloper1u26sz6kydnmpr5zq6nureav4xhasak8nglmggk", + "botAddress": "regen1330aewfwzu30vznhxw7aevaz0kn35fdypa43jd", + "runTime": "every 30 minutes", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1722,6 +1752,12 @@ "botAddress": "evmos1w2v6ztzta7hra7q3j3g6g363tqnh02th0mauj3", "runTime": "every 1 hour", "minimumReward": 1000000000000000000 + }, + { + "address": "evmosvaloper1tdss4m3x7jy9mlepm2dwy8820l7uv6m2vx6z88", + "botAddress": "evmos1f54lck35ehjh3x0e62e7gak9h7zn4vl7s7s3cj", + "runTime": "every 30 minutes", + "minimumReward": 1000000000000000000 } ], "authzSupport": true @@ -1800,6 +1836,12 @@ "botAddress": "sif1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyj744pl", "runTime": "every 1 hour", "minimumReward": 10000000 + }, + { + "address": "sifvaloper14un425lvtpe7dd04fnld4s9y0gmeaxu3f0yxlu", + "botAddress": "sif1330aewfwzu30vznhxw7aevaz0kn35fdymz3mtz", + "runTime": "every 30 minutes", + "minimumReward": 10000000 } ], "authzSupport": false @@ -2298,6 +2340,12 @@ "botAddress": "umee1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy948u2x", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "umeevaloper10ckmqvzeycglg0hrcnepa0la5kw2q9ys6gquku", + "botAddress": "umee1330aewfwzu30vznhxw7aevaz0kn35fdyvfrjqm", + "runTime": "every 30 minutes", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2470,6 +2518,12 @@ "botAddress": "agoric1cwe4fgnmvdzx6kefxs8alldtwhywctnxt7mdr5", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "agoricvaloper1xvz54pusznw8t76985kl3v2epduhyuscr4zxx3", + "botAddress": "agoric14jhnyzfa9sf3jq879y5rp5324klh4av3vwezmm", + "runTime": "every 30 minutes", + "minimumReward": 1000000 } ], "authzSupport": true @@ -3006,6 +3060,12 @@ "botAddress": "cerberus197yc3g8drkf8fyv6fr4ynddy2n22kauuru9cq5", "runTime": "22:00", "minimumReward": 100000 + }, + { + "address": "cerberusvaloper1tnmjjj5ugcunyy75q3c3pn62qpwnf50pnf2xm0", + "botAddress": "cerberus1330aewfwzu30vznhxw7aevaz0kn35fdyzc7y3z", + "runTime": "every 30 minutes", + "minimumReward": 10000000 } ], "authzSupport": true @@ -3085,4 +3145,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From 6bae84455620f07a36e08e7eb70729cdc583ed36 Mon Sep 17 00:00:00 2001 From: "Sachin S. Kamath" Date: Tue, 22 Mar 2022 16:58:54 +0530 Subject: [PATCH 256/862] networks: fix parameters for blockfend on cheqd --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 17874c43e..ca6c802f9 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2206,8 +2206,8 @@ { "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", "botAddress": "cheqd1ypntyymvjg90ntxwes3hxtmuvvh4fcr8w082sd", - "runTime": "21:00", - "minimumReward": 1000 + "runTime": "every 1 hour", + "minimumReward": 100000000 }, { "address": "cheqdvaloper1qsp3a2qd6km9g0hczsac8279wcwmzmvzgvre8w", From 92e9ba5ab70cdd1a1abfa4599ae19c6d5ba8474c Mon Sep 17 00:00:00 2001 From: "Sachin S. Kamath" Date: Tue, 22 Mar 2022 16:59:02 +0530 Subject: [PATCH 257/862] bugfix: fix decimal precision on delegation page --- src/components/Delegate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Delegate.js b/src/components/Delegate.js index 4a8185002..798f2eb97 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -208,7 +208,7 @@ function Delegate(props) { REStake {!!operator() ? ( - {operator().runTimesString()} ( min) + {operator().runTimesString()} ( min) ) : } identifier={selectedValidator.operator_address} tooltip="This validator is not a REStake operator" /> } From 2f0c172aaa834034b2133642f5019b566a780db6 Mon Sep 17 00:00:00 2001 From: Joe Abbey Date: Tue, 22 Mar 2022 09:03:59 -0400 Subject: [PATCH 258/862] batchQueries Parameter to configure batches of queries per network --- scripts/autostake.mjs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index eb154b34e..56ea921b9 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -154,7 +154,8 @@ class Autostake { } getDelegations(client) { - return client.queryClient.getAllValidatorDelegations(client.operator.address, 100, (pages) => { + let batchSize = client.network.data.autostake?.batchQueries || 100 + return client.queryClient.getAllValidatorDelegations(client.operator.address, batchSize, (pages) => { timeStamp("...batch", pages.length) }).catch(error => { timeStamp("ERROR:", error.message || error) @@ -163,6 +164,7 @@ class Autostake { } async getGrantedAddresses(client, addresses) { + let batchSize = client.network.data.autostake?.batchQueries || 50 let grantCalls = addresses.map(item => { return async () => { try { @@ -173,7 +175,7 @@ class Autostake { } } }) - let grantedAddresses = await mapSync(grantCalls, 50, (batch, index) => { + let grantedAddresses = await mapSync(grantCalls, batchSize, (batch, index) => { timeStamp('...batch', index + 1) }) return _.compact(grantedAddresses.flat()) @@ -200,6 +202,7 @@ class Autostake { } async getAutostakeMessages(client, addresses, validators) { + let batchSize = client.network.data.autostake?.batchQueries || 50 let calls = addresses.map(item => { return async () => { try { @@ -209,7 +212,7 @@ class Autostake { } } }) - let messages = await mapSync(calls, 50, (batch, index) => { + let messages = await mapSync(calls, batchSize, (batch, index) => { // timeStamp('...batch', index + 1) }) return _.compact(messages.flat()) From 1ff09d41b4d3b316ea380082affdd51909b996ef Mon Sep 17 00:00:00 2001 From: Raphael Thurnherr Date: Tue, 22 Mar 2022 18:13:22 +0100 Subject: [PATCH 259/862] add decrypto to lum and dig --- src/networks.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/networks.json b/src/networks.json index 17874c43e..3d0f69924 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1214,6 +1214,12 @@ "botAddress": "dig195mm9y35sekrjk73anw2az9lv9xs5mztzzxd7v", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "digvaloper1fhp54fwlfmpwwgrnfwk3v47v53yjtp8fw6nelw", + "botAddress": "dig1wtcvjqx8097gtkjdemle9c0lm8gczm2a9va2hg", + "runTime": "11:00", + "minimumReward": 1000 } ], "authzSupport": false @@ -1971,6 +1977,12 @@ "botAddress": "lum1k4hh4mjvtrwq79vhmlrltdsyq08kec7jwvkm30", "runTime": "every 4 hour", "minimumReward": 10000 + }, + { + "address": "lumvaloper18rd4dk828pfgw680hr93rxjfmj8zvr5xc03hd3", + "botAddress": "lum1wtcvjqx8097gtkjdemle9c0lm8gczm2agjfgq8", + "runTime": "11:00", + "minimumReward": 1000 } ], "authzSupport": true From ca19441e2ba55e545459434d352439400c3034c5 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 22 Mar 2022 18:31:09 +0100 Subject: [PATCH 260/862] Updated Networks List --- src/networks.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/networks.json b/src/networks.json index 17874c43e..fbfbc6cf1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -50,7 +50,7 @@ { "address": "osmovaloper1zfcmwh56kmz4wqqg2t8pxrm228dx2c6hhzhtx7", "botAddress": "osmo1z0xzvw999jh7ekce7eqnex2fj7cnn6xwxqfugk", - "runTime": "21:00", + "runTime": "15 minutes", "minimumReward": 1000 }, { @@ -597,7 +597,7 @@ { "address": "akashvaloper1zfcmwh56kmz4wqqg2t8pxrm228dx2c6hzh0ewm", "botAddress": "akash1z0xzvw999jh7ekce7eqnex2fj7cnn6xwrqht87", - "runTime": "21:00", + "runTime": "15 minutes", "minimumReward": 10000 }, { @@ -705,7 +705,7 @@ { "address": "chihuahuavaloper1y6m72uxs6hhsudxqpq73rsdgkjh6nhkqef8pej", "botAddress": "chihuahua1z0xzvw999jh7ekce7eqnex2fj7cnn6xwdwhzlx", - "runTime": "21:00", + "runTime": "15 minutes", "minimumReward": 1001 }, { @@ -884,7 +884,7 @@ { "address": "gravityvaloper1zfcmwh56kmz4wqqg2t8pxrm228dx2c6hs487gh", "botAddress": "gravity1z0xzvw999jh7ekce7eqnex2fj7cnn6xw2tg5mv", - "runTime": "23:00", + "runTime": "15 minutes", "minimumReward": 1000 }, { @@ -1483,7 +1483,7 @@ { "address": "desmosvaloper17ue85ck027c4grv7nuks7k7p4fqnlc55uqhskj", "botAddress": "desmos1z0xzvw999jh7ekce7eqnex2fj7cnn6xw6rhufu", - "runTime": "22:00", + "runTime": "15 minutes", "minimumReward": 1000 }, { @@ -2242,7 +2242,7 @@ { "address": "umeevaloper18s567a3dxf09q7erpcp8q20dpauugxwx8xsrcc", "botAddress": "umee1upe9xslwkqmmd8m7f7nuyhedhjc6saqhy4pldd", - "runTime": "every 15 minutes", + "runTime": "15 minutes", "minimumReward": 100000 }, { @@ -2776,7 +2776,7 @@ { "address": "cerberusvaloper1r34mlqewsxrde38vp3tzwh3rk5vt6ez0slzuvu", "botAddress": "cerberus16r56awgxe7mkrwpsnurjexfdg0n3f4f3dgf090", - "runTime": "21:00", + "runTime": "15 minutes", "minimumReward": 10000 }, { @@ -3085,4 +3085,4 @@ ], "authzSupport": true } -] \ No newline at end of file +] From e3fafdba746cad51a74e2b1bd19359afa386604c Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 23 Mar 2022 00:10:32 +0000 Subject: [PATCH 261/862] Revert "Update networks.json" c113c6a7d74945261eb19304abb49cc862958328 --- src/networks.json | 352 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 341 insertions(+), 11 deletions(-) diff --git a/src/networks.json b/src/networks.json index 2b16d9137..17874c43e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -11,6 +11,12 @@ "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "operators": [ + { + "address": "osmovaloper1m73mgwn3cm2e8x9a9axa0kw8nqz8a492vg4hp4", + "botAddress": "osmo197yc3g8drkf8fyv6fr4ynddy2n22kauuhqkprd", + "runTime": "22:00", + "minimumReward": 50000 + }, { "address": "osmovaloper122yaxffys6rmv03nwwkmn3rvr5skzxl9lry2a5", "botAddress": "osmo1m7xlymnpxl2d4llu67v08e8xs67dj2fz0wqmqw", @@ -184,6 +190,18 @@ "botAddress": "osmo1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv56exja", "runTime": "21:00", "minimumReward": 1000 + }, + { + "address": "osmovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4d3kvts", + "botAddress": "osmo1jrjdqzxchlt09gj6qvn4ds8suxh9aqwylcfncx", + "runTime": "every 1 hour", + "minimumReward": 1000000 + }, + { + "address": "osmovaloper1vkfmegxrsveefn2wmudh7apxuzu2n77654ad62", + "botAddress": "osmo1k4hh4mjvtrwq79vhmlrltdsyq08kec7jnaczjf", + "runTime": "21:00", + "minimumReward": 1000 } ], "authzSupport": true @@ -200,6 +218,21 @@ "gasPrice": "0.0025ujuno", "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", "operators": [ + { + "address": "junovaloper1qr78535yz60zemv82sww948c6azx40gnawnh6r", + "botAddress": "juno1m7xlymnpxl2d4llu67v08e8xs67dj2fz38ss3q", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, + { + "address": "junovaloper18eancryfzd7n0ag9xy3azqvxgkjla2mrs2uznx", + "botAddress": "juno197yc3g8drkf8fyv6fr4ynddy2n22kauuffx2jr", + "runTime": "22:00", + "minimumReward": 100000 + }, { "address": "junovaloper1ty96k6wly7k9ualepv2hs258u29esn6mq02yd4", "botAddress": "juno15xa7vst3jca9qjkngmlysrdfur8gsex9y0qwal", @@ -391,6 +424,21 @@ "botAddress": "juno1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv2nfdrn", "runTime": "21:00", "minimumReward": 10000 + }, + { + "address": "junovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4kjctkq", + "botAddress": "juno1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyp3ecfg", + "runTime": "every 1 hour", + "minimumReward": 1000000 + }, + { + "address": "junovaloper194v8uwee2fvs2s8fa5k7j03ktwc87h5ym39jfv", + "botAddress": "juno179w4ead6r2xf3uam3pp7kg55tsl5eycyta60qh", + "runTime": [ + "00:00", + "12:00" + ], + "minimumReward": 10000 } ], "authzSupport": true @@ -406,6 +454,18 @@ "gasPrice": "0.0025uatom", "testAddress": "cosmos1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz3k8ynk", "operators": [ + { + "address": "cosmosvaloper1gf4wlkutql95j7wwsxz490s6fahlvk2s9xpwax", + "botAddress": "cosmos1m7xlymnpxl2d4llu67v08e8xs67dj2fz84ntku", + "runTime": "18:00", + "minimumReward": 1000 + }, + { + "address": "cosmosvaloper1m73mgwn3cm2e8x9a9axa0kw8nqz8a492ms63vn", + "botAddress": "cosmos197yc3g8drkf8fyv6fr4ynddy2n22kauulm934l", + "runTime": "22:00", + "minimumReward": 100000 + }, { "address": "cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq", "botAddress": "cosmos1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5f0a762", @@ -483,6 +543,12 @@ "botAddress": "cosmos1rjvyczwh5akp2ylg06dr4s2vlx0jmdwvup2ky0", "runTime": "21:00", "minimumReward": 10000 + }, + { + "address": "cosmosvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q46fe2xk", + "botAddress": "cosmos1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyhr6rw5", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -498,6 +564,15 @@ "testAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", "operators": [ + { + "address": "akashvaloper1ksu368eh54vcqzruqk42dsnx88hvk8px09ws7q", + "botAddress": "akash1m7xlymnpxl2d4llu67v08e8xs67dj2fz2w7v0x", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "akashvaloper1strxz39h5fapp7zvn5mvf8xm0ea9aajqjstv6g", "botAddress": "akash194ytn50yhh67rdha8akhs7c6zulnz4n2yvyy4e", @@ -572,6 +647,12 @@ "botAddress": "akash1xrrfek3qpz4ja8ccrjklfy064asyqq9k7fmgd4", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "akashvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4cyw7r4", + "botAddress": "akash1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy6chyhw", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": false @@ -588,6 +669,15 @@ "testAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", "operators": [ + { + "address": "chihuahuavaloper1ju6mkyxpjsdcmrw0pnx6sd4zu4wywkmhtgnpr5", + "botAddress": "chihuahua1m7xlymnpxl2d4llu67v08e8xs67dj2fzyq79h7", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "chihuahuavaloper1wnu86qppv7gwtgz8xul5grs80s3vd6uqzr6jzq", "botAddress": "chihuahua15xa7vst3jca9qjkngmlysrdfur8gsex93gwmmp", @@ -752,6 +842,18 @@ "botAddress": "chihuahua1xrrfek3qpz4ja8ccrjklfy064asyqq9ks8mp4d", "runTime": "every 15 minutes", "minimumReward": 100000000 + }, + { + "address": "chihuahuavaloper1pzqjgfd25qsyfdtmx9elrqx6zjjvnc9sj52r2y", + "botAddress": "chihuahua1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy5khd0k", + "runTime": "every 1 hour", + "minimumReward": 100000000 + }, + { + "address": "chihuahuavaloper1pqsdyhefm99ag3a45rkqnc5907yksqvz0tqpzg", + "botAddress": "chihuahua1k4hh4mjvtrwq79vhmlrltdsyq08kec7jcnxu9e", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": false @@ -767,6 +869,12 @@ "testAddress": "gravity1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz4x4uk7", "gasPrice": "0.025ugraviton", "operators": [ + { + "address": "gravityvaloper1vdvdl3krc9s4plnnvfk58k83lx5v38s6yse5ny", + "botAddress": "gravity1m7xlymnpxl2d4llu67v08e8xs67dj2fzr9pnn5", + "runTime": "23:00", + "minimumReward": 1000 + }, { "address": "gravityvaloper187reejg4et24jpggqt55vnc47l926w0p8w70mh", "botAddress": "gravity1v9vae554q55xa4pcfdm0p3kv8nxn50jk7u42gp", @@ -862,6 +970,12 @@ "botAddress": "gravity1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9m657na", "runTime": "every 3 hours", "minimumReward": 100000 + }, + { + "address": "gravityvaloper1h4w306x3tm39gas6evu326c9ycxwrvk4drnv5t", + "botAddress": "gravity1jrjdqzxchlt09gj6qvn4ds8suxh9aqwynngmtu", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -900,6 +1014,12 @@ "botAddress": "regen1zks5qjck40m827v2m83la5r3y6lkudw86q4g4z", "runTime": "08:00", "minimumReward": 1000000 + }, + { + "address": "regenvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4ucmxys", + "botAddress": "regen1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygp3lcs", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -939,6 +1059,12 @@ "botAddress": "terra1hz2dutqm7e0t7q3h5cm9d8lchztskm8pekadus", "runTime": "17:28", "minimumReward": 50000 + }, + { + "address": "terravaloper12079m57cew2v02zs624zvyed5479an9wxh03fy", + "botAddress": "terra1knhcrmhcm4d6apcj4vglelax7sz4fm8759s6mj", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -954,6 +1080,12 @@ "gasPrice": "0.02udvpn", "testAddress": "sent1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz2d3ahe", "operators": [ + { + "address": "sentvaloper1hms0qpe27uulkllw96tmeqnl4hvxsf4vwp729v", + "botAddress": "sent1m7xlymnpxl2d4llu67v08e8xs67dj2fzuw9jjn", + "runTime": "21:00", + "minimumReward": 100000 + }, { "address": "sentvaloper138dhtmfslt0ls4ec0jumj8tyajry4dfuv27jay", "botAddress": "sent194ytn50yhh67rdha8akhs7c6zulnz4n2jvl6gv", @@ -1011,6 +1143,15 @@ "testAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", "ownerAddress": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", "operators": [ + { + "address": "digvaloper1vm6m78glgfjaepaxwcdg6py380q7yasds2k2t8", + "botAddress": "dig1m7xlymnpxl2d4llu67v08e8xs67dj2fzlp6q58", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", "botAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", @@ -1087,6 +1228,15 @@ ], "gasPrice": "0.001ubcna", "operators": [ + { + "address": "bcnavaloper1e8twck404wzkfm5sd064dl0ssse3haxau0nqf6", + "botAddress": "bcna1m7xlymnpxl2d4llu67v08e8xs67dj2fza9r27w", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "bcnavaloper1fghw3chlyavtdtlns22ysza77fwygtmrhwwf6m", "botAddress": "bcna15xa7vst3jca9qjkngmlysrdfur8gsex9gdn5j3", @@ -1158,6 +1308,15 @@ ], "gasPrice": "0.08ungm", "operators": [ + { + "address": "emoneyvaloper1039h52c70xlqwfqh29gmqq70zwwf8xrs3t9mps", + "botAddress": "emoney1m7xlymnpxl2d4llu67v08e8xs67dj2fzgkflpp", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 100000 + }, { "address": "emoneyvaloper1xpyajvwsrt6kg68g0uwvy00v9fa7v5f0ygljdp", "botAddress": "emoney15xa7vst3jca9qjkngmlysrdfur8gsex9a7epd7", @@ -1282,6 +1441,15 @@ ], "gasPrice": "0.001udsm", "operators": [ + { + "address": "desmosvaloper1zngdx77g9ywnwmwpwvj9w2eqcs6fhw78gn02d8", + "botAddress": "desmos1pu0pmlvakgdn7lzrwx2rgj6xjvcyln0l4cdfl2", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000000 + }, { "address": "desmosvaloper1xk2kv26fjd9zrg4l3qc7lwxv03w4ydeeppqkkp", "botAddress": "desmos1js340eq8082essggjtv8lf9df7qlnny8xuj9ul", @@ -1548,6 +1716,12 @@ "botAddress": "evmos19mgc0j2m7pfzuc9lthqa6r24laxk094d3a979q", "runTime": "every 3 hours", "minimumReward": 1000000000000000 + }, + { + "address": "evmosvaloper1rh9uz23gmhr6q92a45qrath4fr9ffefz4eas0z", + "botAddress": "evmos1w2v6ztzta7hra7q3j3g6g363tqnh02th0mauj3", + "runTime": "every 1 hour", + "minimumReward": 1000000000000000000 } ], "authzSupport": true @@ -1561,6 +1735,12 @@ "https://rpc.cosmos.directory/sifchain" ], "operators": [ + { + "address": "sifvaloper1ecv3sdx8h54ckv8q8u67cyk0nj37rffwpj7cdx", + "botAddress": "sif1m7xlymnpxl2d4llu67v08e8xs67dj2fzzguaeh", + "runTime": "21:00", + "minimumReward": 1000 + }, { "address": "sifvaloper1esnwgt6g6x2d3m37pw9lkgsxgzenc36n3a4n0f", "botAddress": "sif15xa7vst3jca9qjkngmlysrdfur8gsex9hqvr4g", @@ -1614,6 +1794,12 @@ "botAddress": "sif1xrrfek3qpz4ja8ccrjklfy064asyqq9kk0eemy", "runTime": "every 1 hour", "minimumReward": 10000000 + }, + { + "address": "sifvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4nz2r97", + "botAddress": "sif1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyj744pl", + "runTime": "every 1 hour", + "minimumReward": 10000000 } ], "authzSupport": false @@ -1627,6 +1813,21 @@ "https://rpc.cosmos.directory/lumnetwork" ], "operators": [ + { + "address": "lumvaloper1mpywgfyk6ku9uwfw4t6xhn050cjluhmjck27nx", + "botAddress": "lum1m7xlymnpxl2d4llu67v08e8xs67dj2fzjlwzrg", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000000 + }, + { + "address": "lumvaloper1sgeam9kyphrzrcau8l5an729mtngxahte76zkn", + "botAddress": "lum197yc3g8drkf8fyv6fr4ynddy2n22kauu23ccqt", + "runTime": "22:00", + "minimumReward": 100000 + }, { "address": "lumvaloper1xkv494sduqkpadwesqlsp6069yepsj587dvf46", "botAddress": "lum1js340eq8082essggjtv8lf9df7qlnny88wzu7n", @@ -1764,6 +1965,12 @@ "botAddress": "lum14a78h6202u4xg6phjnd8rmr5fq98zzdlsmdrw5", "runTime": "every 4 hour", "minimumReward": 10000 + }, + { + "address": "lumvaloper1p4qz6gqr3esugvm9n49jl2rz0zhup7ww286t65", + "botAddress": "lum1k4hh4mjvtrwq79vhmlrltdsyq08kec7jwvkm30", + "runTime": "every 4 hour", + "minimumReward": 10000 } ], "authzSupport": true @@ -1879,6 +2086,12 @@ "botAddress": "stars1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wqfzrwp6", "runTime": "every 1 hour", "minimumReward": 10000 + }, + { + "address": "starsvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4qlwk6v", + "botAddress": "stars1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyrld799", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1893,6 +2106,15 @@ "https://rpc.cosmos.directory/comdex" ], "operators": [ + { + "address": "comdexvaloper15xevj3n6eq36t62sjjrsvf6hkuryg8w3pmpqy8", + "botAddress": "comdex1m7xlymnpxl2d4llu67v08e8xs67dj2fzq63f0t", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "comdexvaloper1flh26y7f2vsam4a6snwgrqaxkhe0g2yljstdqm", "botAddress": "comdex15xa7vst3jca9qjkngmlysrdfur8gsex94jphr5", @@ -1975,6 +2197,12 @@ ], "gasPrice": "25ncheq", "operators": [ + { + "address": "cheqdvaloper1ny8gd9tsyqhm56agp4vkpa6fldr2n4guthcvfj", + "botAddress": "cheqd1m7xlymnpxl2d4llu67v08e8xs67dj2fzfhltad", + "runTime": "21:00", + "minimumReward": 1000 + }, { "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", "botAddress": "cheqd1ypntyymvjg90ntxwes3hxtmuvvh4fcr8w082sd", @@ -1992,6 +2220,12 @@ "botAddress": "cheqd195mm9y35sekrjk73anw2az9lv9xs5mzt55rxhx", "runTime": "every 1 hour", "minimumReward": 10000000 + }, + { + "address": "cheqdvaloper19f0w9svr905fhefusyx4z8sf83j6et0g3rjhkl", + "botAddress": "cheqd1etsdevgm5ps240lrw9mg3p2j32tj96ktqafpeu", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2058,6 +2292,12 @@ "botAddress": "umee1m2g72wffh3l65sjlpxvsgmns3y7z58dyurdzyd", "runTime": "every 3 hours", "minimumReward": 50000 + }, + { + "address": "umeevaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4d0h0la", + "botAddress": "umee1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy948u2x", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2071,6 +2311,15 @@ "https://rpc.cosmos.directory/bitsong" ], "operators": [ + { + "address": "bitsongvaloper1pn6mhrwq8vdhns366jwxkggmhmxypuhrrc4huu", + "botAddress": "bitsong1vn2fc2cdt2r5kseptl86rstqudxly2c3jxw4x7", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "bitsongvaloper10uv3t6yru5dryz2yy9em2pzmqezyhsp0gkkxd2", "botAddress": "bitsong1js340eq8082essggjtv8lf9df7qlnny87ddafg", @@ -2121,7 +2370,7 @@ }, { "address": "bitsongvaloper1ech3swu5jeuenxzgd0rq9xz6yz3yn6t0v2x0tw", - "botAddress": "bitsong18fsnw6a3yrletj5rtkp2577yq8aptsdkuwuwcl", + "botAddress": "bitsong1m52073hvnza73uwrf45avgdwljyqx7p04qh3yw", "runTime": "every 1 hour", "minimumReward": 100000 }, @@ -2136,6 +2385,12 @@ "botAddress": "bitsong1g4zywrluj6ac0xpu6f6jqul52rv0am66mklupq", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper1ltu429emv4tkn34n7795dxmeagl78ffqazwf9c", + "botAddress": "bitsong197yc3g8drkf8fyv6fr4ynddy2n22kauunjhehs", + "runTime": "22:00", + "minimumReward": 100000 } ], "authzSupport": true @@ -2149,6 +2404,12 @@ "https://rpc.cosmos.directory/persistence" ], "operators": [ + { + "address": "persistencevaloper1rxjmg3l0myaz6fzzvkum82lzyqz5acyxqqc08l", + "botAddress": "persistence1qzvhepyemfctq3wmc50f2ps06ldx3fnpsf03zp", + "runTime": "20:00", + "minimumReward": 10000 + }, { "address": "persistencevaloper1pdse5rr5njkkka6qeu5m8u704h6z67w5r700vn", "botAddress": "persistence1atgss2zpwl9m8fmm32gzrkjtqw3tdawguqw6tk", @@ -2203,6 +2464,12 @@ "botAddress": "agoric1hl0jgxre5z0nkyjj07c5vfdy44yk44attzqnlt", "runTime": "22:30", "minimumReward": 100000 + }, + { + "address": "agoricvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4acufxj", + "botAddress": "agoric1cwe4fgnmvdzx6kefxs8alldtwhywctnxt7mdr5", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2234,6 +2501,12 @@ "botAddress": "ixo1mjq48r6435aewerpruwc8up3tz3rzan2nvq99x", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "ixovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q42frukn", + "botAddress": "ixo1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygky328", + "runTime": "every 1 hour", + "minimumReward": 100000 } ], "authzSupport": false @@ -2248,6 +2521,15 @@ ], "gasPrice": "0.025uxki", "operators": [ + { + "address": "kivaloper19seaxuh9wp3zum42w6flrjsr5raptxhy3l8qvw", + "botAddress": "ki1m7xlymnpxl2d4llu67v08e8xs67dj2fzkczyjg", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "kivaloper183ak9rcvfazp5rtljve6v06tyue3dfrglvpfux", "botAddress": "ki15xa7vst3jca9qjkngmlysrdfur8gsex9rsj67h", @@ -2323,6 +2605,12 @@ ], "image": "https://raw.githubusercontent.com/Konstellation/DARC_token/main/darctoken.svg", "operators": [ + { + "address": "darcvaloper1txzc42vl5qel54tyuck8z8pfg979hwk867z30z", + "botAddress": "darc1m7xlymnpxl2d4llu67v08e8xs67dj2fzc586mt", + "runTime": "21:00", + "minimumReward": 100000 + }, { "address": "darcvaloper14kuh954mw4xp7gz5kvr3vtul0lpz5xt3tecpep", "botAddress": "darc194ytn50yhh67rdha8akhs7c6zulnz4n2kkajp5", @@ -2419,11 +2707,17 @@ "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ + { + "address": "cerberusvaloper1xjgspyv73d3k3ewygu0v2gcwwplwxkxg03reqy", + "botAddress": "cerberus1m7xlymnpxl2d4llu67v08e8xs67dj2fzmjnzrh", + "runTime": "21:00", + "minimumReward": 10000 + }, { "address": "cerberusvaloper1xkv494sduqkpadwesqlsp6069yepsj58axja5l", "botAddress": "cerberus1js340eq8082essggjtv8lf9df7qlnny8wrlu7v", - "runTime": "every 1 hour", - "minimumReward": 100000000 + "runTime": "every 5 minutes", + "minimumReward": 50000000 }, { "address": "cerberusvaloper1ga0xxzst2hjyjtfqnffz2mapn8vk8ufysqsn30", @@ -2688,6 +2982,30 @@ "botAddress": "cerberus15enapnszjg5en949x326ae3deu35dmw539f9jm", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper1pzqjgfd25qsyfdtmx9elrqx6zjjvnc9stqd7h0", + "botAddress": "cerberus1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyty62ml", + "runTime": "every 1 hour", + "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper1jkf678dyg8k6ewkfu6xwvtl2sz68gfh3ys4s2h", + "botAddress": "cerberus1k4hh4mjvtrwq79vhmlrltdsyq08kec7j8ptm3s", + "runTime": "every 30 minutes", + "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper1tksh2f6fgwdpn2p73e8wvc2zkl9ey6e95vgvwd", + "botAddress": "cerberus1v4xvyv2yar2ytrj7d78dk9e253s9g7n07qyew3", + "runTime": "every 15 minutes", + "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper14vq30fa9mkw00vhnvmtlqxwwnxucl7may9ran2", + "botAddress": "cerberus197yc3g8drkf8fyv6fr4ynddy2n22kauuru9cq5", + "runTime": "22:00", + "minimumReward": 100000 } ], "authzSupport": true @@ -2704,12 +3022,6 @@ "testAddress": "secret1mpf7w0yyz9n3dh42un2zqu2dmsqv7dhkdlh78e", "ownerAddress": "", "operators": [ - { - "address": "secretvaloper1jjlfeshltdy6ngnf5tg6aeaygsvf0kxg8wck04", - "botAddress": "secret185yhf2a4a94en9t8qzwp8qkwx3dfx48m4k3lk7", - "runTime": "21:00", - "minimumReward": 100000 - }, { "address": "secretvaloper16k26akna7h295rfjx3278s7xusnt736vy437y8", "botAddress": "secret1e44rluarkdw56dy2turnwjtvtg4wqvs0swpd2e", @@ -2730,7 +3042,7 @@ }, { "address": "secretvaloper1t5wtcuwjkdct9qkw2h6m48zu2hectpd6ulmekk", - "botAddress": "secret12xpq68caw2aqdu70jm4k792g0v9prhrpzxjday", + "botAddress": "secret18zzklau04kf3qtwrnnzlf20xrxpx3a89dqu42m", "runTime": "21:00", "minimumReward": 100000 }, @@ -2751,8 +3063,26 @@ "botAddress": "secret13x5lkafjz2x7ue468cqc65ea6uajq7uctnadgl", "runTime": "15:00", "minimumReward": 100000 + }, + { + "address": "secretvaloper1ahawe276d250zpxt0xgpfg63ymmu63a0svuvgw", + "botAddress": "secret1cpzlkfep9z8wjthvk5csxfkmjhmqncsl963pqg", + "runTime": "21:00", + "minimumReward": 100000 + }, + { + "address": "secretvaloper17m3c795fz4f36zjgjhr3vkf7e9pn3xxuryeww9", + "botAddress": "secret14hqn2mr82kyacele3nmaw8hmfczfpy37kjpqun", + "runTime": "21:00", + "minimumReward": 100000 + }, + { + "address": "secretvaloper1q0rth4fu4svxnw63vjd7w74nadzsdp0fmkhj3d", + "botAddress": "secret1zpmzxljkuzjqg20dvfwf9lnuk3pcxme5rkup7h", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true } -] +] \ No newline at end of file From 5b58f5fc6d2f9fbb67099f46a25c9471339e07ff Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 23 Mar 2022 00:11:19 +0000 Subject: [PATCH 262/862] Re-add Artemis secret validator --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 17874c43e..5ba153512 100644 --- a/src/networks.json +++ b/src/networks.json @@ -3022,6 +3022,12 @@ "testAddress": "secret1mpf7w0yyz9n3dh42un2zqu2dmsqv7dhkdlh78e", "ownerAddress": "", "operators": [ + { + "address": "secretvaloper1jjlfeshltdy6ngnf5tg6aeaygsvf0kxg8wck04", + "botAddress": "secret185yhf2a4a94en9t8qzwp8qkwx3dfx48m4k3lk7", + "runTime": "21:00", + "minimumReward": 100000 + }, { "address": "secretvaloper16k26akna7h295rfjx3278s7xusnt736vy437y8", "botAddress": "secret1e44rluarkdw56dy2turnwjtvtg4wqvs0swpd2e", From edd975c7f78765a6f0ba940c42f1a7e301536724 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 23 Mar 2022 10:51:40 +0000 Subject: [PATCH 263/862] Add ECO Stake to Regen and Comdex --- src/networks.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/networks.json b/src/networks.json index 99fd41fde..2264ce9f1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1014,7 +1014,17 @@ ], "gasPrice": "0.03uregen", "testAddress": "regen1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczw5vc9j", + "ownerAddress": "regenvaloper1c4y3j05qx652rnxm5mg4yesqdkmhz2f6dl7hhk", "operators": [ + { + "address": "regenvaloper1c4y3j05qx652rnxm5mg4yesqdkmhz2f6dl7hhk", + "botAddress": "regen1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczw5vc9j", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "regenvaloper1gjvu75cq6qxyrtdv66lx9xe92jw9gqdetagaze", "botAddress": "regen1mjq48r6435aewerpruwc8up3tz3rzan2nm4th3", @@ -2159,7 +2169,18 @@ "rpcUrl": [ "https://rpc.cosmos.directory/comdex" ], + "testAddress": "comdex1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczke9x2p", + "ownerAddress": "comdexvaloper17f70yjkvmvld379904jaddx9h0f74n32pjtmp6", "operators": [ + { + "address": "comdexvaloper17f70yjkvmvld379904jaddx9h0f74n32pjtmp6", + "botAddress": "comdex1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczke9x2p", + "runTime": [ + "09:00", + "21:00" + ], + "minimumReward": 1000 + }, { "address": "comdexvaloper15xevj3n6eq36t62sjjrsvf6hkuryg8w3pmpqy8", "botAddress": "comdex1m7xlymnpxl2d4llu67v08e8xs67dj2fzq63f0t", From 1872e26527911e330a7f87e0bafe914a577ebc0a Mon Sep 17 00:00:00 2001 From: mdesmarez <45880572+mdesmarez@users.noreply.github.com> Date: Wed, 23 Mar 2022 17:17:17 +0100 Subject: [PATCH 264/862] Update networks.json my conf in order to restake --- src/networks.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/networks.json b/src/networks.json index 99fd41fde..529f991da 100644 --- a/src/networks.json +++ b/src/networks.json @@ -878,6 +878,12 @@ "botAddress": "chihuahua1330aewfwzu30vznhxw7aevaz0kn35fdya2nr9t", "runTime": "every 30 minutes", "minimumReward": 100000000 + }, + { + "address": "chihuahuavaloper14xwftsm2nrhdzp5md42fg5cf3cndnkz0al2dzs", + "botAddress": "chihuahua1wve3tc8sj42yl3trw3lt0rcyp5medvyascnrf6", + "runTime": "22:00", + "minimumReward": 100000000 } ], "authzSupport": false @@ -2280,6 +2286,12 @@ "botAddress": "cheqd1etsdevgm5ps240lrw9mg3p2j32tj96ktqafpeu", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "cheqdvaloper1ttw6u36w7jxjpadt5eh8vnt5a8wpx6ju2wkuj6", + "botAddress": "cheqd1wve3tc8sj42yl3trw3lt0rcyp5medvyaa0jdrf", + "runTime": "22:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -3084,6 +3096,12 @@ "botAddress": "cerberus1330aewfwzu30vznhxw7aevaz0kn35fdyzc7y3z", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper1gypqhk3hszgcg08h9kj5fja8xzl63txmt3m6k6", + "botAddress": "cerberus1wve3tc8sj42yl3trw3lt0rcyp5medvya027yan", + "runTime": "22:00", + "minimumReward": 10000000 } ], "authzSupport": true From 4838828448b560cc96515cefffad42692aa4885b Mon Sep 17 00:00:00 2001 From: supika <45271955+SUPIKA9@users.noreply.github.com> Date: Wed, 23 Mar 2022 11:48:53 -0700 Subject: [PATCH 265/862] adding spellbooks.tech to cerb restake networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 99fd41fde..78c3c6e0c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -3084,6 +3084,12 @@ "botAddress": "cerberus1330aewfwzu30vznhxw7aevaz0kn35fdyzc7y3z", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper15ms93f3m2h8rmzfkngxh06mklujrxprpl84pyr", + "botAddress": "cerberus1ajvn5l50lvuy5ej5sqhwthve26au0qs56gfa0r", + "runTime": "every 15 minutes", + "minimumReward": 100000000 } ], "authzSupport": true From 1e47b32c3e79f7b6d9b74c7ea0f2a5271453c7d0 Mon Sep 17 00:00:00 2001 From: gh0st | WhisperNode <49853598+gh0stdotexe@users.noreply.github.com> Date: Thu, 24 Mar 2022 03:23:12 -0400 Subject: [PATCH 266/862] Update WhisperNode timers --- src/networks.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/networks.json b/src/networks.json index 99fd41fde..062c266e0 100644 --- a/src/networks.json +++ b/src/networks.json @@ -62,8 +62,8 @@ { "address": "osmovaloper1md9f5524vtmrn64lyv2pdfn7cnkjkklf44vtjz", "botAddress": "osmo1e44rluarkdw56dy2turnwjtvtg4wqvs06sx5ph", - "runTime": "every 1 hour", - "minimumReward": 1 + "runTime": "21:00", + "minimumReward": 1000 }, { "address": "osmovaloper1t8qckan2yrygq7kl9apwhzfalwzgc2429p8f0s", @@ -284,8 +284,8 @@ { "address": "junovaloper193xl2tqh2tjkld2zv49ku5s44ee4qmgr65jcep", "botAddress": "juno1e44rluarkdw56dy2turnwjtvtg4wqvs0yeklse", - "runTime": "every 45 minutes", - "minimumReward": 9999 + "runTime": "every 29 minutes", + "minimumReward": 100000 }, { "address": "junovaloper17n3w6v5q3n0tws4xv8upd9ul4qqes0nlg7q0xd", From 7780440ea276dd12f7190b20e9d1ebb407d41aa4 Mon Sep 17 00:00:00 2001 From: Gjermund Bjaanes Date: Thu, 24 Mar 2022 14:20:17 +0100 Subject: [PATCH 267/862] Fix manual restake by correctly setting up messages --- src/components/ClaimRewards.js | 53 +++++++++++++++++++--------------- src/components/Delegations.js | 32 +++++++++++++++----- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/components/ClaimRewards.js b/src/components/ClaimRewards.js index 1b5cd87a7..6fed81cd8 100644 --- a/src/components/ClaimRewards.js +++ b/src/components/ClaimRewards.js @@ -15,35 +15,39 @@ function ClaimRewards(props) { async function claim(){ props.setLoading(true) - let totalReward = props.rewards.amount - let perValidatorReward = totalReward let signAndBroadcast = props.stargateClient.signAndBroadcast + const gasSimMessages = buildMessages(props.validatorRewards) + let gas + try { + gas = await props.stargateClient.simulate(props.address, gasSimMessages) + } catch (error) { + props.setLoading(false) + props.setError('Failed to broadcast: ' + error.message) + return + } - if(props.restake && perValidatorReward > 0){ - let messages = buildMessages(props.validators, perValidatorReward) + const fee = props.stargateClient.getFee(gas) + const feeAmount = fee.amount[0].amount - try { - gas = await props.stargateClient.simulate(props.address, messages) - } catch (error) { - props.setLoading(false) - props.setError('Failed to broadcast: ' + error.message) - return + const totalReward = props.validatorRewards.reduce((sum, validatorReward) => sum + validatorReward.reward, 0); + const adjustedValidatorRewards = props.validatorRewards.map(validatorReward => { + const shareOfFee = (validatorReward.reward / totalReward) * feeAmount; // To take a proportional amount from each validator relative to total reward + return { + validatorAddress: validatorReward.validatorAddress, + reward: validatorReward.reward - shareOfFee, } - - const fee = props.stargateClient.getFee(gas) - const feeAmount = fee.amount[0].amount + }) signAndBroadcast = props.stargateClient.signAndBroadcastWithoutBalanceCheck - totalReward = (totalReward - feeAmount) - perValidatorReward = parseInt(totalReward / props.validators.length) - } - if(perValidatorReward <= 0){ + + if(adjustedValidatorRewards.some(validatorReward => validatorReward.reward <= 0)) { props.setLoading(false) props.setError('Reward is too low') return } - let messages = buildMessages(props.validators, perValidatorReward) + + let messages = buildMessages(adjustedValidatorRewards) try { gas = gas || await props.stargateClient.simulate(props.address, messages) } catch (error) { @@ -64,15 +68,16 @@ function ClaimRewards(props) { }) } - function buildMessages(validators, perValidatorReward){ - return validators.map(el => { + // Expects a map of string -> string (validator -> reward) + function buildMessages(validatorRewards){ + return validatorRewards.map(validatorReward => { let valMessages = [] valMessages.push({ typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", value: MsgWithdrawDelegatorReward.fromPartial({ delegatorAddress: props.address, - validatorAddress: el + validatorAddress: validatorReward.validatorAddress }) }) @@ -81,8 +86,8 @@ function ClaimRewards(props) { typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", value: MsgDelegate.fromPartial({ delegatorAddress: props.address, - validatorAddress: el, - amount: coin(perValidatorReward, props.network.denom) + validatorAddress: validatorReward.validatorAddress, + amount: coin(parseInt(validatorReward.reward), props.network.denom) }) }) } @@ -93,7 +98,7 @@ function ClaimRewards(props) { return ( <> - {props.validators.length > 0 && ( + {props.validatorRewards.length > 0 && ( claim()}> {props.restake ? 'Manual Compound' : 'Claim Rewards'} diff --git a/src/components/Delegations.js b/src/components/Delegations.js index e2f18e782..0a7804278 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -281,6 +281,26 @@ class Delegations extends React.Component { }; } + validatorRewards(validators) { + if (!this.state.rewards) return; + + const denom = this.props.network.denom; + const validatorRewards = Object.keys(this.state.rewards) + .map(validator => { + const validatorReward = this.state.rewards[validator]; + const reward = validatorReward.reward.find((el) => el.denom === denom) + return { + validatorAddress: validator, + reward: reward ? parseInt(reward.amount) : undefined, + } + }) + .filter(validatorReward => { + return validatorReward.reward && (validators === undefined || validators.includes(validatorReward.validatorAddress)) + }); + + return validatorRewards; + } + denomRewards(rewards) { return rewards.reward.find( (reward) => reward.denom === this.props.network.denom @@ -462,8 +482,7 @@ class Delegations extends React.Component { @@ -475,8 +494,7 @@ class Delegations extends React.Component { restake={true} network={this.props.network} address={this.props.address} - validators={[validatorAddress]} - rewards={this.totalRewards([validatorAddress])} + validatorRewards={this.validatorRewards([validatorAddress])} stargateClient={this.props.stargateClient} onClaimRewards={this.onClaimRewards} setLoading={(loading) => @@ -714,8 +732,7 @@ class Delegations extends React.Component { Date: Thu, 24 Mar 2022 22:35:37 +0800 Subject: [PATCH 268/862] CATS | update networks.json --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 99fd41fde..cd6248f8d 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1877,7 +1877,7 @@ "minimumReward": 100000 }, { - "address": "lumvaloper1xkv494sduqkpadwesqlsp6069yepsj587dvf46", + "address": "lumvaloper1y8jham0v3lhlnwmsh0nw8g92fkqxt9gsuml6ux", "botAddress": "lum1js340eq8082essggjtv8lf9df7qlnny88wzu7n", "runTime": [ "09:00", @@ -2789,7 +2789,7 @@ "address": "cerberusvaloper1xkv494sduqkpadwesqlsp6069yepsj58axja5l", "botAddress": "cerberus1js340eq8082essggjtv8lf9df7qlnny8wrlu7v", "runTime": "every 5 minutes", - "minimumReward": 50000000 + "minimumReward": 5000000 }, { "address": "cerberusvaloper1ga0xxzst2hjyjtfqnffz2mapn8vk8ufysqsn30", From cc14aa16d02239f7bb9ba792b2783df6800ee2a3 Mon Sep 17 00:00:00 2001 From: Validatus <92102712+Validatus@users.noreply.github.com> Date: Thu, 24 Mar 2022 23:37:46 +0100 Subject: [PATCH 269/862] add Validatus as REStake operator add Validatus as REStake operator --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 99fd41fde..a427f1859 100644 --- a/src/networks.json +++ b/src/networks.json @@ -77,6 +77,12 @@ "runTime": "21:00", "minimumReward": 10000 }, + { + "address": "osmovaloper19v94c3z7ckarwsum76kaagma0wqsqhh54xucdk", + "botAddress": "osmo1yuukehecl8czrd28q0tmxpvwce9lzt23pj35zp", + "runTime": "21:00", + "minimumReward": 10000 + }, { "address": "osmovaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5w37wr9t", "botAddress": "osmo1gvgcxwdk4j46gx3knhuackvqr2hta29zzygz95", From 1a3b0949786766d7d8d215d6ee35cf57274534a4 Mon Sep 17 00:00:00 2001 From: azstake <75195495+nhhtrung@users.noreply.github.com> Date: Fri, 25 Mar 2022 14:41:26 +0700 Subject: [PATCH 270/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 99fd41fde..00e88d8e4 100644 --- a/src/networks.json +++ b/src/networks.json @@ -3084,6 +3084,12 @@ "botAddress": "cerberus1330aewfwzu30vznhxw7aevaz0kn35fdyzc7y3z", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper18zf2m4np64ncjr2hg0plk8tjqma25farw2nac5", + "botAddress": "cerberus1kxd2f28ugzwyv9twexw88kpajvgf94xgpl9hmq", + "runTime": "every 1 hour", + "minimumReward": 1000000 } ], "authzSupport": true From c46c3eaaaddfa5a42f0346c688f679cb3834177a Mon Sep 17 00:00:00 2001 From: Dabu Dabu <102351472+DabuDabuValidator@users.noreply.github.com> Date: Fri, 25 Mar 2022 08:23:08 +0000 Subject: [PATCH 271/862] Add Dabu Dabu as restake operator --- src/networks.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/networks.json b/src/networks.json index 99fd41fde..42cdf6cf9 100644 --- a/src/networks.json +++ b/src/networks.json @@ -878,6 +878,12 @@ "botAddress": "chihuahua1330aewfwzu30vznhxw7aevaz0kn35fdya2nr9t", "runTime": "every 30 minutes", "minimumReward": 100000000 + }, + { + "address": "chihuahuavaloper12d2a8hfrq784u9lfxp8qj2huxxglejzrqlylwt", + "botAddress": "chihuahua18m7pnt8enhrlqf4rh7ff9uvcfsr6tmlm4l2wqj", + "runTime": "every 1 hour", + "minimumReward": 100000000 } ], "authzSupport": false @@ -3084,6 +3090,12 @@ "botAddress": "cerberus1330aewfwzu30vznhxw7aevaz0kn35fdyzc7y3z", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + { + "address": "cerberusvaloper1gl6mw8p76erqeltgt6kkqac57m6h5ufy28kas6", + "botAddress": "cerberus18m7pnt8enhrlqf4rh7ff9uvcfsr6tmlm2d8f5m", + "runTime": "every 1 hour", + "minimumReward": 10000000 } ], "authzSupport": true From 5396a2c515f903d15887a671ea2fcfcde0f88b3b Mon Sep 17 00:00:00 2001 From: Jerry Chong Date: Fri, 25 Mar 2022 16:46:58 +0800 Subject: [PATCH 272/862] Add Jerry's Pool --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 99fd41fde..b19de9821 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1618,6 +1618,12 @@ "botAddress": "cro1hl0jgxre5z0nkyjj07c5vfdy44yk44atpy24nv", "runTime": "22:30", "minimumReward": 1000000 + }, + { + "address": "crocncl1s7cu28403gzdvy5tttyskm3zxjejxcv6j5ngp9", + "botAddress": "cro1lj3qga40dc0w2pacfrd2s6puk43ua2vczlv690", + "runTime": "16:30", + "minimumReward": 1000000 } ], "authzSupport": true From 030a8dabce03b284b76073035c64b90a3d34f74e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 25 Mar 2022 09:57:46 +0000 Subject: [PATCH 273/862] Increase validator timeout --- src/utils/QueryClient.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 3f096d67d..83e23a921 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -34,7 +34,7 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { "/cosmos/staking/v1beta1/validators?" + searchParams.toString(), { - timeout: 5000, + timeout: 6000, } ) .then((res) => res.data); From bbf4c88efda7ba99cbc54cc8ab493774700f6ca9 Mon Sep 17 00:00:00 2001 From: kinrokinro Date: Fri, 25 Mar 2022 14:59:11 +0300 Subject: [PATCH 274/862] Umee Nodes.Guru operator --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 68215fb52..db95e3810 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2415,6 +2415,12 @@ "botAddress": "umee1330aewfwzu30vznhxw7aevaz0kn35fdyvfrjqm", "runTime": "every 30 minutes", "minimumReward": 1000000 + }, + { + "address": "umeevaloper1l4xvfq5uj7624m73nn8j4mgra7xq4cpf08q2xj", + "botAddress": "umee1cyjx4pxqx6hxltp8je8mpwrvjhnq3kktj26xms", + "runTime": "every 1 hour", + "minimumReward": 10000 } ], "authzSupport": true From 881352311868750dd426f54fb477966d584964af Mon Sep 17 00:00:00 2001 From: ivandiazperez <42877828+ivandiazperez@users.noreply.github.com> Date: Fri, 25 Mar 2022 16:27:16 +0100 Subject: [PATCH 275/862] Add files via upload --- src/networks.json | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/networks.json b/src/networks.json index 68215fb52..d4252cd86 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1018,6 +1018,12 @@ "botAddress": "gravity1jrjdqzxchlt09gj6qvn4ds8suxh9aqwynngmtu", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "gravityvaloper1yyduggdnk5kgszamt7s9f0ep2n6hylxrtatu5g", + "botAddress": "gravity1k8mymzl8sapy2yqyyce5a35u09rnuae7shvmwg", + "runTime": "18:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1078,6 +1084,12 @@ "botAddress": "regen1330aewfwzu30vznhxw7aevaz0kn35fdypa43jd", "runTime": "every 30 minutes", "minimumReward": 1000000 + }, + { + "address": "regenvaloper1vn6sachfy4aacyl5gwc4ykkn6kaqsqv3vzjd8t", + "botAddress": "regen1k8mymzl8sapy2yqyyce5a35u09rnuae7t94lay", + "runTime": "18:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1357,6 +1369,12 @@ "botAddress": "bcna15enapnszjg5en949x326ae3deu35dmw5hjed0z", "runTime": "every 30 minutes", "minimumReward": 1000000 + }, + { + "address": "bcnavaloper1ldzku04m0hz2qslr0e3ddy2t4vgm63x8ngjs3r", + "botAddress": "bcna1k8mymzl8sapy2yqyyce5a35u09rnuae7whwzrj", + "runTime": "18:00", + "minimumReward": 1000000 } ], "authzSupport": false @@ -1621,6 +1639,12 @@ "botAddress": "desmos1g4zywrluj6ac0xpu6f6jqul52rv0am66r8qy5h", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "desmosvaloper1c5aqg0qjlqj0g99ee0seaeneqgpcz07sepmqx6", + "botAddress": "desmos1k8mymzl8sapy2yqyyce5a35u09rnuae7qlnnuc", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": true @@ -2180,6 +2204,12 @@ "botAddress": "stars1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyrld799", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "starsvaloper1j74f2ef2p9ue7tz0jlcg6ejnjhp5evv0wyw0av", + "botAddress": "stars1k8mymzl8sapy2yqyyce5a35u09rnuae7qmf7q3", + "runTime": "18:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2415,6 +2445,12 @@ "botAddress": "umee1330aewfwzu30vznhxw7aevaz0kn35fdyvfrjqm", "runTime": "every 30 minutes", "minimumReward": 1000000 + }, + { + "address": "umeevaloper1gq5fvkv479n26x63dkh8zscqahxu4tz4ph3jn9", + "botAddress": "umee1k8mymzl8sapy2yqyyce5a35u09rnuae7x3ru0j", + "runTime": "21:00", + "minimumReward": 1000000 } ], "authzSupport": true @@ -2630,6 +2666,12 @@ "botAddress": "ixo1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygky328", "runTime": "every 1 hour", "minimumReward": 100000 + }, + { + "address": "ixovaloper1v275n8eyakuqu5pfpkzczf3s50e80a8ztrwfh5", + "botAddress": "ixo1k8mymzl8sapy2yqyyce5a35u09rnuae7tjq30n", + "runTime": "21:00", + "minimumReward": 100000 } ], "authzSupport": false @@ -2811,6 +2853,12 @@ "botAddress": "fetch14a78h6202u4xg6phjnd8rmr5fq98zzdlkveweh", "runTime": "every 4 hour", "minimumReward": 10000000000000000 + }, + { + "address": "fetch1k8mymzl8sapy2yqyyce5a35u09rnuae786h8fh", + "botAddress": "fetchvaloper1874jxj35dlywfq65cupdnsp02lkqcjqnpgln2w", + "runTime": "18:00", + "minimumReward": 10000000000000000 } ], "authzSupport": false From 86f7a24a991f0bd2fe9f55299951b6030955d8d9 Mon Sep 17 00:00:00 2001 From: altrbo Date: Fri, 25 Mar 2022 16:52:33 +0100 Subject: [PATCH 276/862] Add Bostrom network, changed exec time for Bro_n_Bro --- src/networks.json | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/networks.json b/src/networks.json index 68215fb52..e2e9d5565 100644 --- a/src/networks.json +++ b/src/networks.json @@ -176,7 +176,7 @@ { "address": "osmovaloper13tk45jkxgf7w0nxquup3suwaz2tx483xe832ge", "botAddress": "osmo1grfk7t0k42yxpwqhr7kt4te7j73m2a3v27wqh5", - "runTime": "13:37", + "runTime": "19:37", "minimumReward": 10000 }, { @@ -410,7 +410,7 @@ { "address": "junovaloper1quqxfrxkycr0uzt4yk0d57tcq3zk7srm7sm6r8", "botAddress": "juno1grfk7t0k42yxpwqhr7kt4te7j73m2a3v5h7tx6", - "runTime": "13:37", + "runTime": "19:37", "minimumReward": 10000 }, { @@ -980,7 +980,7 @@ { "address": "gravityvaloper1vyd4k5j636erx5y5kdqghdu3rfjtwc48vdc7r6", "botAddress": "gravity1grfk7t0k42yxpwqhr7kt4te7j73m2a3vx40gyw", - "runTime": "13:37", + "runTime": "19:37", "minimumReward": 10000 }, { @@ -1435,7 +1435,7 @@ { "address": "emoneyvaloper149vyxd36kxpg46rralaw6eejv4d9daqc3nv642", "botAddress": "emoney1grfk7t0k42yxpwqhr7kt4te7j73m2a3vdx8ykm", - "runTime": "13:37", + "runTime": "19:37", "minimumReward": 100000 }, { @@ -1742,7 +1742,7 @@ { "address": "evmosvaloper1ce4vh0e5kanlgc7z0rhcemvd8erjnfzcyfecl7", "botAddress": "evmos1grfk7t0k42yxpwqhr7kt4te7j73m2a3vqyv7mw", - "runTime": "13:37", + "runTime": "19:37", "minimumReward": 1000000 }, { @@ -2142,7 +2142,7 @@ { "address": "starsvaloper1y58hfnm90r4efhlydx0gavz57lvm7k6uulkg3h", "botAddress": "stars1grfk7t0k42yxpwqhr7kt4te7j73m2a3vke2d2h", - "runTime": "13:37", + "runTime": "19:37", "minimumReward": 10000 }, { @@ -3243,5 +3243,24 @@ } ], "authzSupport": true + }, + { + "name": "bostrom", + "restUrl": [ + "https://lcd.bostrom.cybernode.ai/" + ], + "rpcUrl": [ + "https://rpc.bostrom.cybernode.ai/" + ], + "gasPrice": "0boot", + "operators": [ + { + "address": "bostromvaloper1ydc5fy9fjdygvgw36u49yj39fr67pd9m5qexm8", + "botAddress": "bostrom1grfk7t0k42yxpwqhr7kt4te7j73m2a3vpkfrlp", + "runTime": "19:37", + "minimumReward": 100000 + } + ], + "authzSupport": true } ] From e3c90a3251d66ca20cd13df33a2cf12d107d9e24 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 26 Mar 2022 01:08:08 +0000 Subject: [PATCH 277/862] Load validator images after network change --- src/components/App.js | 1 + src/components/NetworkSelect.js | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 39fd37061..a0d209fd0 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -70,6 +70,7 @@ class App extends React.Component { setNetwork(){ const network = this.props.network if(!network) return + this.loadValidatorImages(network, this.props.validators) return this.setState({ error: false, diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index 43d57a839..bd8147fca 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -23,8 +23,6 @@ function NetworkSelect(props) { {networks: [], operators: [], network: {value: ''}} ) - const {loadValidatorImages} = props - const handleOpen = () => { setSelectedNetwork(props.network) setValidators(props.validators) @@ -80,7 +78,6 @@ function NetworkSelect(props) { setValidators({}) network.getValidators().then(data => { setValidators(data) - loadValidatorImages(network, data) setLoading(false) }, (error) => { setError('Unable to connect to this network currently. Try again later.') From b8a273f982e82b1276c3588507be45493ea246d4 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 26 Mar 2022 01:18:33 +0000 Subject: [PATCH 278/862] Add Starname and fix bostrom URLs --- src/networks.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 37e23c7a9..b0210d57b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -3301,10 +3301,10 @@ { "name": "bostrom", "restUrl": [ - "https://lcd.bostrom.cybernode.ai/" + "https://rest.cosmos.directory/bostrom" ], "rpcUrl": [ - "https://rpc.bostrom.cybernode.ai/" + "https://rpc.cosmos.directory/bostrom" ], "gasPrice": "0boot", "operators": [ @@ -3316,5 +3316,17 @@ } ], "authzSupport": true + }, + { + "name": "starname", + "restUrl": [ + "https://rest.cosmos.directory/starname" + ], + "rpcUrl": [ + "https://rpc.cosmos.directory/starname" + ], + "gasPrice": "10uvoi", + "operators": [], + "authzSupport": false } ] From 60697123d159c7951fff970337943c3cea5e1e45 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 26 Mar 2022 02:14:54 +0000 Subject: [PATCH 279/862] Improve initial load time and order --- src/components/App.js | 73 +------------ src/components/Delegations.js | 180 ++++++++++++++++++++++---------- src/components/NetworkFinder.js | 6 +- src/components/NetworkSelect.js | 1 - src/components/Wallet.js | 1 - src/utils/QueryClient.mjs | 4 +- 6 files changed, 136 insertions(+), 129 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index a0d209fd0..5bdf1367c 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -26,11 +26,9 @@ import PoweredByAkash from '../assets/powered-by-akash.svg' class App extends React.Component { constructor(props) { super(props); - this.state = {validatorImages: {}} + this.state = {} this.connect = this.connect.bind(this); this.showNetworkSelect = this.showNetworkSelect.bind(this); - this.getValidatorImage = this.getValidatorImage.bind(this); - this.loadValidatorImages = this.loadValidatorImages.bind(this); this.getBalance = this.getBalance.bind(this); } @@ -45,10 +43,6 @@ class App extends React.Component { } } window.addEventListener("keplr_keystorechange", this.connect) - if(this.props.operators){ - this.loadValidatorImages(this.props.network, _.compact(this.props.operators.map(el => el.validatorData))) - } - this.loadValidatorImages(this.props.network, this.props.validators) } async componentDidUpdate(prevProps){ @@ -70,7 +64,6 @@ class App extends React.Component { setNetwork(){ const network = this.props.network if(!network) return - this.loadValidatorImages(network, this.props.validators) return this.setState({ error: false, @@ -158,65 +151,6 @@ class App extends React.Component { }) } - getValidatorImage(network, validatorAddress, expireCache){ - const images = this.state.validatorImages[network.name] || {} - if(images[validatorAddress]){ - return images[validatorAddress] - } - return this.getValidatorImageCache(validatorAddress, expireCache) - } - - getValidatorImageCache(validatorAddress, expireCache){ - const cache = localStorage.getItem(validatorAddress) - if(!cache) return - - let cacheData = {} - try { - cacheData = JSON.parse(cache) - } catch { - cacheData.url = cache - } - if(!cacheData.url) return - if(!expireCache) return cacheData.url - - const cacheTime = cacheData.time && new Date(cacheData.time) - if(!cacheData.time) return - - const expiry = new Date() - 1000 * 60 * 60 * 24 * 3 - if(cacheTime >= expiry) return cacheData.url - } - - async loadValidatorImages(network, validators) { - this.setState((state, props) => ({ - validatorImages: _.set(state.validatorImages, network.name, state.validatorImages[network.name] || {}) - })); - const calls = Object.values(validators).map(validator => { - return () => { - if(validator.description.identity && !this.getValidatorImage(network, validator.operator_address, true)){ - return fetch("https://keybase.io/_/api/1.0/user/lookup.json?fields=pictures&key_suffix=" + validator.description.identity) - .then((response) => { - return response.json(); - }).then((data) => { - if(data.them && data.them[0] && data.them[0].pictures){ - const imageUrl = data.them[0].pictures.primary.url - this.setState((state, props) => ({ - validatorImages: _.set(state.validatorImages, [network.name, validator.operator_address], imageUrl) - })); - localStorage.setItem(validator.operator_address, JSON.stringify({url: imageUrl, time: +new Date()})) - } - }, error => { }) - }else{ - return null - } - } - }) - const batchCalls = _.chunk(calls, 1); - - for (const batchCall of batchCalls) { - await Promise.all(batchCall.map(call => call())) - } - } - async getBalance() { this.state.queryClient.getBalance(this.state.address, this.props.network.denom) .then( @@ -272,8 +206,8 @@ class App extends React.Component {
    {this.setState({showNetworkSelect: false})}} networks={this.props.networks} network={this.props.network} - validators={this.props.validators} getValidatorImage={this.getValidatorImage} - changeNetwork={this.props.changeNetwork} loadValidatorImages={this.loadValidatorImages} /> + validators={this.props.validators} + changeNetwork={this.props.changeNetwork} />
    @@ -304,7 +238,6 @@ class App extends React.Component { validators={this.props.validators} balance={this.state.balance} getBalance={this.getBalance} - getValidatorImage={this.getValidatorImage} queryClient={this.state.queryClient} stargateClient={this.state.stargateClient} /> diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 0a7804278..2ad20f4c9 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -17,8 +17,10 @@ import { CheckCircle, XCircle } from "react-bootstrap-icons"; class Delegations extends React.Component { constructor(props) { super(props); - this.state = { operatorGrants: {}, validatorLoading: {}, validatorApy: {} }; + this.state = { operatorGrants: {}, validatorLoading: {}, validatorImages: {}, validatorApy: {} }; + this.getValidatorImage = this.getValidatorImage.bind(this); + this.loadValidatorImages = this.loadValidatorImages.bind(this); this.setError = this.setError.bind(this); this.setClaimLoading = this.setClaimLoading.bind(this); this.onClaimRewards = this.onClaimRewards.bind(this); @@ -56,15 +58,20 @@ class Delegations extends React.Component { clearInterval(this.state.refreshInterval); } - refresh() { + async refresh() { this.getRewards(); + await this.getTestGrant(); + if (!this.state.authzMissing && this.props.operators.length) { + await this.getGrants(); + } this.calculateApy(); - this.refreshInterval(); - if (this.props.operators.length) { - this.getGrants(); - } else { - this.getTestGrant(); + if(this.props.operators){ + this.loadValidatorImages(this.props.network, _.compact(this.props.operators.map(el => el.validatorData))) + this.loadValidatorImages(this.props.network, _.omit(this.props.validators, this.props.operators.map(el => el.address))) + }else{ + this.loadValidatorImages(this.props.network, this.props.validators) } + this.refreshInterval(); } refreshInterval() { @@ -106,49 +113,57 @@ class Delegations extends React.Component { }) } - getGrants() { - this.props.operators.forEach((operator) => { - const { botAddress, address } = operator; - this.props.queryClient.getGrants(botAddress, this.props.address).then( - (result) => { - let grantValidators; - if (result.stakeGrant) { - grantValidators = - result.stakeGrant.authorization.allow_list.address; - } - const operatorGrant = { - claimGrant: result.claimGrant, - stakeGrant: result.stakeGrant, - validators: grantValidators || [], - grantsValid: !!( - result.claimGrant && - result.stakeGrant && - grantValidators.includes(address) - ), - grantsExist: !!(result.claimGrant || result.stakeGrant), - }; - this.setState((state, props) => ({ - operatorGrants: _.set( - state.operatorGrants, - botAddress, - operatorGrant - ), - })); - }, - (error) => { - if (error.response && error.response.status === 501) { - this.setState({ authzMissing: true }); - } else { - this.setState({ error: "Failed to get grants. Please refresh" }); + async getGrants() { + const calls = this.props.operators.map((operator) => { + return () => { + const { botAddress, address } = operator; + return this.props.queryClient.getGrants(botAddress, this.props.address).then( + (result) => { + let grantValidators; + if (result.stakeGrant) { + grantValidators = + result.stakeGrant.authorization.allow_list.address; + } + const operatorGrant = { + claimGrant: result.claimGrant, + stakeGrant: result.stakeGrant, + validators: grantValidators || [], + grantsValid: !!( + result.claimGrant && + result.stakeGrant && + grantValidators.includes(address) + ), + grantsExist: !!(result.claimGrant || result.stakeGrant), + }; + this.setState((state, props) => ({ + operatorGrants: _.set( + state.operatorGrants, + botAddress, + operatorGrant + ), + })); + }, + (error) => { + if (error.response && error.response.status === 501) { + this.setState({ authzMissing: true }); + } else { + this.setState({ error: "Failed to get grants. Please refresh" }); + } } - } - ); + ); + } }); + + const batchCalls = _.chunk(calls, 10); + + for (const batchCall of batchCalls) { + await Promise.all(batchCall.map(call => call())) + } } getTestGrant() { - this.props.queryClient - .getGrants(this.props.network.testAddress, this.props.address) + return this.props.queryClient + .getGrants() .then( (result) => {}, (error) => { @@ -159,6 +174,65 @@ class Delegations extends React.Component { ); } + getValidatorImage(network, validatorAddress, expireCache){ + const images = this.state.validatorImages[network.name] || {} + if(images[validatorAddress]){ + return images[validatorAddress] + } + return this.getValidatorImageCache(validatorAddress, expireCache) + } + + getValidatorImageCache(validatorAddress, expireCache){ + const cache = localStorage.getItem(validatorAddress) + if(!cache) return + + let cacheData = {} + try { + cacheData = JSON.parse(cache) + } catch { + cacheData.url = cache + } + if(!cacheData.url) return + if(!expireCache) return cacheData.url + + const cacheTime = cacheData.time && new Date(cacheData.time) + if(!cacheData.time) return + + const expiry = new Date() - 1000 * 60 * 60 * 24 * 3 + if(cacheTime >= expiry) return cacheData.url + } + + async loadValidatorImages(network, validators) { + this.setState((state, props) => ({ + validatorImages: _.set(state.validatorImages, network.name, state.validatorImages[network.name] || {}) + })); + const calls = Object.values(validators).map(validator => { + return () => { + if(validator.description.identity && !this.getValidatorImage(network, validator.operator_address, true)){ + return fetch("https://keybase.io/_/api/1.0/user/lookup.json?fields=pictures&key_suffix=" + validator.description.identity) + .then((response) => { + return response.json(); + }).then((data) => { + if(data.them && data.them[0] && data.them[0].pictures){ + const imageUrl = data.them[0].pictures.primary.url + this.setState((state, props) => ({ + validatorImages: _.set(state.validatorImages, [network.name, validator.operator_address], imageUrl) + })); + localStorage.setItem(validator.operator_address, JSON.stringify({url: imageUrl, time: +new Date()})) + } + }, error => { }) + }else{ + return null + } + } + }) + const batchCalls = _.chunk(calls, 1); + + for (const batchCall of batchCalls) { + await Promise.all(batchCall.map(call => call())) + } + } + onGrant(operator) { const operatorGrant = { grantsValid: true, @@ -336,7 +410,7 @@ class Delegations extends React.Component { @@ -526,7 +600,7 @@ class Delegations extends React.Component { (this.props.delegations[validatorAddress] || {}) .balance } - getValidatorImage={this.props.getValidatorImage} + getValidatorImage={this.getValidatorImage} stargateClient={this.props.stargateClient} onDelegate={this.onClaimRewards} /> @@ -541,7 +615,7 @@ class Delegations extends React.Component { (this.props.delegations[validatorAddress] || {}) .balance } - getValidatorImage={this.props.getValidatorImage} + getValidatorImage={this.getValidatorImage} stargateClient={this.props.stargateClient} onDelegate={this.onClaimRewards} /> @@ -559,7 +633,7 @@ class Delegations extends React.Component { validatorApy={this.state.validatorApy} operators={this.props.operators} availableBalance={this.props.balance} - getValidatorImage={this.props.getValidatorImage} + getValidatorImage={this.getValidatorImage} stargateClient={this.props.stargateClient} onDelegate={this.onClaimRewards} /> @@ -641,7 +715,7 @@ class Delegations extends React.Component { operators={this.props.operators} validators={this.props.validators} validatorApy={this.state.validatorApy} - getValidatorImage={this.props.getValidatorImage} + getValidatorImage={this.getValidatorImage} availableBalance={this.props.balance} stargateClient={this.props.stargateClient} onDelegate={this.props.onAddValidator} @@ -708,7 +782,7 @@ class Delegations extends React.Component { address={this.props.address} validators={this.props.validators} validatorApy={this.state.validatorApy} - getValidatorImage={this.props.getValidatorImage} + getValidatorImage={this.getValidatorImage} delegations={this.props.delegations} availableBalance={this.props.balance} stargateClient={this.props.stargateClient} diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index cce4d0002..74d8dc05c 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -104,8 +104,10 @@ function NetworkFinder() { ) } - return changeNetwork(network, validators)} />; + return changeNetwork(network, validators)} + />; } export default NetworkFinder diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index bd8147fca..d7210e976 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -1,7 +1,6 @@ import React, { useState, useReducer, useEffect } from 'react'; import Network from '../utils/Network.mjs' -import ValidatorImage from './ValidatorImage' import { Button, diff --git a/src/components/Wallet.js b/src/components/Wallet.js index 350ea7ea2..5ff42b8f7 100644 --- a/src/components/Wallet.js +++ b/src/components/Wallet.js @@ -94,7 +94,6 @@ class Wallet extends React.Component { balance={this.props.balance} operators={this.props.operators} validators={this.props.validators} - getValidatorImage={this.props.getValidatorImage} getBalance={this.props.getBalance} delegations={this.state.delegations} queryClient={this.props.queryClient} diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 83e23a921..4aae62fa1 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -113,8 +113,8 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { const getGrants = (botAddress, address, opts) => { const searchParams = new URLSearchParams(); - searchParams.append("grantee", botAddress); - searchParams.append("granter", address); + if(botAddress) searchParams.append("grantee", botAddress); + if(address) searchParams.append("granter", address); // searchParams.append("msg_type_url", "/cosmos.staking.v1beta1.MsgDelegate"); return axios .get(restUrl + "/cosmos/authz/v1beta1/grants?" + searchParams.toString(), opts) From 1f1709939ff3584f79fa414e6f503a7d75c8c5e7 Mon Sep 17 00:00:00 2001 From: poorbuthappy <97994242+poorbuthappy@users.noreply.github.com> Date: Sat, 26 Mar 2022 10:26:33 +0800 Subject: [PATCH 280/862] Update networks.json Add PoorButHappy validator for Bostrom --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index b0210d57b..6100e661c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -3313,7 +3313,13 @@ "botAddress": "bostrom1grfk7t0k42yxpwqhr7kt4te7j73m2a3vpkfrlp", "runTime": "19:37", "minimumReward": 100000 - } + }, + { + "address": "bostromvaloper1r28nhrkwcvqs4qss2l7zqdvc2qfdl8arglv6nn", + "botAddress": "bostrom14a78h6202u4xg6phjnd8rmr5fq98zzdlxzye98", + "runTime": "every 4 hour", + "minimumReward": 100000 + } ], "authzSupport": true }, From 8becf9f4748a9181278e5f5b1bd9f5f04b8315e2 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 26 Mar 2022 03:39:17 +0000 Subject: [PATCH 281/862] Fix validator image sizing --- src/components/App.css | 12 ++++++------ src/components/ValidatorImage.js | 3 ++- src/components/Validators.js | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/App.css b/src/components/App.css index 15378d580..809503d5c 100644 --- a/src/components/App.css +++ b/src/components/App.css @@ -7,16 +7,16 @@ header a.moniker{ } .btn-circle-sm { - width: 40px; - height: 40px; - line-height: 40px; + width: 30px; + height: 30px; + line-height: 30px; font-size: 0.9rem; } .btn-circle-lg { - width: 55px; - height: 55px; - line-height: 55px; + width: 40px; + height: 40px; + line-height: 40px; font-size: 1.1rem; } diff --git a/src/components/ValidatorImage.js b/src/components/ValidatorImage.js index 79e4d131b..b1c225a17 100644 --- a/src/components/ValidatorImage.js +++ b/src/components/ValidatorImage.js @@ -1,10 +1,11 @@ function ValidatorImage(props) { const className = props.className || '' + const defaultSize = !props.width || props.width > 30 ? 'lg' : 'sm' return ( <> {props.imageUrl ? - :
    + : } ); diff --git a/src/components/Validators.js b/src/components/Validators.js index d497ea8a1..056db287d 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -55,9 +55,9 @@ function Validators(props) {
    - +
    -
    +
    {item.description.moniker}
    @@ -84,7 +84,7 @@ function Validators(props) { )} - From 671a44b40dedbc2e9a29ce337752732f0cb16eec Mon Sep 17 00:00:00 2001 From: Luis Lopes Date: Sat, 26 Mar 2022 10:11:59 +0000 Subject: [PATCH 282/862] Add tedcrypto bitsong --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 6100e661c..c7a1b15c9 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2550,6 +2550,12 @@ "botAddress": "bitsong197yc3g8drkf8fyv6fr4ynddy2n22kauunjhehs", "runTime": "22:00", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper1qxw4fjged2xve8ez7nu779tm8ejw92rv0vcuqr", + "botAddress": "bitsong1fx6eahccn5n9m6zwz0p4ynff0asnllvtyvxvd5", + "runTime": "22:00", + "minimumReward": 100000 } ], "authzSupport": true From 23fbd04883f2a632d490195f8a3a769f76d9a974 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 26 Mar 2022 11:50:32 +0000 Subject: [PATCH 283/862] Fix BioClick addresses --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index c7a1b15c9..12ee1195a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2867,8 +2867,8 @@ "minimumReward": 10000000000000000 }, { - "address": "fetch1k8mymzl8sapy2yqyyce5a35u09rnuae786h8fh", - "botAddress": "fetchvaloper1874jxj35dlywfq65cupdnsp02lkqcjqnpgln2w", + "address": "fetchvaloper1874jxj35dlywfq65cupdnsp02lkqcjqnpgln2w", + "botAddress": "fetch1k8mymzl8sapy2yqyyce5a35u09rnuae786h8fh", "runTime": "18:00", "minimumReward": 10000000000000000 } From 415b6b0badee1dec68b772468f12aa8480a37200 Mon Sep 17 00:00:00 2001 From: Anton Kenikh Date: Sat, 26 Mar 2022 15:10:06 +0100 Subject: [PATCH 284/862] Allow to import and override --- .gitignore | 3 + scripts/autostake.mjs | 329 +----------------------------------------- scripts/base.mjs | 328 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 332 insertions(+), 328 deletions(-) create mode 100644 scripts/base.mjs diff --git a/.gitignore b/.gitignore index eeb55de6f..cd7b2112f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ src/networks.local.json +scripts/* +!scripts/autostake.mjs +!scripts/base.mjs # dependencies /node_modules diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 56ea921b9..f1921dbff 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -1,331 +1,4 @@ -import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; -import { Slip10RawIndex, pathToString } from "@cosmjs/crypto"; -import Network from '../src/utils/Network.mjs' -import {timeStamp, mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' - -import { - coin -} from '@cosmjs/stargate' - -import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx.js"; -import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx.js"; -import { MsgExec } from "cosmjs-types/cosmos/authz/v1beta1/tx.js"; - -import fs from 'fs' -import _ from 'lodash' - -class Autostake { - constructor(){ - this.mnemonic = process.env.MNEMONIC - if(!this.mnemonic){ - timeStamp('Please provide a MNEMONIC environment variable') - process.exit() - } - } - - async run(networkName){ - const calls = this.getNetworksData().map(data => { - return async () => { - if(networkName && data.name !== networkName) return - if(data.enabled === false) return - - let client - try { - client = await this.getClient(data) - } catch (error) { - return timeStamp('Failed to connect', error.message) - } - - if(!client) return timeStamp('Skipping') - - timeStamp('Using REST URL', client.network.restUrl) - timeStamp('Using RPC URL', client.network.rpcUrl) - - if(!data.overriden) timeStamp('You are using public nodes, script may fail with many delegations. Check the README to use your own') - - try { - await this.runNetwork(client) - } catch (error) { - return timeStamp('Autostake failed, skipping network', error.message) - } - } - }) - await executeSync(calls, 1) - } - - async runNetwork(client){ - timeStamp('Running autostake') - await this.checkBalance(client) - - timeStamp('Finding delegators...') - const addresses = await this.getDelegations(client).then(delegations => { - return delegations.map(delegation => { - if (delegation.balance.amount === 0) return - - return delegation.delegation.delegator_address - }) - }) - - timeStamp("Checking", addresses.length, "delegators for grants...") - let grantedAddresses = await this.getGrantedAddresses(client, addresses) - - timeStamp("Found", grantedAddresses.length, "delegators with valid grants...") - - let grantMessages = await this.getAutostakeMessages(client, grantedAddresses, [client.operator.address]) - await this.autostake(client, grantMessages) - timeStamp(client.network.prettyName, "finished") - } - - async getClient(data) { - let network = await Network(data, true) - let slip44 - - timeStamp('⚛') - timeStamp('Starting', network.prettyName) - - if(network.data.autostake?.correctSlip44){ - slip44 = network.slip44 || 118 - }else{ - slip44 = network.data.autostake?.slip44 || 118 - } - let hdPath = [ - Slip10RawIndex.hardened(44), - Slip10RawIndex.hardened(slip44), - Slip10RawIndex.hardened(0), - Slip10RawIndex.normal(0), - Slip10RawIndex.normal(0), - ]; - slip44 != 118 && timeStamp('Using HD Path', pathToString(hdPath)) - - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.mnemonic, { - prefix: network.prefix, - hdPaths: [hdPath] - }); - - const accounts = await wallet.getAccounts() - const botAddress = accounts[0].address - - timeStamp('Bot address is', botAddress) - - if (network.slip44 && network.slip44 !== slip44) { - timeStamp("!! You are not using the preferred derivation path !!") - timeStamp("!! You should switch to the correct path unless you have grants. Check the README !!") - } - - const operatorData = data.operators.find(el => el.botAddress === botAddress) - - if (!operatorData) return timeStamp('Not an operator') - if (!network.authzSupport) return timeStamp('No Authz support') - - network = await Network(data) - if (!network.rpcUrl) return timeStamp('Could not connect to RPC API') - if (!network.restUrl) return timeStamp('Could not connect to REST API') - - const client = await network.signingClient(wallet) - client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) - - const validators = await network.getValidators() - const operators = network.getOperators(validators) - const operator = network.getOperatorByBotAddress(operators, botAddress) - - return { - network, - operator, - signingClient: client, - queryClient: network.queryClient - } - } - - checkBalance(client) { - return client.queryClient.getBalance(client.operator.botAddress, client.network.denom) - .then( - (balance) => { - timeStamp("Bot balance is", balance.amount, balance.denom) - if (balance.amount < 1_000) { - timeStamp('Bot balance is too low') - process.exit() - } - }, - (error) => { - timeStamp("ERROR:", error.message || error) - process.exit() - } - ) - } - - getDelegations(client) { - let batchSize = client.network.data.autostake?.batchQueries || 100 - return client.queryClient.getAllValidatorDelegations(client.operator.address, batchSize, (pages) => { - timeStamp("...batch", pages.length) - }).catch(error => { - timeStamp("ERROR:", error.message || error) - process.exit() - }) - } - - async getGrantedAddresses(client, addresses) { - let batchSize = client.network.data.autostake?.batchQueries || 50 - let grantCalls = addresses.map(item => { - return async () => { - try { - const validators = await this.getGrantValidators(client, item) - return validators ? item : undefined - } catch (error) { - timeStamp(item, 'Failed to get address', error.message) - } - } - }) - let grantedAddresses = await mapSync(grantCalls, batchSize, (batch, index) => { - timeStamp('...batch', index + 1) - }) - return _.compact(grantedAddresses.flat()) - } - - getGrantValidators(client, delegatorAddress) { - return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress, { timeout: 5000 }) - .then( - (result) => { - if (result.claimGrant && result.stakeGrant) { - const grantValidators = result.stakeGrant.authorization.allow_list.address - if (!grantValidators.includes(client.operator.address)) { - timeStamp(delegatorAddress, "Not autostaking for this validator, skipping") - return - } - - return grantValidators - } - }, - (error) => { - timeStamp(delegatorAddress, "ERROR skipping this run:", error.message || error) - } - ) - } - - async getAutostakeMessages(client, addresses, validators) { - let batchSize = client.network.data.autostake?.batchQueries || 50 - let calls = addresses.map(item => { - return async () => { - try { - return await this.getAutostakeMessage(client, item, validators) - } catch (error) { - timeStamp(item, 'Failed to get address', error.message) - } - } - }) - let messages = await mapSync(calls, batchSize, (batch, index) => { - // timeStamp('...batch', index + 1) - }) - return _.compact(messages.flat()) - } - - async getAutostakeMessage(client, address, validators) { - const totalRewards = await this.totalRewards(client, address, validators) - - const perValidatorReward = parseInt(totalRewards / validators.length) - - if (perValidatorReward < client.operator.data.minimumReward) { - timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') - return - } - - const withdrawAddress = await client.queryClient.getWithdrawAddress(address, {timeout: 5000}) - if(withdrawAddress && withdrawAddress !== address){ - timeStamp(address, 'has a different withdraw address:', withdrawAddress) - return - } - - timeStamp(address, "Can autostake", perValidatorReward, client.network.denom, validators.length > 1 ? "per validator" : '') - - let messages = validators.map(el => { - return this.buildRestakeMessage(address, el, perValidatorReward, client.network.denom) - }).flat() - - return this.buildExecMessage(client.operator.botAddress, messages) - } - - async autostake(client, messages) { - let batchSize = client.network.data.autostake?.batchTxs || 50 - let batches = _.chunk(_.compact(messages), batchSize) - if(batches.length){ - timeStamp('Sending', messages.length, 'messages in', batches.length, 'batches of', batchSize) - } - let calls = batches.map((batch, index) => { - return async () => { - try { - timeStamp('...batch', index + 1) - const memo = 'REStaked by ' + client.operator.moniker - await client.signingClient.signAndBroadcast(client.operator.botAddress, batch, undefined, memo).then((result) => { - timeStamp("Successfully broadcasted"); - }, (error) => { - timeStamp('Failed to broadcast:', error.message) - }) - } catch (error) { - timeStamp('ERROR: Skipping batch:', error.message) - } - } - }) - await executeSync(calls, 1) - } - - buildExecMessage(botAddress, messages) { - return { - typeUrl: "/cosmos.authz.v1beta1.MsgExec", - value: { - grantee: botAddress, - msgs: messages - } - } - } - - buildRestakeMessage(address, validatorAddress, amount, denom) { - return [{ - typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", - value: MsgWithdrawDelegatorReward.encode(MsgWithdrawDelegatorReward.fromPartial({ - delegatorAddress: address, - validatorAddress: validatorAddress - })).finish() - }, { - typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", - value: MsgDelegate.encode(MsgDelegate.fromPartial({ - delegatorAddress: address, - validatorAddress: validatorAddress, - amount: coin(amount, denom) - })).finish() - }] - } - - totalRewards(client, address, validators) { - return client.queryClient.getRewards(address, { timeout: 5000 }) - .then( - (rewards) => { - const total = Object.values(rewards).reduce((sum, item) => { - const reward = item.reward.find(el => el.denom === client.network.denom) - if (reward && validators.includes(item.validator_address)) { - return sum + parseInt(reward.amount) - } - return sum - }, 0) - return total - }, - (error) => { - timeStamp(address, "ERROR skipping this run:", error.message || error) - return 0 - } - ) - } - - getNetworksData() { - const networksData = fs.readFileSync('src/networks.json'); - const networks = JSON.parse(networksData); - try { - const overridesData = fs.readFileSync('src/networks.local.json'); - const overrides = overridesData && JSON.parse(overridesData) - return overrideNetworks(networks, overrides) - } catch { - return networks - } - } -} +import { Autostake } from "./base.mjs"; const autostake = new Autostake(); const networkName = process.argv[2] diff --git a/scripts/base.mjs b/scripts/base.mjs new file mode 100644 index 000000000..7f41e3022 --- /dev/null +++ b/scripts/base.mjs @@ -0,0 +1,328 @@ +import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; +import { Slip10RawIndex, pathToString } from "@cosmjs/crypto"; +import Network from '../src/utils/Network.mjs' +import {timeStamp, mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' + +import { + coin +} from '@cosmjs/stargate' + +import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx.js"; +import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx.js"; +import { MsgExec } from "cosmjs-types/cosmos/authz/v1beta1/tx.js"; + +import fs from 'fs' +import _ from 'lodash' + +export class Autostake { + constructor(){ + this.mnemonic = process.env.MNEMONIC + if(!this.mnemonic){ + timeStamp('Please provide a MNEMONIC environment variable') + process.exit() + } + } + + async run(networkName){ + const calls = this.getNetworksData().map(data => { + return async () => { + if(networkName && data.name !== networkName) return + if(data.enabled === false) return + + let client + try { + client = await this.getClient(data) + } catch (error) { + return timeStamp('Failed to connect', error.message) + } + + if(!client) return timeStamp('Skipping') + + timeStamp('Using REST URL', client.network.restUrl) + timeStamp('Using RPC URL', client.network.rpcUrl) + + if(!data.overriden) timeStamp('You are using public nodes, script may fail with many delegations. Check the README to use your own') + + try { + await this.runNetwork(client) + } catch (error) { + return timeStamp('Autostake failed, skipping network', error.message) + } + } + }) + await executeSync(calls, 1) + } + + async runNetwork(client){ + timeStamp('Running autostake') + await this.checkBalance(client) + + timeStamp('Finding delegators...') + const addresses = await this.getDelegations(client).then(delegations => { + return delegations.map(delegation => { + if (delegation.balance.amount === 0) return + + return delegation.delegation.delegator_address + }) + }) + + timeStamp("Checking", addresses.length, "delegators for grants...") + let grantedAddresses = await this.getGrantedAddresses(client, addresses) + + timeStamp("Found", grantedAddresses.length, "delegators with valid grants...") + + let grantMessages = await this.getAutostakeMessages(client, grantedAddresses, [client.operator.address]) + await this.autostake(client, grantMessages) + timeStamp(client.network.prettyName, "finished") + } + + async getClient(data) { + let network = await Network(data, true) + let slip44 + + timeStamp('⚛') + timeStamp('Starting', network.prettyName) + + if(network.data.autostake?.correctSlip44){ + slip44 = network.slip44 || 118 + }else{ + slip44 = network.data.autostake?.slip44 || 118 + } + let hdPath = [ + Slip10RawIndex.hardened(44), + Slip10RawIndex.hardened(slip44), + Slip10RawIndex.hardened(0), + Slip10RawIndex.normal(0), + Slip10RawIndex.normal(0), + ]; + slip44 != 118 && timeStamp('Using HD Path', pathToString(hdPath)) + + const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.mnemonic, { + prefix: network.prefix, + hdPaths: [hdPath] + }); + + const accounts = await wallet.getAccounts() + const botAddress = accounts[0].address + + timeStamp('Bot address is', botAddress) + + if (network.slip44 && network.slip44 !== slip44) { + timeStamp("!! You are not using the preferred derivation path !!") + timeStamp("!! You should switch to the correct path unless you have grants. Check the README !!") + } + + const operatorData = data.operators.find(el => el.botAddress === botAddress) + + if (!operatorData) return timeStamp('Not an operator') + if (!network.authzSupport) return timeStamp('No Authz support') + + network = await Network(data) + if (!network.rpcUrl) return timeStamp('Could not connect to RPC API') + if (!network.restUrl) return timeStamp('Could not connect to REST API') + + const client = await network.signingClient(wallet) + client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) + + const validators = await network.getValidators() + const operators = network.getOperators(validators) + const operator = network.getOperatorByBotAddress(operators, botAddress) + + return { + network, + operator, + signingClient: client, + queryClient: network.queryClient + } + } + + checkBalance(client) { + return client.queryClient.getBalance(client.operator.botAddress, client.network.denom) + .then( + (balance) => { + timeStamp("Bot balance is", balance.amount, balance.denom) + if (balance.amount < 1_000) { + timeStamp('Bot balance is too low') + process.exit() + } + }, + (error) => { + timeStamp("ERROR:", error.message || error) + process.exit() + } + ) + } + + getDelegations(client) { + let batchSize = client.network.data.autostake?.batchQueries || 100 + return client.queryClient.getAllValidatorDelegations(client.operator.address, batchSize, (pages) => { + timeStamp("...batch", pages.length) + }).catch(error => { + timeStamp("ERROR:", error.message || error) + process.exit() + }) + } + + async getGrantedAddresses(client, addresses) { + let batchSize = client.network.data.autostake?.batchQueries || 50 + let grantCalls = addresses.map(item => { + return async () => { + try { + const validators = await this.getGrantValidators(client, item) + return validators ? item : undefined + } catch (error) { + timeStamp(item, 'Failed to get address', error.message) + } + } + }) + let grantedAddresses = await mapSync(grantCalls, batchSize, (batch, index) => { + timeStamp('...batch', index + 1) + }) + return _.compact(grantedAddresses.flat()) + } + + getGrantValidators(client, delegatorAddress) { + return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress, { timeout: 5000 }) + .then( + (result) => { + if (result.claimGrant && result.stakeGrant) { + const grantValidators = result.stakeGrant.authorization.allow_list.address + if (!grantValidators.includes(client.operator.address)) { + timeStamp(delegatorAddress, "Not autostaking for this validator, skipping") + return + } + + return grantValidators + } + }, + (error) => { + timeStamp(delegatorAddress, "ERROR skipping this run:", error.message || error) + } + ) + } + + async getAutostakeMessages(client, addresses, validators) { + let batchSize = client.network.data.autostake?.batchQueries || 50 + let calls = addresses.map(item => { + return async () => { + try { + return await this.getAutostakeMessage(client, item, validators) + } catch (error) { + timeStamp(item, 'Failed to get address', error.message) + } + } + }) + let messages = await mapSync(calls, batchSize, (batch, index) => { + // timeStamp('...batch', index + 1) + }) + return _.compact(messages.flat()) + } + + async getAutostakeMessage(client, address, validators) { + const totalRewards = await this.totalRewards(client, address, validators) + + const perValidatorReward = parseInt(totalRewards / validators.length) + + if (perValidatorReward < client.operator.data.minimumReward) { + timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') + return + } + + const withdrawAddress = await client.queryClient.getWithdrawAddress(address, {timeout: 5000}) + if(withdrawAddress && withdrawAddress !== address){ + timeStamp(address, 'has a different withdraw address:', withdrawAddress) + return + } + + timeStamp(address, "Can autostake", perValidatorReward, client.network.denom, validators.length > 1 ? "per validator" : '') + + let messages = validators.map(el => { + return this.buildRestakeMessage(address, el, perValidatorReward, client.network.denom) + }).flat() + + return this.buildExecMessage(client.operator.botAddress, messages) + } + + async autostake(client, messages) { + let batchSize = client.network.data.autostake?.batchTxs || 50 + let batches = _.chunk(_.compact(messages), batchSize) + if(batches.length){ + timeStamp('Sending', messages.length, 'messages in', batches.length, 'batches of', batchSize) + } + let calls = batches.map((batch, index) => { + return async () => { + try { + timeStamp('...batch', index + 1) + const memo = 'REStaked by ' + client.operator.moniker + await client.signingClient.signAndBroadcast(client.operator.botAddress, batch, undefined, memo).then((result) => { + timeStamp("Successfully broadcasted"); + }, (error) => { + timeStamp('Failed to broadcast:', error.message) + }) + } catch (error) { + timeStamp('ERROR: Skipping batch:', error.message) + } + } + }) + await executeSync(calls, 1) + } + + buildExecMessage(botAddress, messages) { + return { + typeUrl: "/cosmos.authz.v1beta1.MsgExec", + value: { + grantee: botAddress, + msgs: messages + } + } + } + + buildRestakeMessage(address, validatorAddress, amount, denom) { + return [{ + typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", + value: MsgWithdrawDelegatorReward.encode(MsgWithdrawDelegatorReward.fromPartial({ + delegatorAddress: address, + validatorAddress: validatorAddress + })).finish() + }, { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: MsgDelegate.encode(MsgDelegate.fromPartial({ + delegatorAddress: address, + validatorAddress: validatorAddress, + amount: coin(amount, denom) + })).finish() + }] + } + + totalRewards(client, address, validators) { + return client.queryClient.getRewards(address, { timeout: 5000 }) + .then( + (rewards) => { + const total = Object.values(rewards).reduce((sum, item) => { + const reward = item.reward.find(el => el.denom === client.network.denom) + if (reward && validators.includes(item.validator_address)) { + return sum + parseInt(reward.amount) + } + return sum + }, 0) + return total + }, + (error) => { + timeStamp(address, "ERROR skipping this run:", error.message || error) + return 0 + } + ) + } + + getNetworksData() { + const networksData = fs.readFileSync('src/networks.json'); + const networks = JSON.parse(networksData); + try { + const overridesData = fs.readFileSync('src/networks.local.json'); + const overrides = overridesData && JSON.parse(overridesData) + return overrideNetworks(networks, overrides) + } catch { + return networks + } + } +} From fd1aaef98149d7bcf4a2217620dc6320c2889d9d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 26 Mar 2022 16:25:34 +0000 Subject: [PATCH 285/862] Ignore failed grant or image requests --- src/components/Delegations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 2ad20f4c9..bdc4a4350 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -157,7 +157,7 @@ class Delegations extends React.Component { const batchCalls = _.chunk(calls, 10); for (const batchCall of batchCalls) { - await Promise.all(batchCall.map(call => call())) + await Promise.allSettled(batchCall.map(call => call())) } } @@ -229,7 +229,7 @@ class Delegations extends React.Component { const batchCalls = _.chunk(calls, 1); for (const batchCall of batchCalls) { - await Promise.all(batchCall.map(call => call())) + await Promise.allSettled(batchCall.map(call => call())) } } From c487f578270aa9a015f9db34441323a656773693 Mon Sep 17 00:00:00 2001 From: Anton Kenikh Date: Sat, 26 Mar 2022 17:52:26 +0100 Subject: [PATCH 286/862] Add Yummy Capital --- src/networks.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/networks.json b/src/networks.json index 12ee1195a..ac529e79b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1676,6 +1676,15 @@ "botAddress": "cro1lj3qga40dc0w2pacfrd2s6puk43ua2vczlv690", "runTime": "16:30", "minimumReward": 1000000 + }, + { + "address": "crocncl1v8l3zyrl02j3zdjj8qma7pshmhfmvwxfnrf3vs", + "botAddress": "cro1rkvlrss04eay5mll79nclr3nc34rq9qnedc6k0", + "runTime": [ + "00:00", + "12:00" + ], + "minimumReward": 1000000 } ], "authzSupport": true From 061a57b35aa008dbf16d703fbaa5d43d9d20a6e0 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 26 Mar 2022 18:36:47 +0000 Subject: [PATCH 287/862] Adjust initial load --- src/components/Delegations.js | 11 +++++++---- src/utils/QueryClient.mjs | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index bdc4a4350..5b057f065 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -60,11 +60,11 @@ class Delegations extends React.Component { async refresh() { this.getRewards(); + this.calculateApy(); await this.getTestGrant(); if (!this.state.authzMissing && this.props.operators.length) { - await this.getGrants(); + this.getGrants(); } - this.calculateApy(); if(this.props.operators){ this.loadValidatorImages(this.props.network, _.compact(this.props.operators.map(el => el.validatorData))) this.loadValidatorImages(this.props.network, _.omit(this.props.validators, this.props.operators.map(el => el.address))) @@ -114,7 +114,10 @@ class Delegations extends React.Component { } async getGrants() { - const calls = this.props.operators.map((operator) => { + const ordered = this.props.operators.sort(el => { + return this.props.delegations[el.address] ? -1 : 0 + }) + const calls = ordered.map((operator) => { return () => { const { botAddress, address } = operator; return this.props.queryClient.getGrants(botAddress, this.props.address).then( @@ -154,7 +157,7 @@ class Delegations extends React.Component { } }); - const batchCalls = _.chunk(calls, 10); + const batchCalls = _.chunk(calls, 5); for (const batchCall of batchCalls) { await Promise.allSettled(batchCall.map(call => call())) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 4aae62fa1..5aa4a4f6b 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -34,7 +34,7 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { "/cosmos/staking/v1beta1/validators?" + searchParams.toString(), { - timeout: 6000, + timeout: 10000, } ) .then((res) => res.data); From 235d7cba8ea2f6c88ff3727fe593418269161460 Mon Sep 17 00:00:00 2001 From: Samuel Tay Date: Sat, 26 Mar 2022 20:01:36 +0000 Subject: [PATCH 288/862] Include X Staking (Crypto.org) --- src/networks.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/networks.json b/src/networks.json index ac529e79b..5a30a97c0 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1685,6 +1685,15 @@ "12:00" ], "minimumReward": 1000000 + }, + { + "address": "crocncl14vxzdsxtmat7hvz58trk823adg3pjh4xh6ghm6", + "botAddress": "cro1dfdcgy3y7jd8q9qfk427g6k53tg97uwnhwlghx", + "runTime": [ + "00:00", + "12:00" + ], + "minimumReward": 100000 } ], "authzSupport": true From d79c46cdd6233ed73072a00db9df48a31e0012fa Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 26 Mar 2022 21:23:44 +0000 Subject: [PATCH 289/862] Skip networks on failure condition --- scripts/base.mjs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 7f41e3022..335a1ee3d 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -55,7 +55,11 @@ export class Autostake { async runNetwork(client){ timeStamp('Running autostake') - await this.checkBalance(client) + const balance = await this.checkBalance(client) + if (!balance || balance < 1_000) { + timeStamp('Bot balance is too low') + return + } timeStamp('Finding delegators...') const addresses = await this.getDelegations(client).then(delegations => { @@ -141,14 +145,10 @@ export class Autostake { .then( (balance) => { timeStamp("Bot balance is", balance.amount, balance.denom) - if (balance.amount < 1_000) { - timeStamp('Bot balance is too low') - process.exit() - } + return balance.amount }, (error) => { timeStamp("ERROR:", error.message || error) - process.exit() } ) } @@ -159,7 +159,7 @@ export class Autostake { timeStamp("...batch", pages.length) }).catch(error => { timeStamp("ERROR:", error.message || error) - process.exit() + return [] }) } From 88a49ad6278b3bf41022a4194f392842fea202dd Mon Sep 17 00:00:00 2001 From: Anton Kenikh Date: Sun, 27 Mar 2022 01:17:40 +0100 Subject: [PATCH 290/862] Fixes and improvements: * fix: sorting for moment dates * fix: using utc date in nextRunFromRuntime * nextRunFromInterval optimization --- src/utils/Operator.mjs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index ff242a62c..9683c4d02 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -43,10 +43,12 @@ const Operator = (data, validatorData) => { function nextRun() { if (!data.runTime) return - if(Array.isArray(data.runTime)){ - const runTimes = data.runTime.map(el => nextRunFromRuntime(el)) - return runTimes.sort().find(el => el.isAfter()); - }else{ + if (Array.isArray(data.runTime)) { + return data.runTime + .map(el => nextRunFromRuntime(el)) + .sort((a, b) => a.valueOf() - b.valueOf()) + .find(el => el.isAfter()); + } else { if(data.runTime.startsWith('every')){ return nextRunFromInterval(data.runTime) } @@ -56,18 +58,14 @@ const Operator = (data, validatorData) => { function nextRunFromInterval(runTime){ const interval = parse(runTime.replace('every ', '')) - const date = moment().startOf('day') - do { - date.add(interval, 'ms') - } while (date.isBefore()) - return date + const diff = moment().startOf('day').diff() + const ms = interval + diff % interval + return moment().add(ms, 'ms') } function nextRunFromRuntime(runTime) { - const date = moment(runTime, 'HH:mm:ss') - if(date.isAfter()) return date - - return date.add(1, 'day') + const date = moment.utc(runTime, 'HH:mm:ss') + return date.isAfter() ? date : date.add(1, 'day') } return { From 466a9f63fad6fa0efa0f36521cfac4a8f69abb4f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 27 Mar 2022 00:29:19 +0000 Subject: [PATCH 291/862] Optional validator lookup status and timeout --- src/utils/Network.mjs | 6 ++++-- src/utils/QueryClient.mjs | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index ba918ea46..fb2321a21 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -45,8 +45,10 @@ const Network = async (data, withoutQueryClient) => { return random } - const getValidators = () => { - return queryClient.getAllValidators(150) + const getValidators = (opts) => { + opts = opts || {} + opts.status = opts.status || 'BOND_STATUS_BONDED' + return queryClient.getAllValidators(150, opts) } return { diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 5aa4a4f6b..694838f06 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -11,9 +11,9 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { "rest" ); - const getAllValidators = (pageSize, pageCallback) => { + const getAllValidators = (pageSize, opts, pageCallback) => { return getAllPages((nextKey) => { - return getValidators(pageSize, nextKey); + return getValidators(pageSize, opts, nextKey); }, pageCallback).then((pages) => { const validators = _.shuffle(pages.map((el) => el.validators).flat()); return validators.reduce( @@ -23,9 +23,10 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { }); }; - const getValidators = (pageSize, nextKey) => { + const getValidators = (pageSize, opts, nextKey) => { + opts = opts || {} const searchParams = new URLSearchParams(); - searchParams.append("status", "BOND_STATUS_BONDED"); + if (opts.status) searchParams.append("status", opts.status); if (pageSize) searchParams.append("pagination.limit", pageSize); if (nextKey) searchParams.append("pagination.key", nextKey); return axios @@ -34,7 +35,7 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { "/cosmos/staking/v1beta1/validators?" + searchParams.toString(), { - timeout: 10000, + timeout: opts.timeout || 10000, } ) .then((res) => res.data); From add0f1d4717195a0f907152d80c318c7097e314d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 27 Mar 2022 01:15:20 +0000 Subject: [PATCH 292/862] Remove test addresses --- src/networks.json | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/networks.json b/src/networks.json index ac529e79b..6c7230964 100644 --- a/src/networks.json +++ b/src/networks.json @@ -8,7 +8,6 @@ "https://rpc.cosmos.directory/osmosis" ], "gasPrice": "0uosmo", - "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "operators": [ { @@ -228,7 +227,6 @@ "https://rpc.cosmos.directory/juno" ], "gasPrice": "0.0025ujuno", - "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", "operators": [ { "address": "junovaloper1qr78535yz60zemv82sww948c6azx40gnawnh6r", @@ -470,7 +468,6 @@ "https://rpc.cosmos.directory/cosmoshub" ], "gasPrice": "0.0025uatom", - "testAddress": "cosmos1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz3k8ynk", "operators": [ { "address": "cosmosvaloper1gf4wlkutql95j7wwsxz490s6fahlvk2s9xpwax", @@ -585,7 +582,6 @@ "rpcUrl": [ "https://rpc.cosmos.directory/akash" ], - "testAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", "operators": [ { @@ -690,7 +686,6 @@ "https://rpc.cosmos.directory/chihuahua" ], "gasPrice": "0.025uhuahua", - "testAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", "operators": [ { @@ -908,7 +903,6 @@ "rpcUrl": [ "https://rpc.cosmos.directory/gravitybridge" ], - "testAddress": "gravity1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz4x4uk7", "gasPrice": "0.025ugraviton", "operators": [ { @@ -1037,7 +1031,6 @@ "https://rpc.cosmos.directory/regen" ], "gasPrice": "0.03uregen", - "testAddress": "regen1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczw5vc9j", "ownerAddress": "regenvaloper1c4y3j05qx652rnxm5mg4yesqdkmhz2f6dl7hhk", "operators": [ { @@ -1103,7 +1096,6 @@ "rpcUrl": [ "https://rpc.cosmos.directory/terra" ], - "testAddress": "terra1rnszu7kumz5lmgdk3fv2pmzt3s8vhcddqep7d4", "gasPrice": "0.015uluna", "operators": [ { @@ -1148,7 +1140,6 @@ "https://rpc.cosmos.directory/sentinel" ], "gasPrice": "0.02udvpn", - "testAddress": "sent1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz2d3ahe", "operators": [ { "address": "sentvaloper1hms0qpe27uulkllw96tmeqnl4hvxsf4vwp729v", @@ -1210,7 +1201,6 @@ "https://rpc.cosmos.directory/dig" ], "gasPrice": "0.0025udig", - "testAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", "ownerAddress": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", "operators": [ { @@ -2232,7 +2222,6 @@ "rpcUrl": [ "https://rpc.cosmos.directory/comdex" ], - "testAddress": "comdex1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczke9x2p", "ownerAddress": "comdexvaloper17f70yjkvmvld379904jaddx9h0f74n32pjtmp6", "operators": [ { @@ -2896,7 +2885,6 @@ "autostake": { "batchTxs": 100 }, - "testAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", "operators": [ { @@ -3241,8 +3229,6 @@ "https://rpc.cosmos.directory/secretnetwork" ], "gasPrice": "0.025uscrt", - "testAddress": "secret1mpf7w0yyz9n3dh42un2zqu2dmsqv7dhkdlh78e", - "ownerAddress": "", "operators": [ { "address": "secretvaloper1jjlfeshltdy6ngnf5tg6aeaygsvf0kxg8wck04", From 6b1a12b5488d46f43d5c92b5d84e4a8cb59d353f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 27 Mar 2022 01:21:54 +0000 Subject: [PATCH 293/862] Don't overwrite operator order --- src/components/Delegations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 5b057f065..68ffb093d 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -114,7 +114,7 @@ class Delegations extends React.Component { } async getGrants() { - const ordered = this.props.operators.sort(el => { + const ordered = [...this.props.operators].sort(el => { return this.props.delegations[el.address] ? -1 : 0 }) const calls = ordered.map((operator) => { From d2bf3ae2c74000ad0f1bbe6a27e10e063cbc5fe1 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 27 Mar 2022 01:28:50 +0000 Subject: [PATCH 294/862] Fix dodgy runtimes --- src/networks.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/networks.json b/src/networks.json index 5a30a97c0..e4d64dde0 100644 --- a/src/networks.json +++ b/src/networks.json @@ -50,7 +50,7 @@ { "address": "osmovaloper1zfcmwh56kmz4wqqg2t8pxrm228dx2c6hhzhtx7", "botAddress": "osmo1z0xzvw999jh7ekce7eqnex2fj7cnn6xwxqfugk", - "runTime": "15 minutes", + "runTime": "every 15 minutes", "minimumReward": 1000 }, { @@ -621,7 +621,7 @@ { "address": "akashvaloper1zfcmwh56kmz4wqqg2t8pxrm228dx2c6hzh0ewm", "botAddress": "akash1z0xzvw999jh7ekce7eqnex2fj7cnn6xwrqht87", - "runTime": "15 minutes", + "runTime": "every 15 minutes", "minimumReward": 10000 }, { @@ -729,7 +729,7 @@ { "address": "chihuahuavaloper1y6m72uxs6hhsudxqpq73rsdgkjh6nhkqef8pej", "botAddress": "chihuahua1z0xzvw999jh7ekce7eqnex2fj7cnn6xwdwhzlx", - "runTime": "15 minutes", + "runTime": "every 15 minutes", "minimumReward": 1001 }, { @@ -926,7 +926,7 @@ { "address": "gravityvaloper1zfcmwh56kmz4wqqg2t8pxrm228dx2c6hs487gh", "botAddress": "gravity1z0xzvw999jh7ekce7eqnex2fj7cnn6xw2tg5mv", - "runTime": "15 minutes", + "runTime": "every 15 minutes", "minimumReward": 1000 }, { @@ -1565,7 +1565,7 @@ { "address": "desmosvaloper17ue85ck027c4grv7nuks7k7p4fqnlc55uqhskj", "botAddress": "desmos1z0xzvw999jh7ekce7eqnex2fj7cnn6xw6rhufu", - "runTime": "15 minutes", + "runTime": "every 15 minutes", "minimumReward": 1000 }, { @@ -2401,7 +2401,7 @@ { "address": "umeevaloper18s567a3dxf09q7erpcp8q20dpauugxwx8xsrcc", "botAddress": "umee1upe9xslwkqmmd8m7f7nuyhedhjc6saqhy4pldd", - "runTime": "15 minutes", + "runTime": "every 15 minutes", "minimumReward": 100000 }, { @@ -2977,7 +2977,7 @@ { "address": "cerberusvaloper1r34mlqewsxrde38vp3tzwh3rk5vt6ez0slzuvu", "botAddress": "cerberus16r56awgxe7mkrwpsnurjexfdg0n3f4f3dgf090", - "runTime": "15 minutes", + "runTime": "every 15 minutes", "minimumReward": 10000 }, { From 1276d6d923437e85d48d060176039d4985c37e06 Mon Sep 17 00:00:00 2001 From: esspam Date: Sun, 27 Mar 2022 09:26:01 +0200 Subject: [PATCH 295/862] Add "Asgard" Validator on Crypto.org chain --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index e4d64dde0..55db3a6c6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1694,6 +1694,12 @@ "12:00" ], "minimumReward": 100000 + }, + { + "address": "crocncl1up7anp4cq0kc799dccgfhesxp0mxklgrpgf856", + "botAddress": "cro1vltdfsd0wujjhqks9dzx3qe5njdpkfszuy5zyv", + "runTime": "00:00", + "minimumReward": 100000 } ], "authzSupport": true From 9458f84befd339063927af357b16b8e8523721d4 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 27 Mar 2022 22:57:37 +0100 Subject: [PATCH 296/862] Add delegator timeout config --- README.md | 12 +++++++----- scripts/base.mjs | 9 ++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e1bfe887e..5a011e1ec 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ cp .env.sample .env **Populate your new .env file with your mnemonic.** -### Running the script (to see if everything works correctly) +### Running the script manually Running the autostake script manually is then simple. @@ -202,9 +202,7 @@ TriggeredBy: restake.timer You will likely want to customise your networks config, e.g. to set your own node URLs to ensure your autocompounding script completes successfully. -Note that REStake requires a node with indexing enabled and minimum gas prices matching the networks.json gas price (or your local override). - -Create a `src/networks.local.json` file with JSON in the following format: +Create a `src/networks.local.json` file and specify the networks you want to override. The below is just an example, **you should only override a config if you need to**. ```json { @@ -218,7 +216,9 @@ Create a `src/networks.local.json` file with JSON in the following format: ], "gasPrice": "0.001uosmo", "autostake": { - "batchTxs": 69 + "batchTxs": 69, + "batchQueries": 50, + "delegatorTimeout": 5000 } }, "desmos": { @@ -237,6 +237,8 @@ Any values you specify will override the `networks.json` file. These are example Arrays will be replaced and not merged. The file is `.gitignore`'d so it won't affect upstream updates. +Note that REStake requires a node with indexing enabled and minimum gas prices matching the networks.json gas price (or your local override). + ## Submiting your operator ### Setup your REStake operator diff --git a/scripts/base.mjs b/scripts/base.mjs index 335a1ee3d..97fa6683b 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -182,7 +182,8 @@ export class Autostake { } getGrantValidators(client, delegatorAddress) { - return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress, { timeout: 5000 }) + let timeout = client.network.data.autostake?.delegatorTimeout || 5000 + return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress, { timeout }) .then( (result) => { if (result.claimGrant && result.stakeGrant) { @@ -228,7 +229,8 @@ export class Autostake { return } - const withdrawAddress = await client.queryClient.getWithdrawAddress(address, {timeout: 5000}) + let timeout = client.network.data.autostake?.delegatorTimeout || 5000 + const withdrawAddress = await client.queryClient.getWithdrawAddress(address, { timeout }) if(withdrawAddress && withdrawAddress !== address){ timeStamp(address, 'has a different withdraw address:', withdrawAddress) return @@ -295,7 +297,8 @@ export class Autostake { } totalRewards(client, address, validators) { - return client.queryClient.getRewards(address, { timeout: 5000 }) + let timeout = client.network.data.autostake?.delegatorTimeout || 5000 + return client.queryClient.getRewards(address, { timeout }) .then( (rewards) => { const total = Object.values(rewards).reduce((sum, item) => { From b43f55f137e581ba0b765a070493e687f3fab8a1 Mon Sep 17 00:00:00 2001 From: spaceshipped Date: Mon, 28 Mar 2022 10:28:46 +0800 Subject: [PATCH 297/862] UniverseDAO. --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 55db3a6c6..f9a83f97c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -457,6 +457,12 @@ "botAddress": "juno1330aewfwzu30vznhxw7aevaz0kn35fdygdakr4", "runTime": "every 30 minutes", "minimumReward": 1000000 + }, + { + "address": "junovaloper1rdtztev7z7udxnq4nl74l9976ww6u7tkpnadvs", + "botAddress": "juno1rdmypyp36l44xkkg5rnm2uxuyhvejr7ge0xwug", + "runTime": "01:00", + "minimumReward": 10000 } ], "authzSupport": true From 45e4d47d2ac368fb0ebcc51db245330db5df4f4c Mon Sep 17 00:00:00 2001 From: joohhnnn <68833933+joohhnnn@users.noreply.github.com> Date: Mon, 28 Mar 2022 17:13:04 +0800 Subject: [PATCH 298/862] Update networks.json --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 55db3a6c6..79bec9951 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2665,6 +2665,12 @@ "botAddress": "agoric14jhnyzfa9sf3jq879y5rp5324klh4av3vwezmm", "runTime": "every 30 minutes", "minimumReward": 1000000 + }, + { + "address": "agoricvaloper1rx04k2fuczu28tvcmu4dzchhwalxvhjrr3k2k9", + "botAddress": "cosmos1448zqw506zylru2g85xdw4qge2q8d304qn2eq7", + "runTime": "21:00", + "minimumReward": 1000000 } ], "authzSupport": true From 5dd347f28d0603c41f18912c7923ae17e666c21b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 28 Mar 2022 19:25:02 +0100 Subject: [PATCH 299/862] Improve initial load and fix some edge cases --- src/components/App.js | 110 ++++++++++++++++------------------ src/components/Delegations.js | 27 ++++++--- src/components/Wallet.js | 5 +- src/utils/QueryClient.mjs | 2 +- 4 files changed, 75 insertions(+), 69 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 5bdf1367c..d84519bae 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -33,44 +33,25 @@ class App extends React.Component { } async componentDidMount() { - await this.setNetwork() - window.onload = async () => { - if (!window.keplr) { - this.setState({keplr: false}) - } else { - this.setState({keplr: true}) - this.connect() - } - } + this.connectKeplr() + window.addEventListener("load", this.connectKeplr) window.addEventListener("keplr_keystorechange", this.connect) } - async componentDidUpdate(prevProps){ - if(!this.state.keplr && window.keplr){ - this.setState({keplr: true}) + async componentDidUpdate(prevProps, prevState){ + if(this.state.keplr != prevState.keplr){ this.connect() - } - if(this.props.network !== prevProps.network){ + }else if(this.props.network && this.props.network !== prevProps.network){ this.setState({ balance: undefined }) this.connect() - await this.setNetwork() } } componentWillUnmount() { + window.removeEventListener("load", this.connectKeplr) window.removeEventListener("keplr_keystorechange", this.connect) } - setNetwork(){ - const network = this.props.network - if(!network) return - - return this.setState({ - error: false, - queryClient: network.queryClient - }) - } - showNetworkSelect(){ this.setState({showNetworkSelect: true}) } @@ -79,20 +60,30 @@ class App extends React.Component { return this.props.network.connected && Object.values(this.props.validators).length > 0 } + connectKeplr() { + if (this.state.keplr && !window.keplr) { + this.setState({ keplr: false }) + } else if(!this.state.keplr && window.keplr){ + this.setState({ keplr: true }) + } + } + async connect() { - if(!this.connected()){ + if (!this.connected()) { return this.setState({ error: 'Could not connect to any available API servers' }) } const chainId = this.props.network.chainId try { - await window.keplr.enable(chainId); + if (window.keplr) { + await window.keplr.enable(chainId); + } } catch (e) { console.log(e.message, e) await this.suggestChain(this.props.network) } - if (window.getOfflineSigner){ + if (window.getOfflineSigner) { try { const offlineSigner = await window.getOfflineSignerAuto(chainId) const key = await window.keplr.getKey(chainId); @@ -105,10 +96,12 @@ class App extends React.Component { this.setState({ address: address, stargateClient: stargateClient, + queryClient: this.props.network.queryClient, error: false }) this.getBalance() } catch (e) { + console.log(e) return this.setState({ error: 'Failed to connect to signing client. API may be down' }) @@ -116,14 +109,15 @@ class App extends React.Component { } } - async disconnect(){ + async disconnect() { this.setState({ address: null, stargateClient: null }) } - suggestChain(network){ + suggestChain(network) { + if (!window.keplr) return const currency = { coinDenom: network.symbol, coinMinimalDenom: network.denom, @@ -147,7 +141,7 @@ class App extends React.Component { bech32PrefixConsPub: network.prefix + "valconspub" }, currencies: [currency], - feeCurrencies:[currency] + feeCurrencies: [currency] }) } @@ -162,10 +156,10 @@ class App extends React.Component { ) } - setCopied(){ - this.setState({copied: true}) + setCopied() { + this.setState({ copied: true }) setTimeout(() => { - this.setState({copied: false}) + this.setState({ copied: false }) }, 2000) } @@ -174,37 +168,37 @@ class App extends React.Component {
    - this.setState({showAbout: true})} role="button" className="text-dark text-decoration-none"> + this.setState({ showAbout: true })} role="button" className="text-dark text-decoration-none"> REStake
    {this.state.address && -
      -
    • - this.setCopied()}> - {this.state.address} - -
    • -
    • - - - - - -
    • - {false && ( -
    • - +
        +
      • + this.setCopied()}> + {this.state.address} + +
      • +
      • + + + + +
      • - )} -
      + {false && ( +
    • + +
    • + )} +
    }
    - {this.setState({showNetworkSelect: false})}} networks={this.props.networks} + { this.setState({ showNetworkSelect: false }) }} networks={this.props.networks} network={this.props.network} validators={this.props.validators} changeNetwork={this.props.changeNetwork} /> diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 68ffb093d..e717f7a6c 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -32,6 +32,7 @@ class Delegations extends React.Component { const isNanoLedger = this.props.stargateClient.getIsNanoLedger(); this.setState({ isNanoLedger: isNanoLedger }); this.refresh(); + this.testAndGetGrants() } async componentDidUpdate(prevProps) { @@ -48,9 +49,16 @@ class Delegations extends React.Component { isNanoLedger: isNanoLedger, authzMissing: false, error: null, - validatorApy: {} + validatorApy: {}, + operatorGrants: {} }); - this.refresh(); + return this.refresh(); + } + + if (!this.props.delegations) return; + const matchingDelegations = _.difference(Object.keys(this.props.delegations), prevProps.delegations ? Object.keys(prevProps.delegations) : []).length === 0 + if (!matchingDelegations){ + this.testAndGetGrants() } } @@ -61,10 +69,6 @@ class Delegations extends React.Component { async refresh() { this.getRewards(); this.calculateApy(); - await this.getTestGrant(); - if (!this.state.authzMissing && this.props.operators.length) { - this.getGrants(); - } if(this.props.operators){ this.loadValidatorImages(this.props.network, _.compact(this.props.operators.map(el => el.validatorData))) this.loadValidatorImages(this.props.network, _.omit(this.props.validators, this.props.operators.map(el => el.address))) @@ -113,7 +117,16 @@ class Delegations extends React.Component { }) } + async testAndGetGrants() { + await this.getTestGrant(); + if (!this.state.authzMissing && this.props.operators.length) { + this.getGrants(); + } + } + async getGrants() { + if(!this.props.delegations) return; + const ordered = [...this.props.operators].sort(el => { return this.props.delegations[el.address] ? -1 : 0 }) @@ -359,7 +372,7 @@ class Delegations extends React.Component { } validatorRewards(validators) { - if (!this.state.rewards) return; + if (!this.state.rewards) return []; const denom = this.props.network.denom; const validatorRewards = Object.keys(this.state.rewards) diff --git a/src/components/Wallet.js b/src/components/Wallet.js index 5ff42b8f7..e2630b126 100644 --- a/src/components/Wallet.js +++ b/src/components/Wallet.js @@ -21,12 +21,11 @@ class Wallet extends React.Component { componentDidUpdate(prevProps) { if(this.props.network !== prevProps.network){ clearInterval(this.state.refreshInterval); + this.setState({ delegations: undefined }) } - if(!this.props.address) return - + if (!this.props.address) return if(this.props.address !== prevProps.address){ - clearInterval(this.state.refreshInterval); this.getDelegations() this.refreshInterval() } diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 5aa4a4f6b..959389602 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -73,7 +73,7 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { .get(restUrl + "/cosmos/bank/v1beta1/balances/" + address) .then((res) => res.data) .then((result) => { - const balance = result.balances.find( + const balance = result.balances?.find( (element) => element.denom === denom ) || { denom: denom, amount: 0 }; return balance; From 4e6d9ff5736d2e57a0fca509a3e19d74a202f1f6 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 28 Mar 2022 19:25:54 +0100 Subject: [PATCH 300/862] Retain initial shuffled validator order --- src/components/Wallet.js | 5 +++++ src/utils/QueryClient.mjs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/Wallet.js b/src/components/Wallet.js index e2630b126..2b2809232 100644 --- a/src/components/Wallet.js +++ b/src/components/Wallet.js @@ -50,6 +50,11 @@ class Wallet extends React.Component { this.props.queryClient.getDelegations(this.props.address) .then( (delegations) => { + const orderedAddresses = Object.keys(this.props.validators) + delegations = orderedAddresses.reduce((sum, address) => { + if(delegations[address]) sum[address] = delegations[address] + return sum + }, {}) this.setState({ isLoaded: true, delegations: delegations, diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 959389602..475049c5c 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -85,7 +85,7 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { .get(restUrl + "/cosmos/staking/v1beta1/delegations/" + address) .then((res) => res.data) .then((result) => { - const delegations = _.shuffle(result.delegation_responses).reduce( + const delegations = result.delegation_responses.reduce( (a, v) => ({ ...a, [v.delegation.validator_address]: v }), {} ); From 436ab514a4a49868458ee944eaefcf5fca618aa1 Mon Sep 17 00:00:00 2001 From: dgtizestake <102550876+dgtizestake@users.noreply.github.com> Date: Mon, 28 Mar 2022 22:04:47 +0300 Subject: [PATCH 301/862] Add Dgtize Stake validator to Sifchain --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index 55db3a6c6..a26c94914 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1930,6 +1930,12 @@ "botAddress": "sif1330aewfwzu30vznhxw7aevaz0kn35fdymz3mtz", "runTime": "every 30 minutes", "minimumReward": 10000000 + }, + { + "address": "sifvaloper1ta2yj7yee373evu8xy73gludh8psy55rnczcqw", + "botAddress": "sif1pfp235023cktu4aqlhuka8mgs8k8g9vw7dxu3h", + "runTime": "every 1 hour", + "minimumReward": 1000 } ], "authzSupport": false From 71d5341e2f7991750e7a107623f1856800f01913 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 28 Mar 2022 00:27:04 +0100 Subject: [PATCH 302/862] Add Withdraw Commission for validator operators --- src/components/ClaimRewards.js | 31 ++++++++++++++++++++++++------- src/components/Delegations.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/components/ClaimRewards.js b/src/components/ClaimRewards.js index 6fed81cd8..ada069ce1 100644 --- a/src/components/ClaimRewards.js +++ b/src/components/ClaimRewards.js @@ -3,7 +3,7 @@ import Coins from './Coins' import { coin } from '@cosmjs/stargate' -import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx"; +import { MsgWithdrawDelegatorReward, MsgWithdrawValidatorCommission } from "cosmjs-types/cosmos/distribution/v1beta1/tx"; import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx"; import { @@ -41,7 +41,7 @@ function ClaimRewards(props) { signAndBroadcast = props.stargateClient.signAndBroadcastWithoutBalanceCheck - if(adjustedValidatorRewards.some(validatorReward => validatorReward.reward <= 0)) { + if(!props.commission && adjustedValidatorRewards.some(validatorReward => validatorReward.reward <= 0)) { props.setLoading(false) props.setError('Reward is too low') return @@ -80,6 +80,15 @@ function ClaimRewards(props) { validatorAddress: validatorReward.validatorAddress }) }) + + if (props.commission) { + valMessages.push({ + typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission", + value: MsgWithdrawValidatorCommission.fromPartial({ + validatorAddress: validatorReward.validatorAddress + }) + }) + } if(props.restake){ valMessages.push({ @@ -96,13 +105,21 @@ function ClaimRewards(props) { }).flat() } + function buttonText() { + if(props.restake){ + return 'Manual Compound' + }else if(props.commission){ + return 'Claim Commission' + }else{ + return 'Claim Rewards' + } + } + return ( <> - {props.validatorRewards.length > 0 && ( - claim()}> - {props.restake ? 'Manual Compound' : 'Claim Rewards'} - - )} + claim()}> + {buttonText()} + ) } diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 68ffb093d..2cfa9ca50 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -1,5 +1,6 @@ import React from "react"; import _ from "lodash"; +import { Bech32 } from '@cosmjs/encoding' import AlertMessage from "./AlertMessage"; import Coins from "./Coins"; import ClaimRewards from "./ClaimRewards"; @@ -337,6 +338,14 @@ class Delegations extends React.Component { ); } + isValidatorOperator(validator) { + if(!this.props.address || !validator || !window.atob) return false; + + const prefix = this.props.network.prefix + const validatorOperator = Bech32.encode(prefix, Bech32.decode(validator.operator_address).data) + return validatorOperator === this.props.address + } + totalRewards(validators) { if (!this.state.rewards) return; @@ -386,6 +395,7 @@ class Delegations extends React.Component { renderValidator(validatorAddress, delegation) { const validator = this.props.validators[validatorAddress]; + const isValidatorOperator = this.isValidatorOperator(validator) if (validator) { const rewards = this.state.rewards && this.state.rewards[validatorAddress]; @@ -398,6 +408,8 @@ class Delegations extends React.Component { : "table-warning" : undefined; + if(isValidatorOperator) rowVariant = 'table-info' + const delegationBalance = (delegation && delegation.balance) || { amount: 0, denom: this.props.network.denom, @@ -579,6 +591,23 @@ class Delegations extends React.Component { } setError={this.setError} /> + {isValidatorOperator && ( + <> +
    + + this.setValidatorLoading(validatorAddress, loading) + } + setError={this.setError} + /> + + )}
    Date: Mon, 28 Mar 2022 20:35:41 +0100 Subject: [PATCH 303/862] Remove MMS addition as address is SimplyStaking See https://github.com/eco-stake/restake/pull/301 --- src/networks.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/networks.json b/src/networks.json index 55db3a6c6..ebd45c3b6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1007,12 +1007,6 @@ "runTime": "21:00", "minimumReward": 10000 }, - { - "address": "gravityvaloper14vaak0zdjtnvh92hw9ypwxj76093yrgzmuguar", - "botAddress": "gravity1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9m657na", - "runTime": "every 3 hours", - "minimumReward": 100000 - }, { "address": "gravityvaloper1h4w306x3tm39gas6evu326c9ycxwrvk4drnv5t", "botAddress": "gravity1jrjdqzxchlt09gj6qvn4ds8suxh9aqwynngmtu", From d0215fe7392da8a6c8adfa948d87526fd6332ee7 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 28 Mar 2022 23:58:43 +0100 Subject: [PATCH 304/862] Match Keplr default gas prices for Osmosis --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 7d279f64c..23bfe1f74 100644 --- a/src/networks.json +++ b/src/networks.json @@ -7,7 +7,7 @@ "rpcUrl": [ "https://rpc.cosmos.directory/osmosis" ], - "gasPrice": "0uosmo", + "gasPrice": "0.0025uosmo", "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "operators": [ From 58070d901d2ce2345369e9cb27d1ed86b80043fa Mon Sep 17 00:00:00 2001 From: Songhua Hu Date: Mon, 28 Mar 2022 20:40:52 -0700 Subject: [PATCH 305/862] Add polkachu to gravity bridge --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 7d279f64c..f754d602a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1024,6 +1024,12 @@ "botAddress": "gravity1k8mymzl8sapy2yqyyce5a35u09rnuae7shvmwg", "runTime": "18:00", "minimumReward": 1000000 + }, + { + "address": "gravityvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pskxg9g", + "botAddress": "gravity1xrrfek3qpz4ja8ccrjklfy064asyqq9khzyh38", + "runTime": "every 1 hour", + "minimumReward": 5000000 } ], "authzSupport": true @@ -3361,7 +3367,7 @@ "botAddress": "bostrom14a78h6202u4xg6phjnd8rmr5fq98zzdlxzye98", "runTime": "every 4 hour", "minimumReward": 100000 - } + } ], "authzSupport": true }, From 7443c72538159519dbad2cbbacdfbddbbc60f7a4 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 29 Mar 2022 18:05:04 +0200 Subject: [PATCH 306/862] Add TTT validator to Lum --- src/networks.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 7d279f64c..7bdc51526 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2113,7 +2113,13 @@ "botAddress": "lum1wtcvjqx8097gtkjdemle9c0lm8gczm2agjfgq8", "runTime": "11:00", "minimumReward": 1000 - } + }, + { + "address": "lumvaloper1zy4xcyx25eg9qha503eqjjgz043l5zy96p83dh", + "botAddress": "lum15hfak84jamn59229c7r6umg2purgd9cysa8rw7", + "runTime": "21:00", + "minimumReward": 1000 + } ], "authzSupport": true }, From b5ff1683444c24ed0476998b45096f86e56ee310 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 29 Mar 2022 18:18:14 +0200 Subject: [PATCH 307/862] Add TTT validator to Lum --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 7bdc51526..595d5ae3e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2117,7 +2117,7 @@ { "address": "lumvaloper1zy4xcyx25eg9qha503eqjjgz043l5zy96p83dh", "botAddress": "lum15hfak84jamn59229c7r6umg2purgd9cysa8rw7", - "runTime": "21:00", + "runTime": "every 1 hour", "minimumReward": 1000 } ], From 033a644fd4cf0a8a0c7e630c40c11617d9f7bf9a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 29 Mar 2022 18:37:25 +0200 Subject: [PATCH 308/862] Add TTT validator to Lum --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 595d5ae3e..0a6390cc7 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2117,7 +2117,7 @@ { "address": "lumvaloper1zy4xcyx25eg9qha503eqjjgz043l5zy96p83dh", "botAddress": "lum15hfak84jamn59229c7r6umg2purgd9cysa8rw7", - "runTime": "every 1 hour", + "runTime": "every 5 minutes", "minimumReward": 1000 } ], From 3febcf38e87db136e5cfccd37674787101549f1d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 29 Mar 2022 18:38:57 +0100 Subject: [PATCH 309/862] Dynamic Cosmos Directory URL and refactor --- Dockerfile | 2 + Dockerfile.production | 2 + src/components/NetworkFinder.js | 22 +++- src/networks.json | 204 -------------------------------- src/utils/Chain.mjs | 16 +-- src/utils/CosmosDirectory.mjs | 44 +++++++ src/utils/Network.mjs | 8 +- 7 files changed, 75 insertions(+), 223 deletions(-) create mode 100644 src/utils/CosmosDirectory.mjs diff --git a/Dockerfile b/Dockerfile index 3084e1878..b3ec8904c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,8 @@ RUN npm install COPY . ./ ENV NODE_ENV=development +ENV DIRECTORY_PROTOCOL=https +ENV DIRECTORY_DOMAIN=cosmos.directory EXPOSE 3000 CMD npm run start diff --git a/Dockerfile.production b/Dockerfile.production index 34f7b764d..1ba6ac656 100644 --- a/Dockerfile.production +++ b/Dockerfile.production @@ -5,6 +5,8 @@ WORKDIR /app COPY package*.json ./ RUN npm ci COPY . ./ +ENV DIRECTORY_PROTOCOL=https +ENV DIRECTORY_DOMAIN=cosmos.directory RUN npm run build # production env diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 74d8dc05c..702e41340 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -1,10 +1,10 @@ import _ from 'lodash' -import axios from 'axios' import React, { useEffect, useReducer } from 'react'; import { useParams, useNavigate } from "react-router-dom"; import Network from '../utils/Network.mjs' -import { overrideNetworks } from '../utils/Helpers.mjs' +import CosmosDirectory from '../utils/CosmosDirectory.mjs' import App from './App'; +import AlertMessage from './AlertMessage' import { Spinner @@ -16,15 +16,20 @@ function NetworkFinder() { const params = useParams(); const navigate = useNavigate() + const directory = CosmosDirectory() + const [state, setState] = useReducer( (state, newState) => ({...state, ...newState}), - {loading: true, networks: [], operators: [], validators: {}} + {loading: true, networks: {}, operators: [], validators: {}} ) const getNetworks = async () => { - const registryNetworks = await axios.get('https://registry.cosmos.directory') - .then(res => res.data) - .then(data => data.reduce((a, v) => ({ ...a, [v.directory]: v}), {})) + try { + registryNetworks = await directory.getChains() + } catch (error) { + setState({error: error.message, loading: false}) + return {} + } const networks = networksData.filter(el => el.enabled !== false).map(data => { const registryData = registryNetworks[data.name] || {} @@ -45,6 +50,7 @@ function NetworkFinder() { } useEffect(() => { + if(state.error) return if(!Object.keys(state.networks).length){ setState({loading: true}) getNetworks().then(networks => { @@ -94,6 +100,10 @@ function NetworkFinder() { } }, [state.network]) + if (state.error) { + return + } + if (state.loading) { return (
    diff --git a/src/networks.json b/src/networks.json index 7d279f64c..d835659a3 100644 --- a/src/networks.json +++ b/src/networks.json @@ -1,12 +1,6 @@ [ { "name": "osmosis", - "restUrl": [ - "https://rest.cosmos.directory/osmosis" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/osmosis" - ], "gasPrice": "0uosmo", "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", @@ -221,12 +215,6 @@ { "name": "juno", "apyEnabled": false, - "restUrl": [ - "https://rest.cosmos.directory/juno" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/juno" - ], "gasPrice": "0.0025ujuno", "testAddress": "juno1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz8yyl52", "operators": [ @@ -469,12 +457,6 @@ }, { "name": "cosmoshub", - "restUrl": [ - "https://rest.cosmos.directory/cosmoshub" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/cosmoshub" - ], "gasPrice": "0.0025uatom", "testAddress": "cosmos1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz3k8ynk", "operators": [ @@ -585,12 +567,6 @@ }, { "name": "akash", - "restUrl": [ - "https://rest.cosmos.directory/akash" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/akash" - ], "testAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", "operators": [ @@ -689,12 +665,6 @@ }, { "name": "chihuahua", - "restUrl": [ - "https://rest.cosmos.directory/chihuahua" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/chihuahua" - ], "gasPrice": "0.025uhuahua", "testAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", @@ -908,12 +878,6 @@ }, { "name": "gravitybridge", - "restUrl": [ - "https://rest.cosmos.directory/gravitybridge" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/gravitybridge" - ], "testAddress": "gravity1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz4x4uk7", "gasPrice": "0.025ugraviton", "operators": [ @@ -1030,12 +994,6 @@ }, { "name": "regen", - "restUrl": [ - "https://rest.cosmos.directory/regen" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/regen" - ], "gasPrice": "0.03uregen", "testAddress": "regen1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczw5vc9j", "ownerAddress": "regenvaloper1c4y3j05qx652rnxm5mg4yesqdkmhz2f6dl7hhk", @@ -1097,12 +1055,6 @@ { "name": "terra", "apyEnabled": false, - "restUrl": [ - "https://rest.cosmos.directory/terra" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/terra" - ], "testAddress": "terra1rnszu7kumz5lmgdk3fv2pmzt3s8vhcddqep7d4", "gasPrice": "0.015uluna", "operators": [ @@ -1141,12 +1093,6 @@ }, { "name": "sentinel", - "restUrl": [ - "https://rest.cosmos.directory/sentinel" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/sentinel" - ], "gasPrice": "0.02udvpn", "testAddress": "sent1yxsmtnxdt6gxnaqrg0j0nudg7et2gqcz2d3ahe", "operators": [ @@ -1203,12 +1149,6 @@ }, { "name": "dig", - "restUrl": [ - "https://rest.cosmos.directory/dig" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/dig" - ], "gasPrice": "0.0025udig", "testAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", "ownerAddress": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", @@ -1296,12 +1236,6 @@ }, { "name": "bitcanna", - "restUrl": [ - "https://rest.cosmos.directory/bitcanna" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/bitcanna" - ], "gasPrice": "0.001ubcna", "operators": [ { @@ -1382,12 +1316,6 @@ { "name": "emoney", "apyEnabled": false, - "restUrl": [ - "https://rest.cosmos.directory/emoney" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/emoney" - ], "gasPrice": "0.08ungm", "operators": [ { @@ -1468,12 +1396,6 @@ { "name": "kava", "apyEnabled": false, - "restUrl": [ - "https://rest.cosmos.directory/kava" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/kava" - ], "gasPrice": "0.00008ukava", "operators": [ { @@ -1515,12 +1437,6 @@ { "name": "desmos", "apyEnabled": false, - "restUrl": [ - "https://rest.cosmos.directory/desmos" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/desmos" - ], "gasPrice": "0.001udsm", "operators": [ { @@ -1651,12 +1567,6 @@ }, { "name": "cryptoorgchain", - "restUrl": [ - "https://rest.cosmos.directory/cryptoorgchain" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/cryptoorgchain" - ], "gasPrice": "0.025basecro", "operators": [ { @@ -1708,12 +1618,6 @@ "name": "evmos", "enabled": false, "apyEnabled": false, - "restUrl": [ - "https://rest.cosmos.directory/evmos" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/evmos" - ], "operators": [ { "address": "evmosvaloper1shvcjzhcxau6qtlz9w82a646ecwp4hq7ayqt0w", @@ -1852,12 +1756,6 @@ }, { "name": "sifchain", - "restUrl": [ - "https://rest.cosmos.directory/sifchain" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/sifchain" - ], "operators": [ { "address": "sifvaloper1ecv3sdx8h54ckv8q8u67cyk0nj37rffwpj7cdx", @@ -1942,12 +1840,6 @@ }, { "name": "lumnetwork", - "restUrl": [ - "https://rest.cosmos.directory/lumnetwork" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/lumnetwork" - ], "operators": [ { "address": "lumvaloper1mpywgfyk6ku9uwfw4t6xhn050cjluhmjck27nx", @@ -2120,12 +2012,6 @@ { "name": "stargaze", "apyEnabled": false, - "restUrl": [ - "https://rest.cosmos.directory/stargaze" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/stargaze" - ], "operators": [ { "address": "starsvaloper1s679uyrt0uhljj8ws905hetwn87jqdz3n33klw", @@ -2247,12 +2133,6 @@ { "name": "comdex", "apyEnabled": false, - "restUrl": [ - "https://rest.cosmos.directory/comdex" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/comdex" - ], "testAddress": "comdex1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczke9x2p", "ownerAddress": "comdexvaloper17f70yjkvmvld379904jaddx9h0f74n32pjtmp6", "operators": [ @@ -2348,12 +2228,6 @@ }, { "name": "cheqd", - "restUrl": [ - "https://rest.cosmos.directory/cheqd" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/cheqd" - ], "gasPrice": "25ncheq", "operators": [ { @@ -2397,12 +2271,6 @@ }, { "name": "umee", - "restUrl": [ - "https://rest.cosmos.directory/umee" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/umee" - ], "operators": [ { "address": "umeevaloper10nsytn9zmtfnhk37hqfe2h9v599ld3h5zs9fqj", @@ -2493,12 +2361,6 @@ }, { "name": "bitsong", - "restUrl": [ - "https://rest.cosmos.directory/bitsong" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/bitsong" - ], "operators": [ { "address": "bitsongvaloper1pn6mhrwq8vdhns366jwxkggmhmxypuhrrc4huu", @@ -2592,12 +2454,6 @@ }, { "name": "persistence", - "restUrl": [ - "https://rest.cosmos.directory/persistence" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/persistence" - ], "operators": [ { "address": "persistencevaloper1rxjmg3l0myaz6fzzvkum82lzyqz5acyxqqc08l", @@ -2641,12 +2497,6 @@ { "name": "agoric", "apyEnabled": false, - "restUrl": [ - "https://rest.cosmos.directory/agoric" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/agoric" - ], "operators": [ { "address": "agoricvaloper1kh8nwuee2y353u6nmv8jffpvhtzs7xu8f4klam", @@ -2683,12 +2533,6 @@ }, { "name": "impacthub", - "restUrl": [ - "https://rest.cosmos.directory/impacthub" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/impacthub" - ], "gasPrice": "0.025uixo", "operators": [ { @@ -2726,12 +2570,6 @@ }, { "name": "kichain", - "restUrl": [ - "https://rest.cosmos.directory/kichain" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/kichain" - ], "gasPrice": "0.025uxki", "operators": [ { @@ -2793,12 +2631,6 @@ }, { "name": "sommelier", - "restUrl": [ - "https://rest.cosmos.directory/sommelier" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/sommelier" - ], "operators": [ { "address": "sommvaloper1cgdlryczzgrk7d4kkeawqg7t6ldz4x84yu305c", @@ -2810,12 +2642,6 @@ }, { "name": "konstellation", - "restUrl": [ - "https://rest.cosmos.directory/konstellation" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/konstellation" - ], "image": "https://raw.githubusercontent.com/Konstellation/DARC_token/main/darctoken.svg", "operators": [ { @@ -2871,12 +2697,6 @@ }, { "name": "fetchhub", - "restUrl": [ - "https://rest.cosmos.directory/fetchhub" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/fetchhub" - ], "operators": [ { "address": "fetchvaloper1a7l5xar7vyymvahfelar6vtkdar5x9a54l7zvc", @@ -2913,12 +2733,6 @@ }, { "name": "cerberus", - "restUrl": [ - "https://rest.cosmos.directory/cerberus" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/cerberus" - ], "gasPrice": "0.025ucrbrus", "autostake": { "batchTxs": 100 @@ -3261,12 +3075,6 @@ }, { "name": "secretnetwork", - "restUrl": [ - "https://rest.cosmos.directory/secretnetwork" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/secretnetwork" - ], "gasPrice": "0.025uscrt", "testAddress": "secret1mpf7w0yyz9n3dh42un2zqu2dmsqv7dhkdlh78e", "ownerAddress": "", @@ -3342,12 +3150,6 @@ }, { "name": "bostrom", - "restUrl": [ - "https://rest.cosmos.directory/bostrom" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/bostrom" - ], "gasPrice": "0boot", "operators": [ { @@ -3367,12 +3169,6 @@ }, { "name": "starname", - "restUrl": [ - "https://rest.cosmos.directory/starname" - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/starname" - ], "gasPrice": "10uvoi", "operators": [], "authzSupport": false diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index 863dc5320..4489caf24 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -1,18 +1,10 @@ -import axios from 'axios' +import CosmosDirectory from './CosmosDirectory.mjs' const Chain = async (data) => { - const getChainData = () => { - return axios.get('https://registry.cosmos.directory/' + data.name + '/chain') - .then(res => res.data) - } - - const getTokenData = async () => { - return axios.get('https://registry.cosmos.directory/' + data.name + '/assetlist') - .then(res => res.data) - } + const directory = CosmosDirectory() - const chainData = await getChainData(); - const tokenData = await getTokenData(); + const chainData = await directory.getChainData(data.name); + const tokenData = await directory.getTokenData(data.name); const getChainInfo = () => { return { diff --git a/src/utils/CosmosDirectory.mjs b/src/utils/CosmosDirectory.mjs new file mode 100644 index 000000000..542679580 --- /dev/null +++ b/src/utils/CosmosDirectory.mjs @@ -0,0 +1,44 @@ +import axios from 'axios' + +function CosmosDirectory(){ + const directoryProtocol = process.env.DIRECTORY_PROTOCOL + const directoryDomain = process.env.DIRECTORY_DOMAIN + const rpcBase = `${directoryProtocol}://rpc.${directoryDomain}` + const restBase = `${directoryProtocol}://rest.${directoryDomain}` + const chainsUrl = `${directoryProtocol}://registry.${directoryDomain}` + + function getChains(){ + return axios.get(chainsUrl) + .then(res => res.data) + .then(data => data.reduce((a, v) => ({ ...a, [v.directory]: v }), {})) + } + + function getChainData(name) { + return axios.get([chainsUrl, name, 'chain'].join('/')) + .then(res => res.data) + } + + async function getTokenData(name) { + return axios.get([chainsUrl, name, 'assetlist'].join('/')) + .then(res => res.data) + } + + function rpcUrl(name){ + return rpcBase + '/' + name + } + + function restUrl(name){ + return restBase + '/' + name + } + + return { + rpcUrl, + restUrl, + chainsUrl, + getChains, + getChainData, + getTokenData + } +} + +export default CosmosDirectory \ No newline at end of file diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index ba918ea46..f7b524534 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -4,13 +4,19 @@ import SigningClient from './SigningClient.mjs' import ApyClient from '../ApyClient.mjs' import Operator from './Operator.mjs' import Chain from './Chain.mjs' +import CosmosDirectory from './CosmosDirectory.mjs' const Network = async (data, withoutQueryClient) => { const chain = await Chain(data) + const directory = CosmosDirectory() + + const rpcUrl = data.rpcUrl || directory.rpcUrl(data.name) + const restUrl = data.restUrl || directory.restUrl(data.name) + let queryClient if(!withoutQueryClient){ - queryClient = await QueryClient(chain.chainId, data.rpcUrl, data.restUrl) + queryClient = await QueryClient(chain.chainId, rpcUrl, restUrl) } const signingClient = (wallet, key) => { From 567f6d0e90d66b50196bee2d00e112969043d175 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 29 Mar 2022 18:53:53 +0100 Subject: [PATCH 310/862] Fix indent --- src/networks.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/networks.json b/src/networks.json index 0a6390cc7..145185422 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2114,12 +2114,12 @@ "runTime": "11:00", "minimumReward": 1000 }, - { - "address": "lumvaloper1zy4xcyx25eg9qha503eqjjgz043l5zy96p83dh", - "botAddress": "lum15hfak84jamn59229c7r6umg2purgd9cysa8rw7", - "runTime": "every 5 minutes", - "minimumReward": 1000 - } + { + "address": "lumvaloper1zy4xcyx25eg9qha503eqjjgz043l5zy96p83dh", + "botAddress": "lum15hfak84jamn59229c7r6umg2purgd9cysa8rw7", + "runTime": "every 5 minutes", + "minimumReward": 1000 + } ], "authzSupport": true }, @@ -3367,7 +3367,7 @@ "botAddress": "bostrom14a78h6202u4xg6phjnd8rmr5fq98zzdlxzye98", "runTime": "every 4 hour", "minimumReward": 100000 - } + } ], "authzSupport": true }, @@ -3383,4 +3383,4 @@ "operators": [], "authzSupport": false } -] +] \ No newline at end of file From 551aa21a09e5b7bddb1ff93cb020dd94272c25f9 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 29 Mar 2022 19:12:14 +0100 Subject: [PATCH 311/862] Fix merge error --- src/components/NetworkFinder.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 702e41340..89efafe4f 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -24,6 +24,7 @@ function NetworkFinder() { ) const getNetworks = async () => { + let registryNetworks try { registryNetworks = await directory.getChains() } catch (error) { From e8683e1963f1a5a5b94197afd7a4b42aec69f2c2 Mon Sep 17 00:00:00 2001 From: westaking Date: Wed, 30 Mar 2022 03:20:17 +0900 Subject: [PATCH 312/862] update networks.json for westaking --- src/networks.json | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/networks.json b/src/networks.json index 7d279f64c..d992777d1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -463,6 +463,12 @@ "botAddress": "juno1rdmypyp36l44xkkg5rnm2uxuyhvejr7ge0xwug", "runTime": "01:00", "minimumReward": 10000 + }, + { + "address": "junovaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapv8jey5z", + "botAddress": "juno1hlt7mew3x8crsvg44xuz4aqqcugh5mqx29lt8z", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": true @@ -579,6 +585,12 @@ "botAddress": "cosmos1330aewfwzu30vznhxw7aevaz0kn35fdy7l7dyf", "runTime": "every 30 minutes", "minimumReward": 1000000 + }, + { + "address": "cosmosvaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvtfc9y5", + "botAddress": "cosmos1hlt7mew3x8crsvg44xuz4aqqcugh5mqxuhusq7", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": true @@ -683,6 +695,12 @@ "botAddress": "akash1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy6chyhw", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "akashvaloper1f6eqjk9zjc7tkzmvdjnz589w0c4sjfx5hd9z3n", + "botAddress": "akash1hlt7mew3x8crsvg44xuz4aqqcugh5mqx3v3hey", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": false @@ -1090,6 +1108,12 @@ "botAddress": "regen1k8mymzl8sapy2yqyyce5a35u09rnuae7t94lay", "runTime": "18:00", "minimumReward": 1000000 + }, + { + "address": "regenvaloper1p3vzy48erskut3z36wgkquthplvyq7h6mvdlwa", + "botAddress": "regen1hlt7mew3x8crsvg44xuz4aqqcugh5mqxr4hvk6", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1135,6 +1159,12 @@ "botAddress": "terra1knhcrmhcm4d6apcj4vglelax7sz4fm8759s6mj", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "terravaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvgk6d65", + "botAddress": "terra1x74w72vp3nug9txqpjzpl4cmh4t2kmjs8uz25r", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1197,6 +1227,12 @@ "botAddress": "sent1m5hgzum68rjf4c7zhezgkj8hlnmr0kghd6djwl", "runTime": "every 1 hour", "minimumReward": 100000 + }, + { + "address": "sentvaloper1vw60jkrl5keyw9qa2cdwtrrytpq3c8f6q7hyrl", + "botAddress": "sent1k2ljxq7uhefwqesda39hr0p5pf4txvk0y6yrw2", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": false @@ -1461,6 +1497,12 @@ "botAddress": "emoney195mm9y35sekrjk73anw2az9lv9xs5mzt444jt2", "runTime": "every 1 hour", "minimumReward": 100000 + }, + { + "address": "emoneyvaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvx7ssph", + "botAddress": "emoney1hlt7mew3x8crsvg44xuz4aqqcugh5mqxn5xyhr", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": false @@ -1508,6 +1550,12 @@ "botAddress": "kava103yfkaqzwl0me0znucd25lrn66hzh0zf4hrpek", "runTime": "every 1 hour", "minimumReward": 1000000 + }, + { + "address": "kavavaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvl7z9xh", + "botAddress": "kava1hlt7mew3x8crsvg44xuz4aqqcugh5mqxqzgdke", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": false @@ -1645,6 +1693,12 @@ "botAddress": "desmos1k8mymzl8sapy2yqyyce5a35u09rnuae7qlnnuc", "runTime": "21:00", "minimumReward": 100000 + }, + { + "address": "desmosvaloper1v0vje887uzfxzc4rgdj5vm8lnkufuylk8ej7mz", + "botAddress": "desmos1uxmek9gvf8r862h0fwtu5cfzeypl569sfs4zml", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1846,6 +1900,12 @@ "botAddress": "evmos1f54lck35ehjh3x0e62e7gak9h7zn4vl7s7s3cj", "runTime": "every 30 minutes", "minimumReward": 1000000000000000000 + }, + { + "address": "evmosvaloper152644qg2h4qmqutftr9fnug0ypxw2apmjgk6rp", + "botAddress": "evmos1hkzpq664h8dnwksqffdmhqmgsme773a7eqj2rg", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": true @@ -1936,6 +1996,12 @@ "botAddress": "sif1pfp235023cktu4aqlhuka8mgs8k8g9vw7dxu3h", "runTime": "every 1 hour", "minimumReward": 1000 + }, + { + "address": "sifvaloper1x5x0zghfep8jkg4n2gz88del6n22yescv3n3rs", + "botAddress": "sif1hlt7mew3x8crsvg44xuz4aqqcugh5mqxe2nx04", + "runTime": "00:30", + "minimumReward": 1000000000000000000 } ], "authzSupport": false @@ -2586,6 +2652,12 @@ "botAddress": "bitsong1fx6eahccn5n9m6zwz0p4ynff0asnllvtyvxvd5", "runTime": "22:00", "minimumReward": 100000 + }, + { + "address": "bitsongvaloper1u6hq8svq4td2e80ucj3whmxh9uehf8nygh4hld", + "botAddress": "bitsong1e2xpkl7tjugpx4djn85ufjn3y0mvsfafd9tyrw", + "runTime": "00:30", + "minimumReward": 1000000 } ], "authzSupport": true From 01e3debc8714a440d53b154099ec4d68e62ba710 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 29 Mar 2022 18:41:01 +0100 Subject: [PATCH 313/862] Handle new paths --- src/utils/CosmosDirectory.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/CosmosDirectory.mjs b/src/utils/CosmosDirectory.mjs index 542679580..addfce74e 100644 --- a/src/utils/CosmosDirectory.mjs +++ b/src/utils/CosmosDirectory.mjs @@ -5,12 +5,12 @@ function CosmosDirectory(){ const directoryDomain = process.env.DIRECTORY_DOMAIN const rpcBase = `${directoryProtocol}://rpc.${directoryDomain}` const restBase = `${directoryProtocol}://rest.${directoryDomain}` - const chainsUrl = `${directoryProtocol}://registry.${directoryDomain}` + const chainsUrl = `${directoryProtocol}://chains.${directoryDomain}` function getChains(){ return axios.get(chainsUrl) .then(res => res.data) - .then(data => data.reduce((a, v) => ({ ...a, [v.directory]: v }), {})) + .then(data => data.reduce((a, v) => ({ ...a, [v.path]: v }), {})) } function getChainData(name) { From 633db772ce5832e2efa317c3732349b199b80f54 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 29 Mar 2022 21:37:39 +0100 Subject: [PATCH 314/862] Switch to cosmos.directory operators API --- scripts/base.mjs | 2 +- src/components/NetworkSelect.js | 21 +- src/networks.json | 3069 +------------------------------ src/utils/CosmosDirectory.mjs | 31 +- src/utils/Network.mjs | 12 +- 5 files changed, 54 insertions(+), 3081 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 97fa6683b..e9c5e9140 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -116,7 +116,7 @@ export class Autostake { timeStamp("!! You should switch to the correct path unless you have grants. Check the README !!") } - const operatorData = data.operators.find(el => el.botAddress === botAddress) + const operatorData = network.operators.find(el => el.botAddress === botAddress) if (!operatorData) return timeStamp('Not an operator') if (!network.authzSupport) return timeStamp('No Authz support') diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index d7210e976..b3293bf10 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -1,5 +1,6 @@ import React, { useState, useReducer, useEffect } from 'react'; +import CosmosDirectory from '../utils/CosmosDirectory.mjs'; import Network from '../utils/Network.mjs' import { @@ -17,6 +18,7 @@ function NetworkSelect(props) { const [error, setError] = useState(false); const [selectedNetwork, setSelectedNetwork] = useState(); const [validators, setValidators] = useState([]); + const [operatorCounts, setOperatorCounts] = useState({}); const [options, setOptions] = useReducer( (state, newState) => ({...state, ...newState}), {networks: [], operators: [], network: {value: ''}} @@ -25,6 +27,9 @@ function NetworkSelect(props) { const handleOpen = () => { setSelectedNetwork(props.network) setValidators(props.validators) + CosmosDirectory().getOperatorCounts().then(counts => { + setOperatorCounts(counts) + }) setShow(true) } @@ -52,17 +57,23 @@ function NetworkSelect(props) { const networks = Object.values(props.networks).sort((a, b) => a.name > b.name ? 1 : -1) setOptions({ networks: networks.map(el => { - return {value: el.name, label: el.pretty_name, image: el.image, operators: el.operators, authz: el.authzSupport} + return { + value: el.name, + label: el.pretty_name, + image: el.image, + operatorCount: operatorCounts[el.name], + authz: el.authzSupport + } }), network: selectedNetwork && { value: selectedNetwork.name, label: selectedNetwork.prettyName, image: selectedNetwork.image, - operators: selectedNetwork.operators, + operatorCount: selectedNetwork.operators.length, authz: selectedNetwork.authzSupport } }) - }, [props.networks, selectedNetwork]) + }, [props.networks, selectedNetwork, operatorCounts]) const selectNetwork = (newValue) => { const data = props.networks[newValue.value] @@ -133,8 +144,8 @@ function NetworkSelect(props) { {network.label}
    - {network.operators.length > 0 && - {network.operators.length} Operator{network.operators.length > 1 ? 's' : ''} + {network.operatorCount > 0 && + {network.operatorCount} Operator{network.operatorCount > 1 ? 's' : ''} } {network.authz ? Authz diff --git a/src/networks.json b/src/networks.json index 5f7757962..837f7e853 100644 --- a/src/networks.json +++ b/src/networks.json @@ -3,2805 +3,153 @@ "name": "osmosis", "gasPrice": "0.0025uosmo", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", - "operators": [ - { - "address": "osmovaloper1m73mgwn3cm2e8x9a9axa0kw8nqz8a492vg4hp4", - "botAddress": "osmo197yc3g8drkf8fyv6fr4ynddy2n22kauuhqkprd", - "runTime": "22:00", - "minimumReward": 50000 - }, - { - "address": "osmovaloper122yaxffys6rmv03nwwkmn3rvr5skzxl9lry2a5", - "botAddress": "osmo1m7xlymnpxl2d4llu67v08e8xs67dj2fz0wqmqw", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 10 - }, - { - "address": "osmovaloper17mggn4znyeyg25wd7498qxl7r2jhgue8td054x", - "botAddress": "osmo1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5p5wwvc", - "runTime": "18:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1pxphtfhqnx9ny27d53z4052e3r76e7qq495ehm", - "botAddress": "osmo194ytn50yhh67rdha8akhs7c6zulnz4n2pv6n63", - "runTime": "every 1 hour", - "minimumReward": 1 - }, - { - "address": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", - "botAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "osmovaloper1zfcmwh56kmz4wqqg2t8pxrm228dx2c6hhzhtx7", - "botAddress": "osmo1z0xzvw999jh7ekce7eqnex2fj7cnn6xwxqfugk", - "runTime": "every 15 minutes", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1vrw2c99yvu5t9gugzuj38w35vmhmazzgx7dckk", - "botAddress": "osmo1yl54sf2ekqsc7y7r33267vhc546trx3fwz6yld", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1md9f5524vtmrn64lyv2pdfn7cnkjkklf44vtjz", - "botAddress": "osmo1e44rluarkdw56dy2turnwjtvtg4wqvs06sx5ph", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1t8qckan2yrygq7kl9apwhzfalwzgc2429p8f0s", - "botAddress": "osmo1eml7agwd5x084ennlg6apvskufzrlvnr7tf8rt", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "osmovaloper16n0t7lmy4wnhjytzkncjdkxrsu9yfjm3ex7hxr", - "botAddress": "osmo106n92ygqknaw5wcgl2e9v0duxxux6ntt7y67ae", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "osmovaloper19v94c3z7ckarwsum76kaagma0wqsqhh54xucdk", - "botAddress": "osmo1yuukehecl8czrd28q0tmxpvwce9lzt23pj35zp", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "osmovaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5w37wr9t", - "botAddress": "osmo1gvgcxwdk4j46gx3knhuackvqr2hta29zzygz95", - "runTime": "21:00", - "minimumReward": 5000 - }, - { - "address": "osmovaloper1xwazl8ftks4gn00y5x3c47auquc62ssuh8af89", - "botAddress": "osmo195mm9y35sekrjk73anw2az9lv9xs5mztjduk29", - "runTime": "17:30", - "minimumReward": 10000 - }, - { - "address": "osmovaloper1u6jr0pztvsjpvx77rfzmtw49xwzu9kas05lk04", - "botAddress": "osmo1v9vae554q55xa4pcfdm0p3kv8nxn50jkjh5zmm", - "runTime": "23:00", - "minimumReward": 10000 - }, - { - "address": "osmovaloper1glmy88g0uf6vmw29pyxu3yq0pxpjqtzqr5e57n", - "botAddress": "osmo12xpq68caw2aqdu70jm4k792g0v9prhrpgc45k2", - "runTime": "22:00", - "minimumReward": 10000 - }, - { - "address": "osmovaloper16q8xd335y38xk2ul67mjg27vdnrcnklt4wx6kt", - "botAddress": "osmo1mjq48r6435aewerpruwc8up3tz3rzan2yzd8h8", - "runTime": "19:00", - "minimumReward": 10000 - }, - { - "address": "osmovaloper1h2c47vd943scjlfum6yc5frvu2l279lwjep5d6", - "botAddress": "osmo1305e92ng6a9pdh3ttc3ld459ckxupxt3lhfnv4", - "runTime": "17:25", - "minimumReward": 10000 - }, - { - "address": "osmovaloper16gm3cvhluf9xfurkx9qgxq7pldvd479l0j6zms", - "botAddress": "osmo17xyeczwsax3st6aykyu4f28s5n3zg6r7n7qza3", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "osmovaloper1kls9ca0h980sqfmt0497jj8kad3lcws6g08mld", - "botAddress": "osmo1uecj925gwmagk8je8c2exmnty6t2jm7xk9shdp", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper10ymws40tepmjcu3a2wuy266ddna4ktas0zuzm4", - "botAddress": "osmo1uwqjtgjhjctjc45ugy7ev5prprhehc7w3xx7ph", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1feh2keupglep6mvxf5c96eulh3puujjryj2h8v", - "botAddress": "osmo1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9h34kq8", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "osmovaloper125kjmvuv5sv7qct2t6wc99h3mp7hpyxw37dcgg", - "botAddress": "osmo1tah24yf5pndrwwc8hsdmjahgyf4s5s7nherfmw", - "runTime": "1:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1z5xyynz9ewuf044uaweswldut34z34z3cwpt7y", - "botAddress": "osmo1hl0jgxre5z0nkyjj07c5vfdy44yk44at3y3ue0", - "runTime": "22:30", - "minimumReward": 1000 - }, - { - "address": "osmovaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjp38f93", - "botAddress": "osmo14gra89zm8753pfuhvujjulmtkf2qq70f3l9gtx", - "runTime": "17:28", - "minimumReward": 100000 - }, - { - "address": "osmovaloper1xf9zpq5kpxks49cg606tzd8qstaykxgt2vs0d5", - "botAddress": "osmo19pqrxrl6n0g0mky4y79hlfzchprmsp5jmu2t6g", - "runTime": "19:31", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1e8238v24qccht9mqc2w0r4luq462yxttfpaeam", - "botAddress": "osmo1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjgq4dj8", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "osmovaloper13tk45jkxgf7w0nxquup3suwaz2tx483xe832ge", - "botAddress": "osmo1grfk7t0k42yxpwqhr7kt4te7j73m2a3v27wqh5", - "runTime": "19:37", - "minimumReward": 10000 - }, - { - "address": "osmovaloper1vmkt6ysppk8m4rhlq78tpqyh429lhlsah4mn3y", - "botAddress": "osmo1syc66updwkat9nhmfplzzvflh5789phc3z4kgk", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper15czt5nhlnvayqq37xun9s9yus0d6y26d5jws45", - "botAddress": "osmo1mmg6pqqnu6ktl4yp0afwzz6g33w8kpw35sufdn", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1y0us8xvsvfvqkk9c6nt5cfyu5au5tww24nrlnx", - "botAddress": "osmo1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv56exja", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4d3kvts", - "botAddress": "osmo1jrjdqzxchlt09gj6qvn4ds8suxh9aqwylcfncx", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "osmovaloper1vkfmegxrsveefn2wmudh7apxuzu2n77654ad62", - "botAddress": "osmo1k4hh4mjvtrwq79vhmlrltdsyq08kec7jnaczjf", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "osmovaloper1gy0nyn2hscxxayj2pdyu8axmfvv75nnvhc079s", - "botAddress": "osmo1330aewfwzu30vznhxw7aevaz0kn35fdykydajm", - "runTime": "every 30 minutes", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "juno", "apyEnabled": false, "gasPrice": "0.0025ujuno", - "operators": [ - { - "address": "junovaloper1qr78535yz60zemv82sww948c6azx40gnawnh6r", - "botAddress": "juno1m7xlymnpxl2d4llu67v08e8xs67dj2fz38ss3q", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "junovaloper18eancryfzd7n0ag9xy3azqvxgkjla2mrs2uznx", - "botAddress": "juno197yc3g8drkf8fyv6fr4ynddy2n22kauuffx2jr", - "runTime": "22:00", - "minimumReward": 100000 - }, - { - "address": "junovaloper1ty96k6wly7k9ualepv2hs258u29esn6mq02yd4", - "botAddress": "juno15xa7vst3jca9qjkngmlysrdfur8gsex9y0qwal", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 10000 - }, - { - "address": "junovaloper17mggn4znyeyg25wd7498qxl7r2jhgue8swpngk", - "botAddress": "juno1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5la79ak", - "runTime": "18:00", - "minimumReward": 1000 - }, - { - "address": "junovaloper1zxx8j75ngm8m38v9l5wreaavwnsuun7gcq5cu8", - "botAddress": "juno1n3m6rj0w9lgzg7leppnaru2xguraxhqcn4eyv6", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5w2aqycm", - "botAddress": "juno1gvgcxwdk4j46gx3knhuackvqr2hta29zudcf56", - "runTime": "20:00", - "minimumReward": 10001 - }, - { - "address": "junovaloper1x8u2ypdr35802tjyjqyxan8x85fzxe6sk0qmh8", - "botAddress": "juno1r8egcurpwxftegr07gjv9gwffw4fk009a4v3ll", - "runTime": "every 1 hour", - "minimumReward": 20000 - }, - { - "address": "junovaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qdelfp7r", - "botAddress": "juno1xs023g3yj2mavwn3cjclgsh8mlk0kgsjsvdd2e", - "runTime": [ - "00:00", - "12:00" - ], - "minimumReward": 10000 - }, - { - "address": "junovaloper193xl2tqh2tjkld2zv49ku5s44ee4qmgr65jcep", - "botAddress": "juno1e44rluarkdw56dy2turnwjtvtg4wqvs0yeklse", - "runTime": "every 29 minutes", - "minimumReward": 100000 - }, - { - "address": "junovaloper17n3w6v5q3n0tws4xv8upd9ul4qqes0nlg7q0xd", - "botAddress": "juno1eml7agwd5x084ennlg6apvskufzrlvnrqzevj9", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1gwd9l44t22e3nteu68akfgykuxu6lq6m6u786d", - "botAddress": "juno1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zhjavf8", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "junovaloper10y7ucn6jhjtakwchgpw32y0tgaku6yn255z7gm", - "botAddress": "juno106n92ygqknaw5wcgl2e9v0duxxux6nttqd24vh", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "junovaloper1xwazl8ftks4gn00y5x3c47auquc62ssuvynw64", - "botAddress": "juno195mm9y35sekrjk73anw2az9lv9xs5mztvyvamt", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "junovaloper1mzqqrctm0hvkghf66jruxjtggd5j447dmugz0j", - "botAddress": "juno1l7je4yr3ffs6d8v6ck49fmueqqndvgpw79c8jp", - "runTime": "14:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1p0r4znp29547d8ra5k8t6ls0ztm3tj74ak38wz", - "botAddress": "juno1v9vae554q55xa4pcfdm0p3kv8nxn50jkv7yf24", - "runTime": "23:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1wd02ktcvpananlvd9u6jm3x3ap3vmw59jv9vez", - "botAddress": "juno12xpq68caw2aqdu70jm4k792g0v9prhrpk39l8y", - "runTime": "14:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1la7th9kc3gdltl53cedxxqpkyk5runudrzwx55", - "botAddress": "juno1mjq48r6435aewerpruwc8up3tz3rzan26tavxf", - "runTime": "19:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1ncu32g0lzhk0epzdar7smd3qv9da2n8w8mwn4k", - "botAddress": "juno1305e92ng6a9pdh3ttc3ld459ckxupxt3p7ecam", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "junovaloper16gm3cvhluf9xfurkx9qgxq7pldvd479l5359xq", - "botAddress": "juno17xyeczwsax3st6aykyu4f28s5n3zg6r7dhsfvl", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1am80fauk3gnzcr76ylg0yju4f0chen5x6zmq7q", - "botAddress": "juno1hpwxnnrtsc6zjx4glup77tfe5z54lmtqnygwzm", - "runTime": "every 12 hours", - "minimumReward": 10000 - }, - { - "address": "junovaloper1297950qqq6yxhgsms6t5dks66gyk6sctplnyd7", - "botAddress": "juno1q200ut9chx7t0nnde4h526vdxwudktgkfhxfw7", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper106y6thy7gphzrsqq443hl69vfdvntgz260uxlc", - "botAddress": "juno1uwqjtgjhjctjc45ugy7ev5prprhehc7w00k4se", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "junovaloper1z3svprqzmquw6mckh6rgyt0dmd4yhv7c6l58qc", - "botAddress": "juno1m5hgzum68rjf4c7zhezgkj8hlnmr0kghqncsdv", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "junovaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pvzc6k3", - "botAddress": "juno1xrrfek3qpz4ja8ccrjklfy064asyqq9k9q45nn", - "runTime": "every 1 hour", - "minimumReward": 50000 - }, - { - "address": "junovaloper124maqmcqv8tquy764ktz7cu0gxnzfw54l2d073", - "botAddress": "juno1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9fc9a3f", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "junovaloper1hx9yj7qgnp8zhkrqfanvz74mcsg9d8eyskvsxg", - "botAddress": "juno1hl0jgxre5z0nkyjj07c5vfdy44yk44at0dphgp", - "runTime": "22:30", - "minimumReward": 100000 - }, - { - "address": "junovaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgj6jfwcp", - "botAddress": "juno14gra89zm8753pfuhvujjulmtkf2qq70f0k4r6g", - "runTime": "17:28", - "minimumReward": 100000 - }, - { - "address": "junovaloper1e8238v24qccht9mqc2w0r4luq462yxttjzn7qt", - "botAddress": "juno1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjkf9xrf", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "junovaloper1quqxfrxkycr0uzt4yk0d57tcq3zk7srm7sm6r8", - "botAddress": "juno1grfk7t0k42yxpwqhr7kt4te7j73m2a3v5h7tx6", - "runTime": "19:37", - "minimumReward": 10000 - }, - { - "address": "junovaloper166r5ylkp70xe0ysq2hjxn26m4q9vfn8q3lv46c", - "botAddress": "juno1d72r4aaxkv2cpnxjh6xuklv9z4prcj2qe8cnrw", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1ka8v934kgrw6679fs9cuu0kesyl0ljjy2kdtrl", - "botAddress": "juno1ka6r58g5h27x6gyyjkzxtpdk95k2yurf4ge0fl", - "runTime": "01:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1fj75m0v53ezdd8mlknzgdg4l3hvwh0h20sg4a8", - "botAddress": "juno1zks5qjck40m827v2m83la5r3y6lkudw8nsa0y6", - "runTime": "08:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1y0us8xvsvfvqkk9c6nt5cfyu5au5tww2wsdcwk", - "botAddress": "juno1rjvyczwh5akp2ylg06dr4s2vlx0jmdwv2nfdrn", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4kjctkq", - "botAddress": "juno1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyp3ecfg", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "junovaloper194v8uwee2fvs2s8fa5k7j03ktwc87h5ym39jfv", - "botAddress": "juno179w4ead6r2xf3uam3pp7kg55tsl5eycyta60qh", - "runTime": [ - "00:00", - "12:00" - ], - "minimumReward": 10000 - }, - { - "address": "junovaloper1jc9fr7s5kal8878trt86ne0k353r4yd6a60zak", - "botAddress": "juno1330aewfwzu30vznhxw7aevaz0kn35fdygdakr4", - "runTime": "every 30 minutes", - "minimumReward": 1000000 - }, - { - "address": "junovaloper1rdtztev7z7udxnq4nl74l9976ww6u7tkpnadvs", - "botAddress": "juno1rdmypyp36l44xkkg5rnm2uxuyhvejr7ge0xwug", - "runTime": "01:00", - "minimumReward": 10000 - }, - { - "address": "junovaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapv8jey5z", - "botAddress": "juno1hlt7mew3x8crsvg44xuz4aqqcugh5mqx29lt8z", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "cosmoshub", "gasPrice": "0.0025uatom", - "operators": [ - { - "address": "cosmosvaloper1gf4wlkutql95j7wwsxz490s6fahlvk2s9xpwax", - "botAddress": "cosmos1m7xlymnpxl2d4llu67v08e8xs67dj2fz84ntku", - "runTime": "18:00", - "minimumReward": 1000 - }, - { - "address": "cosmosvaloper1m73mgwn3cm2e8x9a9axa0kw8nqz8a492ms63vn", - "botAddress": "cosmos197yc3g8drkf8fyv6fr4ynddy2n22kauulm934l", - "runTime": "22:00", - "minimumReward": 100000 - }, - { - "address": "cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq", - "botAddress": "cosmos1ks0uf2zxgv6qjyzjwfvfxyv5vp2m6nk5f0a762", - "runTime": "18:00", - "minimumReward": 1000 - }, - { - "address": "cosmosvaloper1vvwtk805lxehwle9l4yudmq6mn0g32px9xtkhc", - "botAddress": "cosmos1eml7agwd5x084ennlg6apvskufzrlvnrks6h4e", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "cosmosvaloper1fsg635n5vgc7jazz9sx5725wnc3xqgr7awxaag", - "botAddress": "cosmos1v9vae554q55xa4pcfdm0p3kv8nxn50jk6v8jdf", - "runTime": "23:00", - "minimumReward": 10000 - }, - { - "address": "cosmosvaloper140l6y2gp3gxvay6qtn70re7z2s0gn57zfd832j", - "botAddress": "cosmos12xpq68caw2aqdu70jm4k792g0v9prhrpqrxyqc", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "cosmosvaloper1gpx52r9h3zeul45amvcy2pysgvcwddxrgx6cnv", - "botAddress": "cosmos1mjq48r6435aewerpruwc8up3tz3rzan2ve7hp4", - "runTime": "19:00", - "minimumReward": 500000 - }, - { - "address": "cosmosvaloper14qazscc80zgzx3m0m0aa30ths0p9hg8vdglqrc", - "botAddress": "cosmos1305e92ng6a9pdh3ttc3ld459ckxupxt3hv6r68", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "cosmosvaloper16yupepagywvlk7uhpfchtwa0stu5f8cyhh54f2", - "botAddress": "cosmos1hl0jgxre5z0nkyjj07c5vfdy44yk44atelzv0a", - "runTime": "22:30", - "minimumReward": 100000 - }, - { - "address": "cosmosvaloper15r4tc0m6hc7z8drq3dzlrtcs6rq2q9l2nvwher", - "botAddress": "cosmos1dg8u5qll7fjm9nnwqns0ullhtc6g552ugd0eqj", - "runTime": "9:00", - "minimumReward": 10000 - }, - { - "address": "cosmosvaloper124maqmcqv8tquy764ktz7cu0gxnzfw54n3vww8", - "botAddress": "cosmos1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9l2xxk4", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "cosmosvaloper199mlc7fr6ll5t54w7tts7f4s0cvnqgc59nmuxf", - "botAddress": "cosmos19pqrxrl6n0g0mky4y79hlfzchprmsp5jn8emv6", - "runTime": "19:30", - "minimumReward": 10000 - }, - { - "address": "cosmosvaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjkfg0gh", - "botAddress": "cosmos14gra89zm8753pfuhvujjulmtkf2qq70feykca5", - "runTime": "17:28", - "minimumReward": 100000 - }, - { - "address": "cosmosvaloper157v7tczs40axfgejp2m43kwuzqe0wsy0rv8puv", - "botAddress": "cosmos1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjqmxay4", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "cosmosvaloper1y0us8xvsvfvqkk9c6nt5cfyu5au5tww2ztve7q", - "botAddress": "cosmos1rjvyczwh5akp2ylg06dr4s2vlx0jmdwvup2ky0", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "cosmosvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q46fe2xk", - "botAddress": "cosmos1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyhr6rw5", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "cosmosvaloper1g48268mu5vfp4wk7dk89r0wdrakm9p5xk0q50k", - "botAddress": "cosmos1330aewfwzu30vznhxw7aevaz0kn35fdy7l7dyf", - "runTime": "every 30 minutes", - "minimumReward": 1000000 - }, - { - "address": "cosmosvaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvtfc9y5", - "botAddress": "cosmos1hlt7mew3x8crsvg44xuz4aqqcugh5mqxuhusq7", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "akash", "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", - "operators": [ - { - "address": "akashvaloper1ksu368eh54vcqzruqk42dsnx88hvk8px09ws7q", - "botAddress": "akash1m7xlymnpxl2d4llu67v08e8xs67dj2fz2w7v0x", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "akashvaloper1strxz39h5fapp7zvn5mvf8xm0ea9aajqjstv6g", - "botAddress": "akash194ytn50yhh67rdha8akhs7c6zulnz4n2yvyy4e", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", - "botAddress": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "akashvaloper1qwpnhmdlfj6gfyh0e29fjudh0ytfe5l7tjttul", - "botAddress": "akash1yl54sf2ekqsc7y7r33267vhc546trx3ftzyns9", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "akashvaloper1zfcmwh56kmz4wqqg2t8pxrm228dx2c6hzh0ewm", - "botAddress": "akash1z0xzvw999jh7ekce7eqnex2fj7cnn6xwrqht87", - "runTime": "every 15 minutes", - "minimumReward": 10000 - }, - { - "address": "akashvaloper1enhj36w6clcm5fjnp87jnffnkthfux2uj88l6h", - "botAddress": "akash1eml7agwd5x084ennlg6apvskufzrlvnrmthsvr", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "akashvaloper140l6y2gp3gxvay6qtn70re7z2s0gn57ztqs903", - "botAddress": "akash12xpq68caw2aqdu70jm4k792g0v9prhrpdctrez", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "akashvaloper1uepjmgfuk6rnd0djsglu88w7d0t49lmlsqkfuf", - "botAddress": "akash1weeu5yuj8n23hd87wsdxqqgfmzzz9zt6zrr52c", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "akashvaloper1dgxdep80th2qm8xxk8h3j3g93npsd3a8jg60gz", - "botAddress": "akash1mjq48r6435aewerpruwc8up3tz3rzan2pznsc0", - "runTime": "19:00", - "minimumReward": 100000 - }, - { - "address": "akashvaloper1r4sevyvum9ppqlw75t6h0ex9j4j9dzydu8hczm", - "botAddress": "akash17xyeczwsax3st6aykyu4f28s5n3zg6r7k774je", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "akashvaloper1lxh0u07haj646pt9e0l2l4qc3d8htfx5kk698d", - "botAddress": "akash1uwqjtgjhjctjc45ugy7ev5prprhehc7w5xcfwl", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "akashvaloper1epnrvkylqpk0f2d3us3cvx7x9tlycz42lwf0v6", - "botAddress": "akash1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9j3tp00", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "akashvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pz5w0ry", - "botAddress": "akash1xrrfek3qpz4ja8ccrjklfy064asyqq9k7fmgd4", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "akashvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4cyw7r4", - "botAddress": "akash1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy6chyhw", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "akashvaloper1f6eqjk9zjc7tkzmvdjnz589w0c4sjfx5hd9z3n", - "botAddress": "akash1hlt7mew3x8crsvg44xuz4aqqcugh5mqx3v3hey", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": false }, { "name": "chihuahua", "gasPrice": "0.025uhuahua", "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", - "operators": [ - { - "address": "chihuahuavaloper1ju6mkyxpjsdcmrw0pnx6sd4zu4wywkmhtgnpr5", - "botAddress": "chihuahua1m7xlymnpxl2d4llu67v08e8xs67dj2fzyq79h7", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1wnu86qppv7gwtgz8xul5grs80s3vd6uqzr6jzq", - "botAddress": "chihuahua15xa7vst3jca9qjkngmlysrdfur8gsex93gwmmp", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1m4jcru5nsmtzgt4ha8fj4egau3w89cqzdrngnh", - "botAddress": "chihuahua1uxwnhyjd2m5y6mpqsdg95y4ty0a5jtfrd9m05a", - "runTime": "every 10 minutes", - "minimumReward": 10000000 - }, - { - "address": "chihuahuavaloper1tzk3kzjy7fj4w37hm5v48c2t8vvgukzcqgkz8k", - "botAddress": "chihuahua1r8egcurpwxftegr07gjv9gwffw4fk009gjzyep", - "runTime": "every 15 minutes", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5wnscrkq", - "botAddress": "chihuahua1gvgcxwdk4j46gx3knhuackvqr2hta29zf2kujy", - "runTime": "21:00", - "minimumReward": 1001 - }, - { - "address": "chihuahuavaloper1y6m72uxs6hhsudxqpq73rsdgkjh6nhkqef8pej", - "botAddress": "chihuahua1z0xzvw999jh7ekce7eqnex2fj7cnn6xwdwhzlx", - "runTime": "every 15 minutes", - "minimumReward": 1001 - }, - { - "address": "chihuahuavaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qdqj3xsc", - "botAddress": "chihuahua1xs023g3yj2mavwn3cjclgsh8mlk0kgsj9trcv8", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", - "botAddress": "chihuahua1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczjr22j5", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1glk94taeyhfpje4zaz8dsfxmn4yr3g8kkzv4x5", - "botAddress": "chihuahua106n92ygqknaw5wcgl2e9v0duxxux6ntt42yq2f", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1xwazl8ftks4gn00y5x3c47auquc62ssu4ftf5w", - "botAddress": "chihuahua195mm9y35sekrjk73anw2az9lv9xs5mzterzga4", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper18jlk0pkpr8cnnpjtgu3dqxjvpvlnj6r4e2dtvf", - "botAddress": "chihuahua1v9vae554q55xa4pcfdm0p3kv8nxn50jkee2uvt", - "runTime": "23:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1t85yx95l5pajl9hwkeztawqf8sg9uh4lglgu9w", - "botAddress": "chihuahua1h43hjfxlz8t6y5wewma63jz7melv46gj803735", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper140l6y2gp3gxvay6qtn70re7z2s0gn57zum7h5l", - "botAddress": "chihuahua12xpq68caw2aqdu70jm4k792g0v9prhrprkt2p6", - "runTime": "22:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1zl4vt84hya03e8hu7dx4q4cvn2ts2xdr685p5g", - "botAddress": "chihuahua1weeu5yuj8n23hd87wsdxqqgfmzzz9zt6vdrajq", - "runTime": "22:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper13c2hcctczy695gfs5gc637tc844n6a9unlkaqv", - "botAddress": "chihuahua1mjq48r6435aewerpruwc8up3tz3rzan20vneqh", - "runTime": "19:00", - "minimumReward": 10000000 - }, - { - "address": "chihuahuavaloper166ks8xvs36m0ggyxwavv7rj4d9nqwthgq5g7s8", - "botAddress": "chihuahua1305e92ng6a9pdh3ttc3ld459ckxupxt35ehdm9", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "chihuahuavaloper1m2dxkn94t97m7ah65hv4tg2xu0j2a2k4fdee20", - "botAddress": "chihuahua1e44rluarkdw56dy2turnwjtvtg4wqvs037c2k8", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1s7xrzju6p9dms958j6zkddr7yqg6hmves2qgu4", - "botAddress": "chihuahua1juv28kuzuv85hzl5s3scz5rvlv7c9d5cszruv0", - "runTime": "22:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1l6kfy4xvy0a34fseyhvc6f6k8asukfdz7pahxr", - "botAddress": "chihuahua17xyeczwsax3st6aykyu4f28s5n3zg6r7cs7u2p", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1cy9jhuf3cx2z33duxld7p57gtege6hfh6fyc4w", - "botAddress": "chihuahua1hpwxnnrtsc6zjx4glup77tfe5z54lmtqxrxmy9", - "runTime": "10:40", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1jdlfdu2gaj0tlchzjhdnh5r7lfzt6l0zxlhkr6", - "botAddress": "chihuahua1q200ut9chx7t0nnde4h526vdxwudktgkusgugq", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1lxh0u07haj646pt9e0l2l4qc3d8htfx5pd5hur", - "botAddress": "chihuahua1uwqjtgjhjctjc45ugy7ev5prprhehc7w6gcqk8", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1fm68jvjpk0g7dvdq75czjynyszeaduxt5lc0a8", - "botAddress": "chihuahua1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjrwtn9h", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1x8drn3260ezg3se6j7w7wqhy090u4wsjt9raj8", - "botAddress": "chihuahua1x9e5qkjzzmfhw4dt05aludazwvd5x634ssjj76", - "runTime": "16:00", - "minimumReward": 10000000 - }, - { - "address": "chihuahuavaloper16kctxuen3x2avvjj2mdwsl6g8n7d4vjkq6c6q9", - "botAddress": "chihuahua1m8d834ykcy3dgrdve4xhxky3u3c2tl9d3d7gy9", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "chihuahuavaloper1y53zdszvt87fjg9hn50kg78850el26k0l9wxwl", - "botAddress": "chihuahua1cvj2yu8fmtr2my4a7y4r5rnt69flqvcvfxl4yy", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "chihuahuavaloper17h628d2wtlw6844nzv0ktnq9qtm5qm3pym0k44", - "botAddress": "chihuahua1uc877qk45e23hxxx364dxsp8rehu5addswfj06", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "chihuahuavaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4p40qac2", - "botAddress": "chihuahua1xrrfek3qpz4ja8ccrjklfy064asyqq9ks8mp4d", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "chihuahuavaloper1pzqjgfd25qsyfdtmx9elrqx6zjjvnc9sj52r2y", - "botAddress": "chihuahua1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy5khd0k", - "runTime": "every 1 hour", - "minimumReward": 100000000 - }, - { - "address": "chihuahuavaloper1pqsdyhefm99ag3a45rkqnc5907yksqvz0tqpzg", - "botAddress": "chihuahua1k4hh4mjvtrwq79vhmlrltdsyq08kec7jcnxu9e", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "chihuahuavaloper1az42d6p4m277f305q65lsm4kxk5vzg4jzpzgxv", - "botAddress": "chihuahua1330aewfwzu30vznhxw7aevaz0kn35fdya2nr9t", - "runTime": "every 30 minutes", - "minimumReward": 100000000 - }, - { - "address": "chihuahuavaloper14xwftsm2nrhdzp5md42fg5cf3cndnkz0al2dzs", - "botAddress": "chihuahua1wve3tc8sj42yl3trw3lt0rcyp5medvyascnrf6", - "runTime": "22:00", - "minimumReward": 100000000 - }, - { - "address": "chihuahuavaloper12d2a8hfrq784u9lfxp8qj2huxxglejzrqlylwt", - "botAddress": "chihuahua18m7pnt8enhrlqf4rh7ff9uvcfsr6tmlm4l2wqj", - "runTime": "every 1 hour", - "minimumReward": 100000000 - } - ], "authzSupport": false }, { "name": "gravitybridge", "gasPrice": "0.025ugraviton", - "operators": [ - { - "address": "gravityvaloper1vdvdl3krc9s4plnnvfk58k83lx5v38s6yse5ny", - "botAddress": "gravity1m7xlymnpxl2d4llu67v08e8xs67dj2fzr9pnn5", - "runTime": "23:00", - "minimumReward": 1000 - }, - { - "address": "gravityvaloper187reejg4et24jpggqt55vnc47l926w0p8w70mh", - "botAddress": "gravity1v9vae554q55xa4pcfdm0p3kv8nxn50jk7u42gp", - "runTime": "23:00", - "minimumReward": 1000 - }, - { - "address": "gravityvaloper1zfcmwh56kmz4wqqg2t8pxrm228dx2c6hs487gh", - "botAddress": "gravity1z0xzvw999jh7ekce7eqnex2fj7cnn6xw2tg5mv", - "runTime": "every 15 minutes", - "minimumReward": 1000 - }, - { - "address": "gravityvaloper140l6y2gp3gxvay6qtn70re7z2s0gn57zezczfa", - "botAddress": "gravity12xpq68caw2aqdu70jm4k792g0v9prhrpyn5u9s", - "runTime": "22:00", - "minimumReward": 1000 - }, - { - "address": "gravityvaloper1d63hvdgwy64sfex2kyujd0xyfhmu7r7cgxt5ru", - "botAddress": "gravity1mjq48r6435aewerpruwc8up3tz3rzan2gfv0ya", - "runTime": "19:00", - "minimumReward": 10000 - }, - { - "address": "gravityvaloper1452twnqn0z4l34c9uxudeaetk6hudpqpm0w32x", - "botAddress": "gravity1305e92ng6a9pdh3ttc3ld459ckxupxt3nugml0", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "gravityvaloper1728s3k0mgzmc38eswpu9seghl0yczupyhc695s", - "botAddress": "gravity1uwqjtgjhjctjc45ugy7ev5prprhehc7wad8kjd", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "gravityvaloper1uuzcgwd0yerq6yfk2fewfknl0rf6aqgdd0p3a3", - "botAddress": "gravity1hcgxjhvv43g9z6dc58g428jgc8hdlmf7sl2d82", - "runTime": "12:00", - "minimumReward": 1000 - }, - { - "address": "gravityvaloper14vaak0zdjtnvh92hw9ypwxj76093yrgzmuguar", - "botAddress": "gravity1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9m657na", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "gravityvaloper122mak6mduuzyww573jlpgv57qhk96aqgu7sd8g", - "botAddress": "gravity1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97z9sv0tn", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "gravityvaloper1epfpvqsc34sfserdx8x4t3aszdkar3w684fwr6", - "botAddress": "gravity1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjyt59pa", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "gravityvaloper1vyd4k5j636erx5y5kdqghdu3rfjtwc48vdc7r6", - "botAddress": "gravity1grfk7t0k42yxpwqhr7kt4te7j73m2a3vx40gyw", - "runTime": "19:37", - "minimumReward": 10000 - }, - { - "address": "gravityvaloper10m848yf5f66qytun249z9k56nvtvknwenp0dts", - "botAddress": "gravity1kxd2f28ugzwyv9twexw88kpajvgf94xgeghxtr", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "gravityvaloper1xf869399xq4pnxsllzvnwdcra8ly3huj5cnwh5", - "botAddress": "gravity1zks5qjck40m827v2m83la5r3y6lkudw8pjvvxw", - "runTime": "08:00", - "minimumReward": 1000000 - }, - { - "address": "gravityvaloper1pv7ae4kvlyme7r443xl07e2mjuulqr3cs0v89w", - "botAddress": "gravity1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wqewxt0r", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "gravityvaloper1z5nxs28a8krdhv6z6jw7gs5k6qr43xycr8wc6k", - "botAddress": "gravity1rjvyczwh5akp2ylg06dr4s2vlx0jmdwvc3cwp8", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "gravityvaloper1h4w306x3tm39gas6evu326c9ycxwrvk4drnv5t", - "botAddress": "gravity1jrjdqzxchlt09gj6qvn4ds8suxh9aqwynngmtu", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "gravityvaloper1yyduggdnk5kgszamt7s9f0ep2n6hylxrtatu5g", - "botAddress": "gravity1k8mymzl8sapy2yqyyce5a35u09rnuae7shvmwg", - "runTime": "18:00", - "minimumReward": 1000000 - }, - { - "address": "gravityvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pskxg9g", - "botAddress": "gravity1xrrfek3qpz4ja8ccrjklfy064asyqq9khzyh38", - "runTime": "every 1 hour", - "minimumReward": 5000000 - } - ], "authzSupport": true }, { "name": "regen", "gasPrice": "0.03uregen", "ownerAddress": "regenvaloper1c4y3j05qx652rnxm5mg4yesqdkmhz2f6dl7hhk", - "operators": [ - { - "address": "regenvaloper1c4y3j05qx652rnxm5mg4yesqdkmhz2f6dl7hhk", - "botAddress": "regen1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczw5vc9j", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "regenvaloper1gjvu75cq6qxyrtdv66lx9xe92jw9gqdetagaze", - "botAddress": "regen1mjq48r6435aewerpruwc8up3tz3rzan2nm4th3", - "runTime": "19:00", - "minimumReward": 10000 - }, - { - "address": "regenvaloper1ceunjpth8nds7sfmfd9yjmh97vxmwqfyf7r2cn", - "botAddress": "regen1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9qgd6q3", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "regenvaloper1kzhxmgp8z05zrj90nd8h5qx9pm949an5y7nwsz", - "botAddress": "regen1hl0jgxre5z0nkyjj07c5vfdy44yk44atxafsee", - "runTime": "22:30", - "minimumReward": 2000000 - }, - { - "address": "regenvaloper1vnxgn4uwtr0ug8z3u07c26chxn5a95lggwxtzv", - "botAddress": "regen1zks5qjck40m827v2m83la5r3y6lkudw86q4g4z", - "runTime": "08:00", - "minimumReward": 1000000 - }, - { - "address": "regenvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4ucmxys", - "botAddress": "regen1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygp3lcs", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "regenvaloper1u26sz6kydnmpr5zq6nureav4xhasak8nglmggk", - "botAddress": "regen1330aewfwzu30vznhxw7aevaz0kn35fdypa43jd", - "runTime": "every 30 minutes", - "minimumReward": 1000000 - }, - { - "address": "regenvaloper1vn6sachfy4aacyl5gwc4ykkn6kaqsqv3vzjd8t", - "botAddress": "regen1k8mymzl8sapy2yqyyce5a35u09rnuae7t94lay", - "runTime": "18:00", - "minimumReward": 1000000 - }, - { - "address": "regenvaloper1p3vzy48erskut3z36wgkquthplvyq7h6mvdlwa", - "botAddress": "regen1hlt7mew3x8crsvg44xuz4aqqcugh5mqxr4hvk6", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "terra", "apyEnabled": false, "gasPrice": "0.015uluna", - "operators": [ - { - "address": "terravaloper1f2t96sz9hnwsqnneux6v28xfgn07pkxjduvwjz", - "botAddress": "terra194ytn50yhh67rdha8akhs7c6zulnz4n20nnrwr", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "terravaloper13slfa8cc7zvmjt4wkap2lwmlkp4h3azwltlj6s", - "botAddress": "terra1305e92ng6a9pdh3ttc3ld459ckxupxt33gqrc8", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "terravaloper108lmrztvc3pc3w774shgvpry4d3lf79k2ummna", - "botAddress": "terra1hl0jgxre5z0nkyjj07c5vfdy44yk44atlmcvda", - "runTime": "22:30", - "minimumReward": 10000 - }, - { - "address": "terravaloper1audgfvmgt0js54p3s8kj3r40uwej6vy2tv6rrw", - "botAddress": "terra1hz2dutqm7e0t7q3h5cm9d8lchztskm8pekadus", - "runTime": "17:28", - "minimumReward": 50000 - }, - { - "address": "terravaloper12079m57cew2v02zs624zvyed5479an9wxh03fy", - "botAddress": "terra1knhcrmhcm4d6apcj4vglelax7sz4fm8759s6mj", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "terravaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvgk6d65", - "botAddress": "terra1x74w72vp3nug9txqpjzpl4cmh4t2kmjs8uz25r", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "sentinel", "gasPrice": "0.02udvpn", - "operators": [ - { - "address": "sentvaloper1hms0qpe27uulkllw96tmeqnl4hvxsf4vwp729v", - "botAddress": "sent1m7xlymnpxl2d4llu67v08e8xs67dj2fzuw9jjn", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "sentvaloper138dhtmfslt0ls4ec0jumj8tyajry4dfuv27jay", - "botAddress": "sent194ytn50yhh67rdha8akhs7c6zulnz4n2jvl6gv", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "sentvaloper10y0044zsacejntznk6eatvz7mcekqv357fhl7q", - "botAddress": "sent1n3m6rj0w9lgzg7leppnaru2xguraxhqc7uvx0f", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "sentvaloper1gwqt4jzhtvms8u57gpl4tzxjxqq8fss2j3hx48", - "botAddress": "sent1mjq48r6435aewerpruwc8up3tz3rzan2hzgw96", - "runTime": "19:00", - "minimumReward": 1000000 - }, - { - "address": "sentvaloper1tjgec0ssfrlldmut69xsp8vzljugg0g306aae2", - "botAddress": "sent1e44rluarkdw56dy2turnwjtvtg4wqvs0fsran2", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "sentvaloper18mtspwzngrtvtg0l7vuls3dhwp96jsf2t4zcd5", - "botAddress": "sent17xyeczwsax3st6aykyu4f28s5n3zg6r7q79t0v", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "sentvaloper1lxh0u07haj646pt9e0l2l4qc3d8htfx543ss9m", - "botAddress": "sent1uwqjtgjhjctjc45ugy7ev5prprhehc7wzxrhn2", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "sentvaloper1wd944at7mn6eqm7c7m5lfd9ej3g6astqawjnqn", - "botAddress": "sent1m5hgzum68rjf4c7zhezgkj8hlnmr0kghd6djwl", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "sentvaloper1vw60jkrl5keyw9qa2cdwtrrytpq3c8f6q7hyrl", - "botAddress": "sent1k2ljxq7uhefwqesda39hr0p5pf4txvk0y6yrw2", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": false }, { "name": "dig", "gasPrice": "0.0025udig", "ownerAddress": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", - "operators": [ - { - "address": "digvaloper1vm6m78glgfjaepaxwcdg6py380q7yasds2k2t8", - "botAddress": "dig1m7xlymnpxl2d4llu67v08e8xs67dj2fzlp6q58", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", - "botAddress": "dig1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczfzw03d", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "digvaloper1mcm5pj79epddgms7e4r4f93w2m5lxh496tv8h2", - "botAddress": "dig1mjq48r6435aewerpruwc8up3tz3rzan25dhurw", - "runTime": "19:00", - "minimumReward": 100000 - }, - { - "address": "digvaloper184tx500pdv53uua98ezu2vy2zcfp5nfwrsjnz7", - "botAddress": "dig1305e92ng6a9pdh3ttc3ld459ckxupxt30cngcu", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "digvaloper1up3slj4nr5y23gxkvqn8h5ra9mv0pj2vpemf5t", - "botAddress": "dig17xyeczwsax3st6aykyu4f28s5n3zg6r7r36efc", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "digvaloper1j80fpcsumfkxypvydvtwtz3j4sdwr8c2gfr33l", - "botAddress": "dig1zr3hn5faf8tjdc2h576qt02g8t5h9pevxxh9lf", - "runTime": "22:00", - "minimumReward": 10000 - }, - { - "address": "digvaloper1zqp8f7ehuf64psmg0tz3cydacgk2nshg338e8m", - "botAddress": "dig1v9vae554q55xa4pcfdm0p3kv8nxn50jkzcwe0j", - "runTime": "23:00", - "minimumReward": 10000 - }, - { - "address": "digvaloper1dv3v662kd3pp6pxfagck4zyysas82adspfvtw4", - "botAddress": "dig1uwqjtgjhjctjc45ugy7ev5prprhehc7wpfu947", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "digvaloper1s8kaf75yr6v693lhcdwx9r6d3z0sgs2nns36pq", - "botAddress": "dig1e44rluarkdw56dy2turnwjtvtg4wqvs02lu047", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "digvaloper1uc8nz85axexc9svkxjy0ccm28cc0uctvs33ewg", - "botAddress": "dig1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9870d5w", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "digvaloper1xwazl8ftks4gn00y5x3c47auquc62ssuz3xtv7", - "botAddress": "dig195mm9y35sekrjk73anw2az9lv9xs5mztzzxd7v", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "digvaloper1fhp54fwlfmpwwgrnfwk3v47v53yjtp8fw6nelw", - "botAddress": "dig1wtcvjqx8097gtkjdemle9c0lm8gczm2a9va2hg", - "runTime": "11:00", - "minimumReward": 1000 - } - ], "authzSupport": false }, { "name": "bitcanna", "gasPrice": "0.001ubcna", - "operators": [ - { - "address": "bcnavaloper1e8twck404wzkfm5sd064dl0ssse3haxau0nqf6", - "botAddress": "bcna1m7xlymnpxl2d4llu67v08e8xs67dj2fza9r27w", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "bcnavaloper1fghw3chlyavtdtlns22ysza77fwygtmrhwwf6m", - "botAddress": "bcna15xa7vst3jca9qjkngmlysrdfur8gsex9gdn5j3", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "bcnavaloper1r7up4azsnmprakycclqrh60phvq6zptq8lx8qk", - "botAddress": "bcna1mjq48r6435aewerpruwc8up3tz3rzan2kfwkf8", - "runTime": "19:00", - "minimumReward": 100000 - }, - { - "address": "bcnavaloper1s43h79qx6j85whr2p6060wvxjuyq9yqdak39dj", - "botAddress": "bcna1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zmswkxf", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "bcnavaloper12wskdt5tcc9yx5ajdx8xjjujuc0fgs5p54j6ug", - "botAddress": "bcna1m5hgzum68rjf4c7zhezgkj8hlnmr0kghv3t2zz", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "bcnavaloper1gsq4dsxvgsswkgrsftz0k905rzjc0n2hv26xgj", - "botAddress": "bcna1hl0jgxre5z0nkyjj07c5vfdy44yk44atr0jd80", - "runTime": "22:30", - "minimumReward": 20000000 - }, - { - "address": "bcnavaloper1xwazl8ftks4gn00y5x3c47auquc62ssuxx8m5k", - "botAddress": "bcna195mm9y35sekrjk73anw2az9lv9xs5mztqxl859", - "runTime": "every 1hour", - "minimumReward": 10000 - }, - { - "address": "bcnavaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pxqv0cj", - "botAddress": "bcna1xrrfek3qpz4ja8ccrjklfy064asyqq9kfzxwua", - "runTime": "every 1 hour", - "minimumReward": 10000000 - }, - { - "address": "bcnavaloper1t5rvn85jhmul9rxn3qryhrd99fqcddkz5357rz", - "botAddress": "bcna1zks5qjck40m827v2m83la5r3y6lkudw8ljw4t5", - "runTime": "08:00", - "minimumReward": 10000000 - }, - { - "address": "bcnavaloper1ttkg00prt6w9a9zsgh9ywvftmqm22n4z5xezma", - "botAddress": "bcna15enapnszjg5en949x326ae3deu35dmw5hjed0z", - "runTime": "every 30 minutes", - "minimumReward": 1000000 - }, - { - "address": "bcnavaloper1ldzku04m0hz2qslr0e3ddy2t4vgm63x8ngjs3r", - "botAddress": "bcna1k8mymzl8sapy2yqyyce5a35u09rnuae7whwzrj", - "runTime": "18:00", - "minimumReward": 1000000 - } - ], "authzSupport": false }, { "name": "emoney", "apyEnabled": false, "gasPrice": "0.08ungm", - "operators": [ - { - "address": "emoneyvaloper1039h52c70xlqwfqh29gmqq70zwwf8xrs3t9mps", - "botAddress": "emoney1m7xlymnpxl2d4llu67v08e8xs67dj2fzgkflpp", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 100000 - }, - { - "address": "emoneyvaloper1xpyajvwsrt6kg68g0uwvy00v9fa7v5f0ygljdp", - "botAddress": "emoney15xa7vst3jca9qjkngmlysrdfur8gsex9a7epd7", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 100000 - }, - { - "address": "emoneyvaloper1z0cwp2wezmaehlhzdmqpx55vhxupuk52d7ucjy", - "botAddress": "emoney194ytn50yhh67rdha8akhs7c6zulnz4n2x5nhm7", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "emoneyvaloper1fskgtauswg2d0p79d9gdqj2yjyfulg3d65dtue", - "botAddress": "emoney1mjq48r6435aewerpruwc8up3tz3rzan2r6yrkg", - "runTime": "19:00", - "minimumReward": 2000000 - }, - { - "address": "emoneyvaloper1ggpn5jtrgf5hhzw79la6k4h9jgcws3erjzccuh", - "botAddress": "emoney1305e92ng6a9pdh3ttc3ld459ckxupxt3c0qhd6", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "emoneyvaloper1r7v26mcg7frd8l44p5vr2wfv7eqrhpagffvncl", - "botAddress": "emoney1v9vae554q55xa4pcfdm0p3kv8nxn50jk40ax65", - "runTime": "23:00", - "minimumReward": 10000 - }, - { - "address": "emoneyvaloper1lxh0u07haj646pt9e0l2l4qc3d8htfx5ev9y8d", - "botAddress": "emoney1uwqjtgjhjctjc45ugy7ev5prprhehc7wk706qc", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "emoneyvaloper10rk00srmct4z5huqjh7fc64vgqasl46jwm9g9l", - "botAddress": "emoney1m5hgzum68rjf4c7zhezgkj8hlnmr0kghezplad", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "emoneyvaloper1sz7rp920hgupfl0p6c7kwkaes8q8tx3lmzhcrc", - "botAddress": "emoney1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9sfujpg", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "emoneyvaloper149vyxd36kxpg46rralaw6eejv4d9daqc3nv642", - "botAddress": "emoney1grfk7t0k42yxpwqhr7kt4te7j73m2a3vdx8ykm", - "runTime": "19:37", - "minimumReward": 100000 - }, - { - "address": "emoneyvaloper1xwazl8ftks4gn00y5x3c47auquc62ssudg660q", - "botAddress": "emoney195mm9y35sekrjk73anw2az9lv9xs5mzt444jt2", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "emoneyvaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvx7ssph", - "botAddress": "emoney1hlt7mew3x8crsvg44xuz4aqqcugh5mqxn5xyhr", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": false }, { "name": "kava", "apyEnabled": false, "gasPrice": "0.00008ukava", - "operators": [ - { - "address": "kavavaloper1m8428ygqwa4yahjtc9ulp9n7eqwt7dn37n8vas", - "botAddress": "kava142w4wme2n75ggckvxdzhqtlfypxttpt80c7jka", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 10000 - }, - { - "address": "kavavaloper1hztgl202pvetspxch0pcchaqzrduw4qq64q08u", - "botAddress": "kava194ytn50yhh67rdha8akhs7c6zulnz4n24za76y", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "kavavaloper17jr6wyhm2twyph6d88l5ux945gwmf8vfkhckda", - "botAddress": "kava1v9vae554q55xa4pcfdm0p3kv8nxn50jkxen0mw", - "runTime": "23:00", - "minimumReward": 1000 - }, - { - "address": "kavavaloper1gd5hf545caremavv28apr9mzf7f7ns395c27mq", - "botAddress": "kava1mjq48r6435aewerpruwc8up3tz3rzan2sv22hj", - "runTime": "19:00", - "minimumReward": 10000 - }, - { - "address": "kavavaloper125s8t5c6ypwee7ytun90lnhgpls2zl3vta43aj", - "botAddress": "kava103yfkaqzwl0me0znucd25lrn66hzh0zf4hrpek", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "kavavaloper1ptyzewnns2kn37ewtmv6ppsvhdnmeapvl7z9xh", - "botAddress": "kava1hlt7mew3x8crsvg44xuz4aqqcugh5mqxqzgdke", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": false }, { "name": "desmos", "apyEnabled": false, "gasPrice": "0.001udsm", - "operators": [ - { - "address": "desmosvaloper1zngdx77g9ywnwmwpwvj9w2eqcs6fhw78gn02d8", - "botAddress": "desmos1pu0pmlvakgdn7lzrwx2rgj6xjvcyln0l4cdfl2", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000000 - }, - { - "address": "desmosvaloper1xk2kv26fjd9zrg4l3qc7lwxv03w4ydeeppqkkp", - "botAddress": "desmos1js340eq8082essggjtv8lf9df7qlnny8xuj9ul", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000000 - }, - { - "address": "desmosvaloper18yazgsq8yvn2f8c734fmnu2ssfrzpw7l8tqckm", - "botAddress": "desmos1ueyqd3shd7lrpqacjzap8vy6akh2ek8l40fn0y", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "desmosvaloper1xwazl8ftks4gn00y5x3c47auquc62ssu07r7m6", - "botAddress": "desmos1hcuv07593sxu4luumnh4dy2fujaxyh7xd9rya5", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "desmosvaloper1np3qcmwdju3rtc4a5mavfyflum5gqsljy2vucp", - "botAddress": "desmos1v80dwktqstytcnz5esa0hxxam3d83q4xfedj4d", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "desmosvaloper17ue85ck027c4grv7nuks7k7p4fqnlc55uqhskj", - "botAddress": "desmos1z0xzvw999jh7ekce7eqnex2fj7cnn6xw6rhufu", - "runTime": "every 15 minutes", - "minimumReward": 1000 - }, - { - "address": "desmosvaloper1r0emf6ap6y5advzgtsf4uz3g68p5t5484j4alp", - "botAddress": "desmos1nrt8va6he35w4vclr6sejkqpvsmr4vtas330me", - "runTime": "22:00", - "minimumReward": 1000 - }, - { - "address": "desmosvaloper1lzzctd3a839xxk7vf4cwuhcvgcnmxrhrx7d92e", - "botAddress": "desmos1mjq48r6435aewerpruwc8up3tz3rzan2cpn8kd", - "runTime": "19:00", - "minimumReward": 10000 - }, - { - "address": "desmosvaloper1lf3fg79gf2qf6ept7ec22kjd8s6gj3swr2ca0v", - "botAddress": "desmos1e44rluarkdw56dy2turnwjtvtg4wqvs0xnc5qa", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "desmosvaloper1d6xe3ldswgaurszrp3emspvhspvu7hxm4ty8mv", - "botAddress": "desmos1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97z4cn8er", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "desmosvaloper19c6nnp6afxj82ehxtlejyvg6zh58050jcgpdj6", - "botAddress": "desmos1m5hgzum68rjf4c7zhezgkj8hlnmr0kghzekmag", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "desmosvaloper1zm3l7p8n5dxqeadsfxy3rd0j3c2knnx3chg77a", - "botAddress": "desmos19n0raswpfd9f7779ah637h20ltae0g7ussa8at", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "desmosvaloper1s5gnmccngltteh6ugtsz7fdrykc294tnw2e7pz", - "botAddress": "desmos1hl0jgxre5z0nkyjj07c5vfdy44yk44atd80uc9", - "runTime": "22:30", - "minimumReward": 100000 - }, - { - "address": "desmosvaloper1jrld5g998gqm4yx26l6cvhxz7y5adgxqzfdpes", - "botAddress": "desmos1hcgxjhvv43g9z6dc58g428jgc8hdlmf7qh4946", - "runTime": "12:00", - "minimumReward": 10000 - }, - { - "address": "desmosvaloper1pe2fwwffxn2qnykeut8wzm20sv6eevxedlgpfu", - "botAddress": "desmos169n5p9xuhsnn85xunkg7sdmhtwqgwc3jhnzc0u", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "desmosvaloper1axt95v5rpc97dtnl24mxtfj95gpxu59vn6xyvw", - "botAddress": "desmos14a78h6202u4xg6phjnd8rmr5fq98zzdl3fa6vc", - "runTime": "every 4 hour", - "minimumReward": 10000 - }, - { - "address": "desmosvaloper1sx0gd08yfjl76knllyqs7vgc8mvcqg88uhspvd", - "botAddress": "desmos1yqx9sggaw3evt6pwxajf8lh0q7lxlp3yfyd6x4", - "runTime": "08:00", - "minimumReward": 1000000 - }, - { - "address": "desmosvaloper1mzar2v3xh3at49t2azg9qnkuc9nfvyurqmatuv", - "botAddress": "desmos1g4zywrluj6ac0xpu6f6jqul52rv0am66r8qy5h", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "desmosvaloper1c5aqg0qjlqj0g99ee0seaeneqgpcz07sepmqx6", - "botAddress": "desmos1k8mymzl8sapy2yqyyce5a35u09rnuae7qlnnuc", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "desmosvaloper1v0vje887uzfxzc4rgdj5vm8lnkufuylk8ej7mz", - "botAddress": "desmos1uxmek9gvf8r862h0fwtu5cfzeypl569sfs4zml", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "cryptoorgchain", "gasPrice": "0.025basecro", - "operators": [ - { - "address": "crocncl15m2ae4c2ajpkz6hw0d4ucvwfyuwq8ns5z369u8", - "botAddress": "cro1v9vae554q55xa4pcfdm0p3kv8nxn50jkzh0t3c", - "runTime": "23:00", - "minimumReward": 100000 - }, - { - "address": "crocncl1tkev46yqrjzrjzrqtty30ex68eja2zcltyq2vj", - "botAddress": "cro1hl0jgxre5z0nkyjj07c5vfdy44yk44atpy24nv", - "runTime": "22:30", - "minimumReward": 1000000 - }, - { - "address": "crocncl1s7cu28403gzdvy5tttyskm3zxjejxcv6j5ngp9", - "botAddress": "cro1lj3qga40dc0w2pacfrd2s6puk43ua2vczlv690", - "runTime": "16:30", - "minimumReward": 1000000 - }, - { - "address": "crocncl1v8l3zyrl02j3zdjj8qma7pshmhfmvwxfnrf3vs", - "botAddress": "cro1rkvlrss04eay5mll79nclr3nc34rq9qnedc6k0", - "runTime": [ - "00:00", - "12:00" - ], - "minimumReward": 1000000 - }, - { - "address": "crocncl14vxzdsxtmat7hvz58trk823adg3pjh4xh6ghm6", - "botAddress": "cro1dfdcgy3y7jd8q9qfk427g6k53tg97uwnhwlghx", - "runTime": [ - "00:00", - "12:00" - ], - "minimumReward": 100000 - }, - { - "address": "crocncl1up7anp4cq0kc799dccgfhesxp0mxklgrpgf856", - "botAddress": "cro1vltdfsd0wujjhqks9dzx3qe5njdpkfszuy5zyv", - "runTime": "00:00", - "minimumReward": 100000 - } - ], "authzSupport": true }, { "name": "evmos", "enabled": false, "apyEnabled": false, - "operators": [ - { - "address": "evmosvaloper1shvcjzhcxau6qtlz9w82a646ecwp4hq7ayqt0w", - "botAddress": "evmos194ytn50yhh67rdha8akhs7c6zulnz4n2tkcdkt", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "evmosvaloper1x5y65hgngh77sxta6s2vpjnfjep3ltllyhf2hs", - "botAddress": "evmos16e67hq4eqgnfw6pxu8v33mz259z5r30d9v2uq2", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "evmosvaloper1chx7v975g72xuw8kdpjt94dh35daqqfyc37kys", - "botAddress": "evmos1k0fpumkxl35p3se0n52e6dc3mh8vy24kn0uqsg", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "evmosvaloper14zatq4jagqtm9ejgvglnv0t364d88u80futp65", - "botAddress": "evmos1xul3g9w27tlns7k3d2reqjxqdsgyknkx74prwv", - "runTime": "21:00", - "minimumReward": 1001 - }, - { - "address": "evmosvaloper1pz3mcahcrglf3md4lggax5r95gvmppc6x5w7hw", - "botAddress": "evmos17va68yw35aleakz62jwqn24s22fd3k545mclkl", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "evmosvaloper146mfv59nypacvs5l9h0takrcv8jtlk90wr0ggp", - "botAddress": "evmos17xyeczwsax3st6aykyu4f28s5n3zg6r7eyzu3t", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "evmosvaloper1qp49y6vh8vvv5yf8ule8fwx6sss82ncz39tunl", - "botAddress": "evmos10g8dt086qsfw0s27us8uvhnnftf9aanx4afg3h", - "runTime": "21:05", - "minimumReward": 1000000000000000000 - }, - { - "address": "evmosvaloper1f6m9d94lkenw9fy5wmytatt76l849kx6ugdz70", - "botAddress": "evmos175l97fdm2a6x5xp82psec52elzu8nsl7dmc7an", - "runTime": "19:00", - "minimumReward": 1000 - }, - { - "address": "evmosvaloper1aep37tvd5yh4ydeya2a88g2tkjz6f2lcrfjumh", - "botAddress": "evmos1v9vae554q55xa4pcfdm0p3kv8nxn50jkcdkuhp", - "runTime": "23:00", - "minimumReward": 1000000 - }, - { - "address": "evmosvaloper1qhazu8zleyn5chrkxymewx3xw5guq2vm6q7zl0", - "botAddress": "evmos1t8e0n59ta6dnf38vqel7ydme9tajvjegs4jw5g", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "evmosvaloper18zt355ccyxd3kj23mz5hdz00qqn5lk5kjnj74m", - "botAddress": "evmos1uwqjtgjhjctjc45ugy7ev5prprhehc7wmuyqdd", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "evmosvaloper1a36r4jvcprvewxnr2qhfs0h4spec35238s2h7t", - "botAddress": "evmos1axp36fwjy2vzlaym52urh80wq9l7z2t0shrj2y", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "evmosvaloper1ce4vh0e5kanlgc7z0rhcemvd8erjnfzcyfecl7", - "botAddress": "evmos1grfk7t0k42yxpwqhr7kt4te7j73m2a3vqyv7mw", - "runTime": "19:37", - "minimumReward": 1000000 - }, - { - "address": "evmosvaloper19fxanpnjlggzuur3m3x0puk5ez7j9lrttexwsw", - "botAddress": "evmos1hl0jgxre5z0nkyjj07c5vfdy44yk44atm7nz44", - "runTime": "22:30", - "minimumReward": 1000000 - }, - { - "address": "evmosvaloper17vze0tk7q7gwpd6jt69p4m5svrty40yw9a88e3", - "botAddress": "evmos13craf042e76qasxu6f8vl9ds7vtcajyxmr9asz", - "runTime": "22:30", - "minimumReward": 1000000 - }, - { - "address": "evmosvaloper125fkz3mq6qxxpkmphdl3ep92t0d3y969xmt8hz", - "botAddress": "evmos1spdlljxpvgzn52qky7hafsdqxpchpydqfg403n", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "evmosvaloper10qpycc2egucukw8afcz4us7xlxxmfwh6rscvjz", - "botAddress": "evmos1uzjr9euyy4dflxta40lar6w5jm8jdm8gpeszf0", - "runTime": "20:00", - "minimumReward": 1000000000000000 - }, - { - "address": "evmosvaloper1r4egnngxqudu7nsxa6uygcygn36j30v7w3luh6", - "botAddress": "evmos1czfankzgd7p2245kyszuse70jy7sjzmnz5qmul", - "runTime": "every 1 hour", - "minimumReward": 1000000000000000 - }, - { - "address": "evmosvaloper1vw76ju5rwt9us7kz8tg42t6yfmq2jftydu74u6", - "botAddress": "evmos14q8vu02v3npvnrxkt7te05tx6ral53rx4hh80f", - "runTime": "21:00", - "minimumReward": 1000000000000000 - }, - { - "address": "evmosvaloper1rhddecqpwfjdkwmu6lmfzekh936kcu6wxs7jxa", - "botAddress": "evmos19mgc0j2m7pfzuc9lthqa6r24laxk094d3a979q", - "runTime": "every 3 hours", - "minimumReward": 1000000000000000 - }, - { - "address": "evmosvaloper1rh9uz23gmhr6q92a45qrath4fr9ffefz4eas0z", - "botAddress": "evmos1w2v6ztzta7hra7q3j3g6g363tqnh02th0mauj3", - "runTime": "every 1 hour", - "minimumReward": 1000000000000000000 - }, - { - "address": "evmosvaloper1tdss4m3x7jy9mlepm2dwy8820l7uv6m2vx6z88", - "botAddress": "evmos1f54lck35ehjh3x0e62e7gak9h7zn4vl7s7s3cj", - "runTime": "every 30 minutes", - "minimumReward": 1000000000000000000 - }, - { - "address": "evmosvaloper152644qg2h4qmqutftr9fnug0ypxw2apmjgk6rp", - "botAddress": "evmos1hkzpq664h8dnwksqffdmhqmgsme773a7eqj2rg", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "sifchain", - "operators": [ - { - "address": "sifvaloper1ecv3sdx8h54ckv8q8u67cyk0nj37rffwpj7cdx", - "botAddress": "sif1m7xlymnpxl2d4llu67v08e8xs67dj2fzzguaeh", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "sifvaloper1esnwgt6g6x2d3m37pw9lkgsxgzenc36n3a4n0f", - "botAddress": "sif15xa7vst3jca9qjkngmlysrdfur8gsex9hqvr4g", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "sifvaloper1gstrmcv3tqrskdj7uqu4x4tkh8nfw4qp9f85pm", - "botAddress": "sif1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zya3pps", - "runTime": "every 1 hour", - "minimumReward": 100000000 - }, - { - "address": "sifvaloper1lnhxf6war6qlldemkqzp0t3g57hpe9a6nh3tyv", - "botAddress": "sif194ytn50yhh67rdha8akhs7c6zulnz4n2v2x4rg", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "sifvaloper1uepjmgfuk6rnd0djsglu88w7d0t49lmlmxj56z", - "botAddress": "sif1weeu5yuj8n23hd87wsdxqqgfmzzz9zt629p9uf", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "sifvaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5w0djvt9", - "botAddress": "sif1gvgcxwdk4j46gx3knhuackvqr2hta29z0z5yud", - "runTime": "21:00", - "minimumReward": 1001 - }, - { - "address": "sifvaloper1kttxh8lxsmez56pen3cw307raf45fj9vmvwn5j", - "botAddress": "sif1mjq48r6435aewerpruwc8up3tz3rzan2fy3pw7", - "runTime": "19:00", - "minimumReward": 1000 - }, - { - "address": "sifvaloper1a2ly5lyry8l4wvx27fzdu6ha5mj53sxceu4uvj", - "botAddress": "sif1m8d834ykcy3dgrdve4xhxky3u3c2tl9dh9us2v", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "sifvaloper1r9ssdmc2xm3kv2y5m35mhrvnqms4pjw27umt2l", - "botAddress": "sif1hl0jgxre5z0nkyjj07c5vfdy44yk44atuzd6qk", - "runTime": "22:30", - "minimumReward": 100000 - }, - { - "address": "sifvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pfj2j90", - "botAddress": "sif1xrrfek3qpz4ja8ccrjklfy064asyqq9kk0eemy", - "runTime": "every 1 hour", - "minimumReward": 10000000 - }, - { - "address": "sifvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4nz2r97", - "botAddress": "sif1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyj744pl", - "runTime": "every 1 hour", - "minimumReward": 10000000 - }, - { - "address": "sifvaloper14un425lvtpe7dd04fnld4s9y0gmeaxu3f0yxlu", - "botAddress": "sif1330aewfwzu30vznhxw7aevaz0kn35fdymz3mtz", - "runTime": "every 30 minutes", - "minimumReward": 10000000 - }, - { - "address": "sifvaloper1ta2yj7yee373evu8xy73gludh8psy55rnczcqw", - "botAddress": "sif1pfp235023cktu4aqlhuka8mgs8k8g9vw7dxu3h", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "sifvaloper1x5x0zghfep8jkg4n2gz88del6n22yescv3n3rs", - "botAddress": "sif1hlt7mew3x8crsvg44xuz4aqqcugh5mqxe2nx04", - "runTime": "00:30", - "minimumReward": 1000000000000000000 - } - ], "authzSupport": false }, { "name": "lumnetwork", - "operators": [ - { - "address": "lumvaloper1mpywgfyk6ku9uwfw4t6xhn050cjluhmjck27nx", - "botAddress": "lum1m7xlymnpxl2d4llu67v08e8xs67dj2fzjlwzrg", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000000 - }, - { - "address": "lumvaloper1sgeam9kyphrzrcau8l5an729mtngxahte76zkn", - "botAddress": "lum197yc3g8drkf8fyv6fr4ynddy2n22kauu23ccqt", - "runTime": "22:00", - "minimumReward": 100000 - }, - { - "address": "lumvaloper1y8jham0v3lhlnwmsh0nw8g92fkqxt9gsuml6ux", - "botAddress": "lum1js340eq8082essggjtv8lf9df7qlnny88wzu7n", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000000 - }, - { - "address": "lumvaloper16w9g7epcvwnmya5khpj2kk9x7k39pjp3ufa3uj", - "botAddress": "lum15xa7vst3jca9qjkngmlysrdfur8gsex98h7u0h", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 10000 - }, - { - "address": "lumvaloper1u6jr0pztvsjpvx77rfzmtw49xwzu9kash9slqs", - "botAddress": "lum1v9vae554q55xa4pcfdm0p3kv8nxn50jk0x6mca", - "runTime": "23:00", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5wf0p22w", - "botAddress": "lum1gvgcxwdk4j46gx3knhuackvqr2hta29zl4xmxj", - "runTime": "23:00", - "minimumReward": 1001 - }, - { - "address": "lumvaloper16xvwljz2vqpsdzuskdlqt25e8gxmlpnfq3dcxh", - "botAddress": "lum1r8egcurpwxftegr07gjv9gwffw4fk0097djrdh", - "runTime": "every 1 hour", - "minimumReward": 1001 - }, - { - "address": "lumvaloper1txgtzgdj90mqddhyu5gcpkkdeu9g2fu22d85d7", - "botAddress": "lum1e44rluarkdw56dy2turnwjtvtg4wqvs08pgdz3", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1nnl9ygsp04rhkuh3yrr98cnvke586mxlhpfe3w", - "botAddress": "lum1eml7agwd5x084ennlg6apvskufzrlvnrr687qd", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1068lx20jqswpwtykfezssdkn524kqlmlqjeqpa", - "botAddress": "lum19pqrxrl6n0g0mky4y79hlfzchprmsp5jxdyjew", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1v73py5myadey4cal2wh4nlk99h59jh52muuv55", - "botAddress": "lum1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97z52r7m0", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1up3slj4nr5y23gxkvqn8h5ra9mv0pj2vv70zs4", - "botAddress": "lum17xyeczwsax3st6aykyu4f28s5n3zg6r7w0wm7h", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1axqn30kead92gdsg7pusc6ezxx8ltq465r3hv3", - "botAddress": "lum1mjq48r6435aewerpruwc8up3tz3rzan2enr75p", - "runTime": "19:00", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1kls9ca0h980sqfmt0497jj8kad3lcws6s7gjsg", - "botAddress": "lum1uecj925gwmagk8je8c2exmnty6t2jm7xt57ww8", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1krkmg6f0sjwalkx3nq39yt0upxgys7alme6lps", - "botAddress": "lum1h666jcyjycu90w3j6q6cpswsmzh9f73t0lwkf6", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1y69ary732dmngmj2g2e8m2ssedugya4may0tj5", - "botAddress": "lum1305e92ng6a9pdh3ttc3ld459ckxupxt3zx820n", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "lumvaloper1uqw4e63xwwjmxt6ympdl20dsxtjr7lzx2p20hu", - "botAddress": "lum1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr92qm0rp", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "lumvaloper1zs59ua4l0h6a2hnh08v8qn76f6qh7uxc22vey5", - "botAddress": "lum1hl0jgxre5z0nkyjj07c5vfdy44yk44atv4l96f", - "runTime": "22:30", - "minimumReward": 100000 - }, - { - "address": "lumvaloper1trkdt99yc4je55759s8z4fctrluxf9pmla7k80", - "botAddress": "lum1327kewr0m28edp6ee022myyxzvlf3g5exxpvr7", - "runTime": "14:30", - "minimumReward": 10000 - }, - { - "address": "lumvaloper1z7ss5slnpjfkceahl323ag9tkf3a6yrs88h4ng", - "botAddress": "lum1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgj43m53p", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "lumvaloper1xwazl8ftks4gn00y5x3c47auquc62ssu0kjqgq", - "botAddress": "lum195mm9y35sekrjk73anw2az9lv9xs5mzt0uj0fr", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "lumvaloper1g239l2skjnz3dszek5s5npquza5gvypdrv6w88", - "botAddress": "lum1zks5qjck40m827v2m83la5r3y6lkudw8sgrakj", - "runTime": "08:00", - "minimumReward": 1000000 - }, - { - "address": "lumvaloper1qckuz6ks3kgjjnp26gtw3ya3e2l7tdp4hcvyem", - "botAddress": "lum1g4zywrluj6ac0xpu6f6jqul52rv0am66z4sakm", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "lumvaloper1lnqradz7whff07t60pzetvkhgqamt9xu9f2j6k", - "botAddress": "lum14a78h6202u4xg6phjnd8rmr5fq98zzdlsmdrw5", - "runTime": "every 4 hour", - "minimumReward": 10000 - }, - { - "address": "lumvaloper1p4qz6gqr3esugvm9n49jl2rz0zhup7ww286t65", - "botAddress": "lum1k4hh4mjvtrwq79vhmlrltdsyq08kec7jwvkm30", - "runTime": "every 4 hour", - "minimumReward": 10000 - }, - { - "address": "lumvaloper18rd4dk828pfgw680hr93rxjfmj8zvr5xc03hd3", - "botAddress": "lum1wtcvjqx8097gtkjdemle9c0lm8gczm2agjfgq8", - "runTime": "11:00", - "minimumReward": 1000 - }, - { - "address": "lumvaloper1zy4xcyx25eg9qha503eqjjgz043l5zy96p83dh", - "botAddress": "lum15hfak84jamn59229c7r6umg2purgd9cysa8rw7", - "runTime": "every 5 minutes", - "minimumReward": 1000 - } - ], "authzSupport": true }, { "name": "stargaze", "apyEnabled": false, - "operators": [ - { - "address": "starsvaloper1s679uyrt0uhljj8ws905hetwn87jqdz3n33klw", - "botAddress": "stars15xa7vst3jca9qjkngmlysrdfur8gsex9xp5g3j", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "starsvaloper10wxn2lv29yqnw2uf4jf439kwy5ef00qd0jluj0", - "botAddress": "stars1xs023g3yj2mavwn3cjclgsh8mlk0kgsjjzetx5", - "runTime": [ - "00:00", - "12:00" - ], - "minimumReward": 10000 - }, - { - "address": "starsvaloper1y3cxrze7kmktj93atd42g9rffyg823g0qjqelc", - "botAddress": "stars1eml7agwd5x084ennlg6apvskufzrlvnrzvd27g", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "starsvaloper1r4sevyvum9ppqlw75t6h0ex9j4j9dzydyuhsmz", - "botAddress": "stars17xyeczwsax3st6aykyu4f28s5n3zg6r70ey0qj", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "starsvaloper12v78y2lrpy2euhuzjtd6ssyzz3zllgs0uqk3nn", - "botAddress": "stars1305e92ng6a9pdh3ttc3ld459ckxupxt3rsd73k", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "starsvaloper1mz2qks48v486d9m8wp4l9fxm2e9l0e0kzk79m5", - "botAddress": "stars1mjq48r6435aewerpruwc8up3tz3rzan2c9f22y", - "runTime": "19:00", - "minimumReward": 1000 - }, - { - "address": "starsvaloper1hr4ag3mdzy08rl6r7pga832kvchqctvmzdneds", - "botAddress": "stars1m5hgzum68rjf4c7zhezgkj8hlnmr0kghzavkpp", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "starsvaloper1yskcx5zfkr3nacn6f5zd08yw72zwm4s06x8tpz", - "botAddress": "stars1hcgxjhvv43g9z6dc58g428jgc8hdlmf7qn0gfn", - "runTime": "12:00", - "minimumReward": 10000 - }, - { - "address": "starsvaloper13agg47uffkehwqpvqeqp86aamjmkvmt4hyt9z7", - "botAddress": "stars1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9tk3may", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "starsvaloper17h2x3j7u44qkrq0sk8ul0r2qr440rwgjvlln5d", - "botAddress": "stars14gra89zm8753pfuhvujjulmtkf2qq70fdcp9k9", - "runTime": "17:28", - "minimumReward": 1000000 - }, - { - "address": "starsvaloper1y58hfnm90r4efhlydx0gavz57lvm7k6uulkg3h", - "botAddress": "stars1grfk7t0k42yxpwqhr7kt4te7j73m2a3vke2d2h", - "runTime": "19:37", - "minimumReward": 10000 - }, - { - "address": "starsvaloper1xru87608vdps23q4s79006lcsm0tfxcl4juuy5", - "botAddress": "stars1d72r4aaxkv2cpnxjh6xuklv9z4prcj2qmfv40r", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "starsvaloper1xqum4wt30f3aqw8mmv823ty3x7gpmmde9zfndf", - "botAddress": "stars1r3g7japkptsvt3gv52gvuudprtcue762n08yrf", - "runTime": "12:00", - "minimumReward": 10000 - }, - { - "address": "starsvaloper1926sz228mm6yjlwye9hfxxh0hh4nm4aqfp29l7", - "botAddress": "stars1kxd2f28ugzwyv9twexw88kpajvgf94xgfyjr96", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "starsvaloper162ty3nf7wd5tvqhsu486fexh832fyham0cvphg", - "botAddress": "stars1zks5qjck40m827v2m83la5r3y6lkudw837ffgh", - "runTime": "08:00", - "minimumReward": 1000000 - }, - { - "address": "starsvaloper1jnum75gr8jsn4xn9u48kc9ma5c4mrmvzsvad6j", - "botAddress": "stars1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wqfzrwp6", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "starsvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4qlwk6v", - "botAddress": "stars1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyrld799", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "starsvaloper1j74f2ef2p9ue7tz0jlcg6ejnjhp5evv0wyw0av", - "botAddress": "stars1k8mymzl8sapy2yqyyce5a35u09rnuae7qmf7q3", - "runTime": "18:00", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "comdex", "apyEnabled": false, "ownerAddress": "comdexvaloper17f70yjkvmvld379904jaddx9h0f74n32pjtmp6", - "operators": [ - { - "address": "comdexvaloper17f70yjkvmvld379904jaddx9h0f74n32pjtmp6", - "botAddress": "comdex1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczke9x2p", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "comdexvaloper15xevj3n6eq36t62sjjrsvf6hkuryg8w3pmpqy8", - "botAddress": "comdex1m7xlymnpxl2d4llu67v08e8xs67dj2fzq63f0t", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "comdexvaloper1flh26y7f2vsam4a6snwgrqaxkhe0g2yljstdqm", - "botAddress": "comdex15xa7vst3jca9qjkngmlysrdfur8gsex94jphr5", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "comdexvaloper195re7mhwh9urewm3rvaj9r7vm6j63c4sd78njd", - "botAddress": "comdex194ytn50yhh67rdha8akhs7c6zulnz4n2wctp45", - "runTime": "every 1 hour", - "minimumReward": 1000 - }, - { - "address": "comdexvaloper19qz6sgw7llrft2x05lp4swy569e5sla6gl3cuu", - "botAddress": "comdex1e44rluarkdw56dy2turnwjtvtg4wqvs04yhxwj", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "comdexvaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5wh2dqrg", - "botAddress": "comdex1gvgcxwdk4j46gx3knhuackvqr2hta29zdses23", - "runTime": "21:00", - "minimumReward": 1001 - }, - { - "address": "comdexvaloper14yh3sqetnphupx2r4jrjctpsqqg7jqmnmzpxc4", - "botAddress": "comdex1h43hjfxlz8t6y5wewma63jz7melv46gjr47jfp", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "comdexvaloper1gfe4f7urf866xte5cpmkgsw7q2u97qj06ldtj3", - "botAddress": "comdex1mjq48r6435aewerpruwc8up3tz3rzan2tku4cz", - "runTime": "19:00", - "minimumReward": 1000 - }, - { - "address": "comdexvaloper12dseyeqwsv3lkxlks45p4fp7et4qnzn5vkavjf", - "botAddress": "comdex1v9vae554q55xa4pcfdm0p3kv8nxn50jkar9s57", - "runTime": "23:00", - "minimumReward": 1000 - }, - { - "address": "comdexvaloper10d87jx68zygmwagu9ggzxpept07zs7nmcpyjr6", - "botAddress": "comdex1305e92ng6a9pdh3ttc3ld459ckxupxt3srcprs", - "runTime": "19:15", - "minimumReward": 10000 - }, - { - "address": "comdexvaloper1lxh0u07haj646pt9e0l2l4qc3d8htfx59hp5ft", - "botAddress": "comdex1uwqjtgjhjctjc45ugy7ev5prprhehc7w7jhvwj", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "comdexvaloper120g4zfrj75ezl6cmd466tk0rj9h3dkskc5tvkh", - "botAddress": "comdex1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh3wefn8", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "comdexvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4p3447dz", - "botAddress": "comdex1xrrfek3qpz4ja8ccrjklfy064asyqq9k5a5ddc", - "runTime": "every 1 hour", - "minimumReward": 1000000 - } - ], "authzSupport": false }, { "name": "cheqd", "gasPrice": "25ncheq", - "operators": [ - { - "address": "cheqdvaloper1ny8gd9tsyqhm56agp4vkpa6fldr2n4guthcvfj", - "botAddress": "cheqd1m7xlymnpxl2d4llu67v08e8xs67dj2fzfhltad", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "cheqdvaloper1m0f5mdkusqq24zhf8azjn2j5v2jqwqlt3ukrc0", - "botAddress": "cheqd1ypntyymvjg90ntxwes3hxtmuvvh4fcr8w082sd", - "runTime": "every 1 hour", - "minimumReward": 100000000 - }, - { - "address": "cheqdvaloper1qsp3a2qd6km9g0hczsac8279wcwmzmvzgvre8w", - "botAddress": "cheqd1e44rluarkdw56dy2turnwjtvtg4wqvs0ufeyu5", - "runTime": "21:00", - "minimumReward": 1000 - }, - { - "address": "cheqdvaloper1xwazl8ftks4gn00y5x3c47auquc62ssu5r5utr", - "botAddress": "cheqd195mm9y35sekrjk73anw2az9lv9xs5mzt55rxhx", - "runTime": "every 1 hour", - "minimumReward": 10000000 - }, - { - "address": "cheqdvaloper19f0w9svr905fhefusyx4z8sf83j6et0g3rjhkl", - "botAddress": "cheqd1etsdevgm5ps240lrw9mg3p2j32tj96ktqafpeu", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "cheqdvaloper1ttw6u36w7jxjpadt5eh8vnt5a8wpx6ju2wkuj6", - "botAddress": "cheqd1wve3tc8sj42yl3trw3lt0rcyp5medvyaa0jdrf", - "runTime": "22:00", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "umee", - "operators": [ - { - "address": "umeevaloper10nsytn9zmtfnhk37hqfe2h9v599ld3h5zs9fqj", - "botAddress": "umee1yq67lj5nnkcc2jk9ngfe3v6mt5ra0n9d45v3nc", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "umeevaloper18s567a3dxf09q7erpcp8q20dpauugxwx8xsrcc", - "botAddress": "umee1upe9xslwkqmmd8m7f7nuyhedhjc6saqhy4pldd", - "runTime": "every 15 minutes", - "minimumReward": 100000 - }, - { - "address": "umeevaloper14vmuhgm04ffvzts9jxm2wy5d538g96ru9fsg5v", - "botAddress": "umee1m5hgzum68rjf4c7zhezgkj8hlnmr0kghyhx5wz", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "umeevaloper1py60fpud4w468zn7mthfa25xnzuawjum7w90q2", - "botAddress": "umee194ytn50yhh67rdha8akhs7c6zulnz4n2mp5ug3", - "runTime": "every 1 hour", - "minimumReward": 1 - }, - { - "address": "umeevaloper1gvt9l5tshr0fp8ksl2tuem584z69eyvt9m8yh6", - "botAddress": "umee19pqrxrl6n0g0mky4y79hlfzchprmsp5jp3yygg", - "runTime": "every 6 hours", - "minimumReward": 10000 - }, - { - "address": "umeevaloper1q5z5vdxtvqmf4eq37pm2xnytc69st382cadf0x", - "botAddress": "umee1mmg6pqqnu6ktl4yp0afwzz6g33w8kpw3wajxln", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "umeevaloper142xnwdgpmpzgsyckxwyd5c9ppmmvqa9k7c4fh8", - "botAddress": "umee1kxd2f28ugzwyv9twexw88kpajvgf94xg0wcp2e", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "umeevaloper1sex685w68vvv563nsyv8nezawq4hhg7qjkyhzg", - "botAddress": "umee1s0ncp8tlxwhmvhyrvrglcvl4l2lxd5wq0gfvwe", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "umeevaloper19yy0u0ttam2p9k874vsh2u4tt7e6cmq8h2w9nv", - "botAddress": "umee1rjvyczwh5akp2ylg06dr4s2vlx0jmdwvwhhfqa", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "umeevaloper1sm5a00zlr5wkhr2259shqk9lk3w307jazmf0e5", - "botAddress": "umee1m2g72wffh3l65sjlpxvsgmns3y7z58dyurdzyd", - "runTime": "every 3 hours", - "minimumReward": 50000 - }, - { - "address": "umeevaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4d0h0la", - "botAddress": "umee1jrjdqzxchlt09gj6qvn4ds8suxh9aqwy948u2x", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "umeevaloper10ckmqvzeycglg0hrcnepa0la5kw2q9ys6gquku", - "botAddress": "umee1330aewfwzu30vznhxw7aevaz0kn35fdyvfrjqm", - "runTime": "every 30 minutes", - "minimumReward": 1000000 - }, - { - "address": "umeevaloper1l4xvfq5uj7624m73nn8j4mgra7xq4cpf08q2xj", - "botAddress": "umee1cyjx4pxqx6hxltp8je8mpwrvjhnq3kktj26xms", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "umeevaloper1gq5fvkv479n26x63dkh8zscqahxu4tz4ph3jn9", - "botAddress": "umee1k8mymzl8sapy2yqyyce5a35u09rnuae7x3ru0j", - "runTime": "21:00", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "bitsong", - "operators": [ - { - "address": "bitsongvaloper1pn6mhrwq8vdhns366jwxkggmhmxypuhrrc4huu", - "botAddress": "bitsong1vn2fc2cdt2r5kseptl86rstqudxly2c3jxw4x7", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "bitsongvaloper10uv3t6yru5dryz2yy9em2pzmqezyhsp0gkkxd2", - "botAddress": "bitsong1js340eq8082essggjtv8lf9df7qlnny87ddafg", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000000 - }, - { - "address": "bitsongvaloper1mvpx2jnq7vnur3dehwesddg6as5eua0jy2a99s", - "botAddress": "bitsong1wdc6rmm2ng2jkeytt4ptas3vhx2t94yjvcg4g4", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "bitsongvaloper129y6g55wz7mz9vs2nudfvfdaun80fuphnltfpf", - "botAddress": "bitsong14td30sayes36tm042gcrwpynky927x2c434c06", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "bitsongvaloper1mceksy7l3xenuyx4dzl64erf3fh29a3r2ercnz", - "botAddress": "bitsong1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh6gfrgl", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "bitsongvaloper18wf0w252jxk3kgl5vlst8ttat8xzfnvejuftk2", - "botAddress": "bitsong16swn43y3wt70jex2l0dzcf987w8000psq7mpu8", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "bitsongvaloper1xwazl8ftks4gn00y5x3c47auquc62ssugxgm5z", - "botAddress": "bitsong1kxv5szlqgt8p7w95w76m7ahgjt58ultqkmpjja", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "bitsongvaloper1pqlxe507avzg2hq8ylcju037ywyh3myfsv9wln", - "botAddress": "bitsong1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zdfvlv5", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "bitsongvaloper1ech3swu5jeuenxzgd0rq9xz6yz3yn6t0v2x0tw", - "botAddress": "bitsong1m52073hvnza73uwrf45avgdwljyqx7p04qh3yw", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "bitsongvaloper1fk4up90cx352jwr6clug8pkg5jt28ha9whf6pu", - "botAddress": "bitsong14a78h6202u4xg6phjnd8rmr5fq98zzdlfczze0", - "runTime": "every 4 hour", - "minimumReward": 100000 - }, - { - "address": "bitsongvaloper1vtc6xlwpeuf9r5ee5hxq40hcllxcad9pnl99xn", - "botAddress": "bitsong1g4zywrluj6ac0xpu6f6jqul52rv0am66mklupq", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "bitsongvaloper1ltu429emv4tkn34n7795dxmeagl78ffqazwf9c", - "botAddress": "bitsong197yc3g8drkf8fyv6fr4ynddy2n22kauunjhehs", - "runTime": "22:00", - "minimumReward": 100000 - }, - { - "address": "bitsongvaloper1qxw4fjged2xve8ez7nu779tm8ejw92rv0vcuqr", - "botAddress": "bitsong1fx6eahccn5n9m6zwz0p4ynff0asnllvtyvxvd5", - "runTime": "22:00", - "minimumReward": 100000 - }, - { - "address": "bitsongvaloper1u6hq8svq4td2e80ucj3whmxh9uehf8nygh4hld", - "botAddress": "bitsong1e2xpkl7tjugpx4djn85ufjn3y0mvsfafd9tyrw", - "runTime": "00:30", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "persistence", - "operators": [ - { - "address": "persistencevaloper1rxjmg3l0myaz6fzzvkum82lzyqz5acyxqqc08l", - "botAddress": "persistence1qzvhepyemfctq3wmc50f2ps06ldx3fnpsf03zp", - "runTime": "20:00", - "minimumReward": 10000 - }, - { - "address": "persistencevaloper1pdse5rr5njkkka6qeu5m8u704h6z67w5r700vn", - "botAddress": "persistence1atgss2zpwl9m8fmm32gzrkjtqw3tdawguqw6tk", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "persistencevaloper1nchnrey36nrvzjslscu0c3l8j0r4z92hlsz3gk", - "botAddress": "persistence1v9vae554q55xa4pcfdm0p3kv8nxn50jk5qpprd", - "runTime": "23:00", - "minimumReward": 10000 - }, - { - "address": "persistencevaloper10sc98vt6saux8asexnsp2hgvkgmjmful8w5cuw", - "botAddress": "persistence1n8htrjks32y59u3fxwuy6fnz868n667k8ltezs", - "runTime": "every 1 hour", - "minimumReward": 10000 - }, - { - "address": "persistencevaloper1f9p23ru4sw8p2044237ckfhwdpklrn0ahdaujg", - "botAddress": "persistence1m5hgzum68rjf4c7zhezgkj8hlnmr0kghcdacy5", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "persistencevaloper1r7gdc8ag4ktmrvhed2xp09n3klrjuznwdzsru2", - "botAddress": "persistence1cuzmc99n343he6mvj3mzq7fnc4ncqlau3zter9", - "runTime": "21:00", - "minimumReward": 100000 - } - ], "authzSupport": true }, { "name": "agoric", "apyEnabled": false, - "operators": [ - { - "address": "agoricvaloper1kh8nwuee2y353u6nmv8jffpvhtzs7xu8f4klam", - "botAddress": "agoric148kutx4cag3js995cesc0rpuwpe85hq7zk5ry3", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "agoricvaloper1mxhgvj2c93xahahx9d9fc7rwwtufqza5cn6uhn", - "botAddress": "agoric1hl0jgxre5z0nkyjj07c5vfdy44yk44attzqnlt", - "runTime": "22:30", - "minimumReward": 100000 - }, - { - "address": "agoricvaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q4acufxj", - "botAddress": "agoric1cwe4fgnmvdzx6kefxs8alldtwhywctnxt7mdr5", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "agoricvaloper1xvz54pusznw8t76985kl3v2epduhyuscr4zxx3", - "botAddress": "agoric14jhnyzfa9sf3jq879y5rp5324klh4av3vwezmm", - "runTime": "every 30 minutes", - "minimumReward": 1000000 - }, - { - "address": "agoricvaloper1rx04k2fuczu28tvcmu4dzchhwalxvhjrr3k2k9", - "botAddress": "cosmos1448zqw506zylru2g85xdw4qge2q8d304qn2eq7", - "runTime": "21:00", - "minimumReward": 1000000 - } - ], "authzSupport": true }, { "name": "impacthub", "gasPrice": "0.025uixo", - "operators": [ - { - "address": "ixovaloper1m3kz9q3hp50udyq5tt4rawrexnay4y7jpyvtr9", - "botAddress": "ixo1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9qlc5jx", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "ixovaloper1a3qpp5ff9cgnusxwufdkmnvn83446lf9vq88ga", - "botAddress": "ixo1m5hgzum68rjf4c7zhezgkj8hlnmr0kghf59ewr", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "ixovaloper1ktjjvaurdupt7nvmfkgycn7mv0jcx5frz0dwzf", - "botAddress": "ixo1mjq48r6435aewerpruwc8up3tz3rzan2nvq99x", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "ixovaloper1n3mhyp9fvcmuu8l0q8qvjy07x0rql8q42frukn", - "botAddress": "ixo1jrjdqzxchlt09gj6qvn4ds8suxh9aqwygky328", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "ixovaloper1v275n8eyakuqu5pfpkzczf3s50e80a8ztrwfh5", - "botAddress": "ixo1k8mymzl8sapy2yqyyce5a35u09rnuae7tjq30n", - "runTime": "21:00", - "minimumReward": 100000 - } - ], "authzSupport": false }, { "name": "kichain", "gasPrice": "0.025uxki", - "operators": [ - { - "address": "kivaloper19seaxuh9wp3zum42w6flrjsr5raptxhy3l8qvw", - "botAddress": "ki1m7xlymnpxl2d4llu67v08e8xs67dj2fzkczyjg", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "kivaloper183ak9rcvfazp5rtljve6v06tyue3dfrglvpfux", - "botAddress": "ki15xa7vst3jca9qjkngmlysrdfur8gsex9rsj67h", - "runTime": [ - "09:00", - "21:00" - ], - "minimumReward": 1000 - }, - { - "address": "kivaloper1n5cpxhwm5rpgellky5njz67sx8sp0t6ejyxevq", - "botAddress": "ki1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9w8hfjp", - "runTime": "20:00", - "minimumReward": 1000000 - }, - { - "address": "kivaloper14932eu6d398f0ycqj6xtmy9xhaewzegkhz2eap", - "botAddress": "ki1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zsd0c20", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "kivaloper1lykm0h574w0r3jm545hhlfnd8937a70022xlwl", - "botAddress": "ki1v9vae554q55xa4pcfdm0p3kv8nxn50jktpkafa", - "runTime": "23:00", - "minimumReward": 100000 - }, - { - "address": "kivaloper14g4ztk36q529vx8ash7y8hr047kgzxlvyh2ch5", - "botAddress": "ki1mjq48r6435aewerpruwc8up3tz3rzan2a50c9p", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "kivaloper14932eu6d398f0ycqj6xtmy9xhaewzegkhz2eap", - "botAddress": "ki1jh0hzxtyu2a2nc5s9xdxx7zr50wvf97zsd0c20", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "kivaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pq8jud7", - "botAddress": "ki1xrrfek3qpz4ja8ccrjklfy064asyqq9kzl8qsm", - "runTime": "every 1 hour", - "minimumReward": 5000000 - } - ], "authzSupport": false }, { - "name": "sommelier", - "operators": [ - { - "address": "sommvaloper1cgdlryczzgrk7d4kkeawqg7t6ldz4x84yu305c", - "botAddress": "somm1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9nkf28l", - "runTime": "20:00", - "minimumReward": 1000000 - } - ] + "name": "sommelier" }, { "name": "konstellation", "image": "https://raw.githubusercontent.com/Konstellation/DARC_token/main/darctoken.svg", - "operators": [ - { - "address": "darcvaloper1txzc42vl5qel54tyuck8z8pfg979hwk867z30z", - "botAddress": "darc1m7xlymnpxl2d4llu67v08e8xs67dj2fzc586mt", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "darcvaloper14kuh954mw4xp7gz5kvr3vtul0lpz5xt3tecpep", - "botAddress": "darc194ytn50yhh67rdha8akhs7c6zulnz4n2kkajp5", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "darcvaloper1cmtxucuhf0hysjrx02p9fvzjr5kkcgcj9nxwhc", - "botAddress": "darc1v9vae554q55xa4pcfdm0p3kv8nxn50jk9dnrq7", - "runTime": "23:00", - "minimumReward": 100000 - }, - { - "address": "darcvaloper1yww5jnu4y39vyea47xs4z7r45yz9rl5dpxg67v", - "botAddress": "darc1m8d834ykcy3dgrdve4xhxky3u3c2tl9dde8hgs", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "darcvaloper1pk8e47hdlw9mtnphz3klhkeed8wlq8gwjw7m0w", - "botAddress": "darc1mjq48r6435aewerpruwc8up3tz3rzan2nc2xvz", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "darcvaloper17h628d2wtlw6844nzv0ktnq9qtm5qm3p6uxnx5", - "botAddress": "darc1uc877qk45e23hxxx364dxsp8rehu5addv6sdr0", - "runTime": "every 15 minutes", - "minimumReward": 100000 - }, - { - "address": "darcvaloper1jt9w26mpxxjsk63mvd4m2ynj0af09csldy6sgw", - "botAddress": "darc1xrrfek3qpz4ja8ccrjklfy064asyqq9kvnz7ec", - "runTime": "every 1 hour", - "minimumReward": 10000000 - }, - { - "address": "darcvaloper17w25mz7gvf8f84fevy3akzhaugjewp4dd8f5rj", - "botAddress": "darc1vkayq65xn0tguetzm6r2ejal7zmlwgemf8ncwq", - "runTime": "every 3 hours", - "minimumReward": 1000000 - } - ], "authzSupport": false }, { "name": "fetchhub", - "operators": [ - { - "address": "fetchvaloper1a7l5xar7vyymvahfelar6vtkdar5x9a54l7zvc", - "botAddress": "fetch1v9vae554q55xa4pcfdm0p3kv8nxn50jkf3wk07", - "runTime": "23:00", - "minimumReward": 10000000000000000 - }, - { - "address": "fetchvaloper13uwrjldm8m45cuarjdvgk9qwnysf835rgrugc6", - "botAddress": "fetch1m5hgzum68rjf4c7zhezgkj8hlnmr0kgh9uj0g8", - "runTime": "every 1 hour", - "minimumReward": 10000000000000000 - }, - { - "address": "fetchvaloper1mr8tqsr3cjp4vh30j8h32uluh5nhnjnh8lkef0", - "botAddress": "fetch1mjq48r6435aewerpruwc8up3tz3rzan2lyhnrz", - "runTime": "21:00", - "minimumReward": 10000000000000000 - }, - { - "address": "fetchvaloper132elwf5n6pjhxjh464khn8m2rzzr7wv4rlkwev", - "botAddress": "fetch14a78h6202u4xg6phjnd8rmr5fq98zzdlkveweh", - "runTime": "every 4 hour", - "minimumReward": 10000000000000000 - }, - { - "address": "fetchvaloper1874jxj35dlywfq65cupdnsp02lkqcjqnpgln2w", - "botAddress": "fetch1k8mymzl8sapy2yqyyce5a35u09rnuae786h8fh", - "runTime": "18:00", - "minimumReward": 10000000000000000 - } - ], "authzSupport": false }, { @@ -2811,436 +159,21 @@ "batchTxs": 100 }, "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", - "operators": [ - { - "address": "cerberusvaloper1xjgspyv73d3k3ewygu0v2gcwwplwxkxg03reqy", - "botAddress": "cerberus1m7xlymnpxl2d4llu67v08e8xs67dj2fzmjnzrh", - "runTime": "21:00", - "minimumReward": 10000 - }, - { - "address": "cerberusvaloper1xkv494sduqkpadwesqlsp6069yepsj58axja5l", - "botAddress": "cerberus1js340eq8082essggjtv8lf9df7qlnny8wrlu7v", - "runTime": "every 5 minutes", - "minimumReward": 5000000 - }, - { - "address": "cerberusvaloper1ga0xxzst2hjyjtfqnffz2mapn8vk8ufysqsn30", - "botAddress": "cerberus15xa7vst3jca9qjkngmlysrdfur8gsex9w6ru0g", - "runTime": "every 5 minutes", - "minimumReward": 10000 - }, - { - "address": "cerberusvaloper18twaqg4vaextnm5dddgmlm5quwymaswkchqt3a", - "botAddress": "cerberus1uxwnhyjd2m5y6mpqsdg95y4ty0a5jtfrjhkgq5", - "runTime": "every 4 minutes", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper1ej2es5fjztqjcd4pwa0zyvaevtjd2y5w2yl7tt", - "botAddress": "cerberus1ftaxf307mumu6gwg05q360xeryspcgsq6fvnyc", - "runTime": "every 5 minutes", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper1u7m9kwj8tvwjaxympp3r8xq7d0pf5la994x7u8", - "botAddress": "cerberus1r8egcurpwxftegr07gjv9gwffw4fk009hq0rdg", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", - "botAddress": "cerberus1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczd38dxa", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1uvl2g9nd8qttjjyxjs30x7fj878d3wt4lt4juq", - "botAddress": "cerberus1e7tcnlaen39yafqxcj5zhnd0m8r8xlzyq92vrv", - "runTime": "every 15 minutes", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper18g9868awp4dtxn5n5cjd7e30y8djevc80hqyad", - "botAddress": "cerberus1h43hjfxlz8t6y5wewma63jz7melv46gjcaue9a", - "runTime": "every 3 minutes", - "minimumReward": 10000 - }, - { - "address": "cerberusvaloper1g3d36rfxfqtlnz3hzd05cs2wrjgykcz2g649a8", - "botAddress": "cerberus1e44rluarkdw56dy2turnwjtvtg4wqvs0wv4dzw", - "runTime": "every 3 minutes", - "minimumReward": 10000 - }, - { - "address": "cerberusvaloper1td92z5qwl39f407gxf6lu2x80enat7t3jfefnf", - "botAddress": "cerberus1zwjfxr3j9gnnrhxwtt8t6qqr4pdn7cgjuux537", - "runTime": "every 1 hour", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1r34mlqewsxrde38vp3tzwh3rk5vt6ez0slzuvu", - "botAddress": "cerberus16r56awgxe7mkrwpsnurjexfdg0n3f4f3dgf090", - "runTime": "every 15 minutes", - "minimumReward": 10000 - }, - { - "address": "cerberusvaloper1lxz6ucd5qydhpwtpatn9wu83fc002v08tek6zh", - "botAddress": "cerberus1305e92ng6a9pdh3ttc3ld459ckxupxt3tt620v", - "runTime": "every 20 minutes", - "minimumReward": 10000 - }, - { - "address": "cerberusvaloper1y3thykrje2fmdcf8wva8l8kphmwpx89ur4erq3", - "botAddress": "cerberus1v9vae554q55xa4pcfdm0p3kv8nxn50jkxt8mcz", - "runTime": "every 15 minutes", - "minimumReward": 10000 - }, - { - "address": "cerberusvaloper1evv5y2ake002n9l27t5qhqcwhgwd6up2m2yhcy", - "botAddress": "cerberus1m8d834ykcy3dgrdve4xhxky3u3c2tl9dwln0sv", - "runTime": "every 1 hour", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1s3k0rhxxt8jl0tgyn2sule8rkzkx58x04a900g", - "botAddress": "cerberus1x9e5qkjzzmfhw4dt05aludazwvd5x6340zl42n", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper16cfkt9n0pcftg0ky3rk9aegqdp383uwge38rdl", - "botAddress": "cerberus1mjq48r6435aewerpruwc8up3tz3rzan2s77757", - "runTime": "21:00", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper10ypajp3q5zu5yxfud3ayd95th0k7467k3s5vh7", - "botAddress": "cerberus13yfd74cezsrjcmhvmh6wkfwfuj7fds5eenhn64", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1c4pewt03f0fnwrf4vcgawzl6pllm3l9kfffu7l", - "botAddress": "cerberus1jsmtq9z9qpeda0pyff9w8u66mkxrtzser8cjew", - "runTime": "every 15 minutes", - "minimumReward": 100000 - }, - { - "address": "cerberusvaloper1cy9jhuf3cx2z33duxld7p57gtege6hfhrar9g9", - "botAddress": "cerberus1hpwxnnrtsc6zjx4glup77tfe5z54lmtqe3tusv", - "runTime": "every 17 minutes", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper1zl4vt84hya03e8hu7dx4q4cvn2ts2xdrrnnufr", - "botAddress": "cerberus1weeu5yuj8n23hd87wsdxqqgfmzzz9zt6nlw6xf", - "runTime": "every 15 minutes", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper1xwazl8ftks4gn00y5x3c47auquc62ssuvav5f9", - "botAddress": "cerberus195mm9y35sekrjk73anw2az9lv9xs5mztx300fu", - "runTime": "every 15 minutes", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper1dh25k2zuf0tgz8dsp3v0utw0xch2ljxu3lnsct", - "botAddress": "cerberus12yeq3h92ew3e00tt2kma5q0t7tmangvy9ydsxn", - "runTime": "every 15 minutes", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper1q9d3algfdhuyggwtr3svgdmu22yke9yuy6nye3", - "botAddress": "cerberus1zf8gz27gs5py3ua9lpu958w24j0jyj0t7jzmra", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1l6kfy4xvy0a34fseyhvc6f6k8asukfdz8462mg", - "botAddress": "cerberus17xyeczwsax3st6aykyu4f28s5n3zg6r78znm7g", - "runTime": "every 5 minutes", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper13uz8x0rgm950639yklqq5tnjh6gwfpfhu67yyk", - "botAddress": "cerberus106n92ygqknaw5wcgl2e9v0duxxux6ntt2cf87q", - "runTime": "every 1 hour", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1j80fpcsumfkxypvydvtwtz3j4sdwr8c2x9fw5y", - "botAddress": "cerberus1zr3hn5faf8tjdc2h576qt02g8t5h9pevz478ge", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper1g2k6a7akrmm9jwa0n8eg2vfe70j2u5l4myw8lt", - "botAddress": "cerberus17xs4md6flec0klct07874ntqppg78ane2dm4f3", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1krkmg6f0sjwalkx3nq39yt0upxgys7alcjytq4", - "botAddress": "cerberus1h666jcyjycu90w3j6q6cpswsmzh9f73txjnkf9", - "runTime": "every 2 minutes", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper1gk0q0vfxcz04sqchtjxyc038n8x06pd6s5yshf", - "botAddress": "cerberus12s3ys786fatss7ngyrq7cqlcth7au4n3fww5a8", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1gh20fpn3jg6yv5csljpy2zwellksyuxcxztcy9", - "botAddress": "cerberus1f5exrqqv9q2cy3sannyu42u94ght7zcwmgdggr", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper140l6y2gp3gxvay6qtn70re7z2s0gn57z90e2f5", - "botAddress": "cerberus12xpq68caw2aqdu70jm4k792g0v9prhrpuyxd4n", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1ge32slufqfqy206mlvcszcgtg570thgke0rdhs", - "botAddress": "cerberus1h5kwkrrny99qjejnymgvzxpyktudnqa76uhe8e", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper198ljzq359ygqauxargj7je28sxlnj0tj2h7m73", - "botAddress": "cerberus1sfh2p794yrfte06h2ue3qg30ekdrf84e5za2s5", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1krc59rqye9ptzurkyuccu6ykse895m9culxzwy", - "botAddress": "cerberus1d72r4aaxkv2cpnxjh6xuklv9z4prcj2qnjmp3e", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1d0pk9sx7gfthuwmcepvuq9wcreqdr5cxmaj6et", - "botAddress": "cerberus1usypwpuwuvwmks4ymqz5s4csya00xttuqf4fcr", - "runTime": "every 1 hour", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1m5dzr80ag23sqmw2cnzelj73xmsd62qtnswujc", - "botAddress": "cerberus1q200ut9chx7t0nnde4h526vdxwudktgkrz9muf", - "runTime": "every 15 minutes", - "minimumReward": 100000 - }, - { - "address": "cerberusvaloper19f0w9svr905fhefusyx4z8sf83j6et0gfa2l5e", - "botAddress": "cerberus1etsdevgm5ps240lrw9mg3p2j32tj96ktjc9g8x", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1j5f0gy8qa79v2eddmh32ef0z827345w04utym0", - "botAddress": "cerberus1vqhz5g5sq8ku0ka5x9ygal9flkg5vzr9rdx0r7", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1j5dmnpyk4hghkph33c5fz9nqw2tkj43u2lmw0w", - "botAddress": "cerberus1fat08u2m6a4nqqqh55agp82gx4r6hzwwh0705f", - "runTime": "every 10 minutes", - "minimumReward": 25000000 - }, - { - "address": "cerberusvaloper13pdq3ee636ak6tytg5htyf0jcp20vfuxekeaeq", - "botAddress": "cerberus1r3g7japkptsvt3gv52gvuudprtcue762m5ssan", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1gp957czryfgyvxwn3tfnyy2f0t9g2p4pvm8q9p", - "botAddress": "cerberus1xrrfek3qpz4ja8ccrjklfy064asyqq9k04kxpy", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper13r2avh5y99w2duw4wpsh3vtx77j0s9h5zp2q3q", - "botAddress": "cerberus1cvj2yu8fmtr2my4a7y4r5rnt69flqvcvk5jjsd", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1kdqjzvznnufaykuwpu2glu9g33jshw8w2zxpsr", - "botAddress": "cerberus1w3pe97xn72djdhdvmk7wm3ylzht7z3kcdk7j0s", - "runTime": "every 15 minutes", - "minimumReward": 50000000 - }, - { - "address": "cerberusvaloper1wmanxw8y2s4cauegx5lagq5gxgqzpuuccwascc", - "botAddress": "cerberus1uc877qk45e23hxxx364dxsp8rehu5add0uy4mn", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper1n507lrafyk934tuyu903pxyrqedzencv2kvqu7", - "botAddress": "cerberus15enapnszjg5en949x326ae3deu35dmw539f9jm", - "runTime": "every 30 minutes", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper1pzqjgfd25qsyfdtmx9elrqx6zjjvnc9stqd7h0", - "botAddress": "cerberus1jrjdqzxchlt09gj6qvn4ds8suxh9aqwyty62ml", - "runTime": "every 1 hour", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper1jkf678dyg8k6ewkfu6xwvtl2sz68gfh3ys4s2h", - "botAddress": "cerberus1k4hh4mjvtrwq79vhmlrltdsyq08kec7j8ptm3s", - "runTime": "every 30 minutes", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper1tksh2f6fgwdpn2p73e8wvc2zkl9ey6e95vgvwd", - "botAddress": "cerberus1v4xvyv2yar2ytrj7d78dk9e253s9g7n07qyew3", - "runTime": "every 15 minutes", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper14vq30fa9mkw00vhnvmtlqxwwnxucl7may9ran2", - "botAddress": "cerberus197yc3g8drkf8fyv6fr4ynddy2n22kauuru9cq5", - "runTime": "22:00", - "minimumReward": 100000 - }, - { - "address": "cerberusvaloper1tnmjjj5ugcunyy75q3c3pn62qpwnf50pnf2xm0", - "botAddress": "cerberus1330aewfwzu30vznhxw7aevaz0kn35fdyzc7y3z", - "runTime": "every 30 minutes", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper1gypqhk3hszgcg08h9kj5fja8xzl63txmt3m6k6", - "botAddress": "cerberus1wve3tc8sj42yl3trw3lt0rcyp5medvya027yan", - "runTime": "22:00", - "minimumReward": 10000000 - }, - { - "address": "cerberusvaloper15ms93f3m2h8rmzfkngxh06mklujrxprpl84pyr", - "botAddress": "cerberus1ajvn5l50lvuy5ej5sqhwthve26au0qs56gfa0r", - "runTime": "every 15 minutes", - "minimumReward": 100000000 - }, - { - "address": "cerberusvaloper18zf2m4np64ncjr2hg0plk8tjqma25farw2nac5", - "botAddress": "cerberus1kxd2f28ugzwyv9twexw88kpajvgf94xgpl9hmq", - "runTime": "every 1 hour", - "minimumReward": 1000000 - }, - { - "address": "cerberusvaloper1gl6mw8p76erqeltgt6kkqac57m6h5ufy28kas6", - "botAddress": "cerberus18m7pnt8enhrlqf4rh7ff9uvcfsr6tmlm2d8f5m", - "runTime": "every 1 hour", - "minimumReward": 10000000 - } - ], "authzSupport": true }, { "name": "secretnetwork", "gasPrice": "0.025uscrt", - "operators": [ - { - "address": "secretvaloper1jjlfeshltdy6ngnf5tg6aeaygsvf0kxg8wck04", - "botAddress": "secret185yhf2a4a94en9t8qzwp8qkwx3dfx48m4k3lk7", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "secretvaloper16k26akna7h295rfjx3278s7xusnt736vy437y8", - "botAddress": "secret1e44rluarkdw56dy2turnwjtvtg4wqvs0swpd2e", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "secretvaloper1vzkdmu0sa8gaj686jh5all7hpmmsp8x87vyz8z", - "botAddress": "secret1hl0jgxre5z0nkyjj07c5vfdy44yk44atm6k9jp", - "runTime": "22:30", - "minimumReward": 100000 - }, - { - "address": "secretvaloper182dg8p7nshdjjt5sypcx6hw9p8vlwqpntpm5k6", - "botAddress": "secret14gra89zm8753pfuhvujjulmtkf2qq70fmpz3qg", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "secretvaloper1t5wtcuwjkdct9qkw2h6m48zu2hectpd6ulmekk", - "botAddress": "secret18zzklau04kf3qtwrnnzlf20xrxpx3a89dqu42m", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "secretvaloper12y30xefd0wg53xtyv9lw7mjcdvf8nxgzzva3sr", - "botAddress": "secret194ytn50yhh67rdha8akhs7c6zulnz4n2tja23l", - "runTime": "every 1 hour", - "minimumReward": 100000 - }, - { - "address": "secretvaloper16w5hlcf389le2n60t32eqf43plp539ged9sruy", - "botAddress": "secret1mjq48r6435aewerpruwc8up3tz3rzan2wu27uf", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "secretvaloper14mwwdad00y7lwwmmk3yw2l2qhn6jzjpy28fmfw", - "botAddress": "secret13x5lkafjz2x7ue468cqc65ea6uajq7uctnadgl", - "runTime": "15:00", - "minimumReward": 100000 - }, - { - "address": "secretvaloper1ahawe276d250zpxt0xgpfg63ymmu63a0svuvgw", - "botAddress": "secret1cpzlkfep9z8wjthvk5csxfkmjhmqncsl963pqg", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "secretvaloper17m3c795fz4f36zjgjhr3vkf7e9pn3xxuryeww9", - "botAddress": "secret14hqn2mr82kyacele3nmaw8hmfczfpy37kjpqun", - "runTime": "21:00", - "minimumReward": 100000 - }, - { - "address": "secretvaloper1q0rth4fu4svxnw63vjd7w74nadzsdp0fmkhj3d", - "botAddress": "secret1zpmzxljkuzjqg20dvfwf9lnuk3pcxme5rkup7h", - "runTime": "21:00", - "minimumReward": 100000 - } - ], "authzSupport": true }, { "name": "bostrom", "gasPrice": "0boot", - "operators": [ - { - "address": "bostromvaloper1ydc5fy9fjdygvgw36u49yj39fr67pd9m5qexm8", - "botAddress": "bostrom1grfk7t0k42yxpwqhr7kt4te7j73m2a3vpkfrlp", - "runTime": "19:37", - "minimumReward": 100000 - }, - { - "address": "bostromvaloper1r28nhrkwcvqs4qss2l7zqdvc2qfdl8arglv6nn", - "botAddress": "bostrom14a78h6202u4xg6phjnd8rmr5fq98zzdlxzye98", - "runTime": "every 4 hour", - "minimumReward": 100000 - } - ], "authzSupport": true }, { "name": "starname", "gasPrice": "10uvoi", - "operators": [], "authzSupport": false } ] \ No newline at end of file diff --git a/src/utils/CosmosDirectory.mjs b/src/utils/CosmosDirectory.mjs index addfce74e..db4d10802 100644 --- a/src/utils/CosmosDirectory.mjs +++ b/src/utils/CosmosDirectory.mjs @@ -6,6 +6,15 @@ function CosmosDirectory(){ const rpcBase = `${directoryProtocol}://rpc.${directoryDomain}` const restBase = `${directoryProtocol}://rest.${directoryDomain}` const chainsUrl = `${directoryProtocol}://chains.${directoryDomain}` + const validatorsUrl = `${directoryProtocol}://validators.${directoryDomain}` + + function rpcUrl(name){ + return rpcBase + '/' + name + } + + function restUrl(name){ + return restBase + '/' + name + } function getChains(){ return axios.get(chainsUrl) @@ -23,12 +32,22 @@ function CosmosDirectory(){ .then(res => res.data) } - function rpcUrl(name){ - return rpcBase + '/' + name + function getOperators(chainName){ + return axios.get(validatorsUrl + '/chains/' + chainName) + .then(res => res.data) + .then(data => data.validators.filter(el => el.restake )) } - function restUrl(name){ - return restBase + '/' + name + function getOperatorCounts(){ + return axios.get(validatorsUrl) + .then(res => res.data) + .then(data => data.reduce((sum, validator) => { + validator.chains.forEach(chain => { + sum[chain.name] = sum[chain.name] || 0 + if(!!chain.restake) sum[chain.name]++ + }) + return sum + }, {})) } return { @@ -37,7 +56,9 @@ function CosmosDirectory(){ chainsUrl, getChains, getChainData, - getTokenData + getTokenData, + getOperators, + getOperatorCounts } } diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index b8982093e..d46bc1b65 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -10,6 +10,14 @@ const Network = async (data, withoutQueryClient) => { const chain = await Chain(data) const directory = CosmosDirectory() + const operators = (await directory.getOperators(data.name)).map(el => { + return { + address: el.address, + botAddress: el.restake.address, + runTime: el.restake.run_time, + minimumReward: el.restake.minimum_reward + } + }) const rpcUrl = data.rpcUrl || directory.rpcUrl(data.name) const restUrl = data.restUrl || directory.restUrl(data.name) @@ -44,7 +52,7 @@ const Network = async (data, withoutQueryClient) => { } const sortOperators = () => { - const random = _.shuffle(data.operators) + const random = _.shuffle(operators) if(data.ownerAddress){ return _.sortBy(random, ({address}) => address === data.ownerAddress ? 0 : 1) } @@ -75,7 +83,7 @@ const Network = async (data, withoutQueryClient) => { testAddress: data.testAddress, restUrl: queryClient && queryClient.restUrl, rpcUrl: queryClient && queryClient.rpcUrl, - operators: data.operators, + operators: operators, authzSupport: chain.authzSupport, data, chain, From cf10bf4cbc586f06e7837b6d4ec7ff10d7727e45 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 29 Mar 2022 22:22:40 +0100 Subject: [PATCH 315/862] Update README --- README.md | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 5a011e1ec..d7533de99 100644 --- a/README.md +++ b/README.md @@ -237,36 +237,42 @@ Any values you specify will override the `networks.json` file. These are example Arrays will be replaced and not merged. The file is `.gitignore`'d so it won't affect upstream updates. -Note that REStake requires a node with indexing enabled and minimum gas prices matching the networks.json gas price (or your local override). +Note that REStake requires a node with indexing enabled and minimum gas prices matching the `networks.json` gas price (or your local override). ## Submiting your operator ### Setup your REStake operator -You now need to update the [networks.json](./src/networks.json) file at `./src/networks.json` to add your operator to any networks you want to auto-compound for. Check the existing file for examples, but the operators array is simple: +You now need to update the [Validator Registry](https://github.com/eco-stake/validator-registry) to add your operator information to any networks you want to auto-compound for. Check the README and existing validators for examples, but the config for a network looks like this: ```json -"operators": [{ - "address": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", - "botAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", - "runTime": ["09:00", "21:00"], - "minimumReward": 1000 +{ + "name": "akash", + "address": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", + "restake": { + "address": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", + "run_time": [ + "09:00", + "21:00" + ], + "minimum_reward": 1000 + } }, ``` -`address` is your validator's address, and `botAddress` is the address from your new hot wallet you generated earlier. +`address` is your validator's address, and `restake.address` is the address from your new hot wallet you generated earlier. -`runTime` is the time *in UTC* that you intend to run your bot, and there are a few options. Pass a single time, e.g. `09:00` to specify a single run at 9am UTC. Use an array for multiple specified times, e.g. `["09:00", "21:00"]`. Use an interval string for multiple times per hour/day, e.g. `"every 15 minutes"`. +`restake.run_time` is the time *in UTC* that you intend to run your bot, and there are a few options. Pass a single time, e.g. `09:00` to specify a single run at 9am UTC. Use an array for multiple specified times, e.g. `["09:00", "21:00"]`. Use an interval string for multiple times per hour/day, e.g. `"every 15 minutes"`. -`minimumReward` is the minimum reward to trigger autostaking, otherwise the address be skipped. This could be set higher for more frequent restaking. Note this is in the base denomination, e.g. `uosmo`. +`restake.minimum_reward` is the minimum reward to trigger autostaking, otherwise the address be skipped. This could be set higher for more frequent restaking. Note this is in the base denomination, e.g. `uosmo`. Repeat this config for all networks you want to REStake for. -Note that the `botAddress` is the address which will be granted by the delegator in the UI to carry out their restaking transactions. +Note that the `restake.address` is the address which will be granted by the delegator in the UI to carry out their restaking transactions. #### Submit your operator -You can now submit your `networks.json` update to the repository in a pull request which will be merged and deployed as soon as possible. +You can now submit your [Validator Registry](https://github.com/eco-stake/validator-registry) update to that repository in a pull request which will be merged as soon as possible. REStake automatically updates within 15 minutes of changes being merged. ## Adding/updating a network @@ -277,17 +283,7 @@ To add a network to REStake, add the required information to `networks.json` as ```json { "name": "osmosis", - "restUrl": [ - "https://rest.cosmos.directory/osmosis", - ], - "rpcUrl": [ - "https://rpc.cosmos.directory/osmosis", - ], "gasPrice": "0.025uosmo", - "testAddress": "osmo1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczed559y", - "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", - "operators": [ - ], "authzSupport": true } ``` @@ -308,9 +304,9 @@ Alternative run from source using `docker-compose up` or `npm start`. The REStake UI is both validator and network agnostic. Any validator can be added as an operator and run this tool to provide an auto-compounding service to their delegators, but they can also run their own UI if they choose and adjust the branding to suit themselves. -For this to work, we need a common source of chain information, and a common source of 'operator' information. Chain information is sourced from the [Chain Registry](https://github.com/cosmos/chain-registry), via an API provided by [cosmos.directory](https://registry.cosmos.directory). Operator information currently lives in the [networks.json](./src/networks.json) file in this repository. +For this to work, we need a common source of chain information, and a common source of 'operator' information. Chain information is sourced from the [Chain Registry](https://github.com/cosmos/chain-registry), via an API provided by [cosmos.directory](https://github.com/eco-stake/cosmos-directory). Operator information lives in the [Validator Registry](https://github.com/eco-stake/validator-registry). -If you fork this repository to provide your own UI, please keep up to date with the upstream to ensure you have the latest [networks.json](./src/networks.json) to include all operators. Some honesty is needed until we have a more decentralised solution. +Now we have a common source of operator information, applications can integrate with REStake validators easier using the data directly from GitHub, or via the [cosmos.directory](https://github.com/eco-stake/cosmos-directory) project. ## Disclaimer From 165f5b2cdbf33c7a9bccc672661208d3f20e4277 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 29 Mar 2022 22:31:31 +0100 Subject: [PATCH 316/862] Allow override of operators from networks.json For forked UIs etc --- src/components/NetworkSelect.js | 2 +- src/utils/Network.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index b3293bf10..c8c5f6c42 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -61,7 +61,7 @@ function NetworkSelect(props) { value: el.name, label: el.pretty_name, image: el.image, - operatorCount: operatorCounts[el.name], + operatorCount: el.operators?.length || operatorCounts[el.name], authz: el.authzSupport } }), diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index d46bc1b65..44c47f176 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -10,7 +10,7 @@ const Network = async (data, withoutQueryClient) => { const chain = await Chain(data) const directory = CosmosDirectory() - const operators = (await directory.getOperators(data.name)).map(el => { + const operators = data.operators || (await directory.getOperators(data.name)).map(el => { return { address: el.address, botAddress: el.restake.address, From 72b0e2b9c00bfe0ffd2d66df82438007a0396c12 Mon Sep 17 00:00:00 2001 From: Galadrin Date: Wed, 30 Mar 2022 01:04:10 +0200 Subject: [PATCH 317/862] add Rizon-Network --- src/networks.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 837f7e853..ca2fe8b8c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -175,5 +175,10 @@ "name": "starname", "gasPrice": "10uvoi", "authzSupport": false + }, + { + "name": "rizon", + "gasPrice": "0.0001uatolo", + "authzSupport": true } -] \ No newline at end of file +] From b1a9ab10721d2c516120cd7e7069e1e247dcb98a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 30 Mar 2022 09:11:10 +0100 Subject: [PATCH 318/862] Add default for cosmos.directory --- src/utils/CosmosDirectory.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/CosmosDirectory.mjs b/src/utils/CosmosDirectory.mjs index db4d10802..1d6b97265 100644 --- a/src/utils/CosmosDirectory.mjs +++ b/src/utils/CosmosDirectory.mjs @@ -1,8 +1,8 @@ import axios from 'axios' function CosmosDirectory(){ - const directoryProtocol = process.env.DIRECTORY_PROTOCOL - const directoryDomain = process.env.DIRECTORY_DOMAIN + const directoryProtocol = process.env.DIRECTORY_PROTOCOL || 'https' + const directoryDomain = process.env.DIRECTORY_DOMAIN || 'cosmos.directory' const rpcBase = `${directoryProtocol}://rpc.${directoryDomain}` const restBase = `${directoryProtocol}://rest.${directoryDomain}` const chainsUrl = `${directoryProtocol}://chains.${directoryDomain}` From 1f5fdf10815acd18a38cdeeae2ca34cdf09790e2 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 30 Mar 2022 10:07:08 +0100 Subject: [PATCH 319/862] Prepare for cosmos.directory API change --- src/utils/CosmosDirectory.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/CosmosDirectory.mjs b/src/utils/CosmosDirectory.mjs index 1d6b97265..e49f9e0f8 100644 --- a/src/utils/CosmosDirectory.mjs +++ b/src/utils/CosmosDirectory.mjs @@ -19,6 +19,7 @@ function CosmosDirectory(){ function getChains(){ return axios.get(chainsUrl) .then(res => res.data) + .then(data => Array.isArray(data) ? data : data.chains) // deprecate .then(data => data.reduce((a, v) => ({ ...a, [v.path]: v }), {})) } @@ -41,6 +42,7 @@ function CosmosDirectory(){ function getOperatorCounts(){ return axios.get(validatorsUrl) .then(res => res.data) + .then(data => Array.isArray(data) ? data : data.validators) // deprecate .then(data => data.reduce((sum, validator) => { validator.chains.forEach(chain => { sum[chain.name] = sum[chain.name] || 0 From abf5faa555d851dae43596d69032d6b74dccbf12 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 30 Mar 2022 19:47:13 +0100 Subject: [PATCH 320/862] Random delay autostaking if using cosmos.directory --- scripts/base.mjs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index e9c5e9140..6ae153c6c 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -38,10 +38,18 @@ export class Autostake { if(!client) return timeStamp('Skipping') - timeStamp('Using REST URL', client.network.restUrl) - timeStamp('Using RPC URL', client.network.rpcUrl) + const { restUrl, rpcUrl } = client.network - if(!data.overriden) timeStamp('You are using public nodes, script may fail with many delegations. Check the README to use your own') + timeStamp('Using REST URL', restUrl) + timeStamp('Using RPC URL', rpcUrl) + + const usingDirectory = !![restUrl, rpcUrl].find(el => el.match("cosmos.directory")) + + if(usingDirectory){ + timeStamp('You are using public nodes, script may fail with many delegations. Check the README to use your own') + timeStamp('Delaying briefly to reduce load...') + await new Promise(r => setTimeout(r, (Math.random() * 31) * 1000)); + } try { await this.runNetwork(client) From 2351c0fb29af405d8de31fe5a02a383c0a3aa6a6 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 30 Mar 2022 23:07:51 +0100 Subject: [PATCH 321/862] Remove live grant test and move to script --- .gitignore | 1 + package.json | 3 +- scripts/checkAuthz.mjs | 40 +++++++++++++++++++++++++++ src/components/Delegations.js | 52 +++++++++++------------------------ 4 files changed, 59 insertions(+), 37 deletions(-) create mode 100644 scripts/checkAuthz.mjs diff --git a/.gitignore b/.gitignore index cd7b2112f..644c9b9b9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ src/networks.local.json scripts/* !scripts/autostake.mjs !scripts/base.mjs +!scripts/checkAuthz.mjs # dependencies /node_modules diff --git a/package.json b/package.json index 1d847f194..8ddc9cfcc 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "start": "npx parcel src/index.html", "prebuild": "rm -rf dist/", "build": "npx parcel build src/index.html", - "autostake": "node scripts/autostake.mjs" + "autostake": "node scripts/autostake.mjs", + "checkAuthz": "node scripts/checkAuthz.mjs" }, "eslintConfig": { "extends": [ diff --git a/scripts/checkAuthz.mjs b/scripts/checkAuthz.mjs new file mode 100644 index 000000000..98c748370 --- /dev/null +++ b/scripts/checkAuthz.mjs @@ -0,0 +1,40 @@ +import fs from 'fs' +import _ from 'lodash' +import Network from '../src/utils/Network.mjs' +import { executeSync } from '../src/utils/Helpers.mjs' + +async function checkAuthz(networkName) { + const networksData = fs.readFileSync('src/networks.json'); + const networks = JSON.parse(networksData); + const calls = networks.map(data => { + return async () => { + if(networkName && data.name !== networkName) return + try { + const network = await Network(data) + const support = await testAuthz(network) + if (data.authzSupport !== support){ + console.log(network.name, 'support is different', support ? 'ENABLED' : 'DISABLED') + } + } catch (error) { + console.log(data.name, error.message) + } + } + }) + await executeSync(calls, 5) +} +async function testAuthz(network) { + return network.queryClient + .getGrants() + .then( + (result) => { }, + (error) => { + if (error.response && error.response.status === 501) { + return false + } + return true + } + ); +} + +const networkName = process.argv[2] +checkAuthz(networkName) \ No newline at end of file diff --git a/src/components/Delegations.js b/src/components/Delegations.js index c2743cff5..36ee5c693 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -32,8 +32,8 @@ class Delegations extends React.Component { async componentDidMount() { const isNanoLedger = this.props.stargateClient.getIsNanoLedger(); this.setState({ isNanoLedger: isNanoLedger }); + this.getGrants() this.refresh(); - this.testAndGetGrants() } async componentDidUpdate(prevProps) { @@ -48,7 +48,6 @@ class Delegations extends React.Component { const isNanoLedger = this.props.stargateClient.getIsNanoLedger(); this.setState({ isNanoLedger: isNanoLedger, - authzMissing: false, error: null, validatorApy: {}, operatorGrants: {} @@ -56,10 +55,9 @@ class Delegations extends React.Component { return this.refresh(); } - if (!this.props.delegations) return; - const matchingDelegations = _.difference(Object.keys(this.props.delegations), prevProps.delegations ? Object.keys(prevProps.delegations) : []).length === 0 - if (!matchingDelegations){ - this.testAndGetGrants() + const delegationsChanged = _.difference(Object.keys(this.props.delegations || {}), Object.keys(prevProps.delegations || {})).length > 0 + if (delegationsChanged) { + this.getGrants() } } @@ -118,17 +116,12 @@ class Delegations extends React.Component { }) } - async testAndGetGrants() { - await this.getTestGrant(); - if (!this.state.authzMissing && this.props.operators.length) { - this.getGrants(); - } - } - async getGrants() { - if(!this.props.delegations) return; + if(!this.authzSupport() || !this.props.operators.length) return const ordered = [...this.props.operators].sort(el => { + if(!this.props.delegations) return 0 + return this.props.delegations[el.address] ? -1 : 0 }) const calls = ordered.map((operator) => { @@ -161,11 +154,7 @@ class Delegations extends React.Component { })); }, (error) => { - if (error.response && error.response.status === 501) { - this.setState({ authzMissing: true }); - } else { - this.setState({ error: "Failed to get grants. Please refresh" }); - } + this.setState({ error: "Failed to get grants. Please refresh" }); } ); } @@ -178,19 +167,6 @@ class Delegations extends React.Component { } } - getTestGrant() { - return this.props.queryClient - .getGrants() - .then( - (result) => {}, - (error) => { - if (error.response && error.response.status === 501) { - this.setState({ authzMissing: true }); - } - } - ); - } - getValidatorImage(network, validatorAddress, expireCache){ const images = this.state.validatorImages[network.name] || {} if(images[validatorAddress]){ @@ -311,13 +287,17 @@ class Delegations extends React.Component { this.setState({ error: error }); } + authzSupport() { + return this.props.network.authzSupport + } + grantsValid(operator) { const grants = this.state.operatorGrants[operator.botAddress]; return grants && grants.grantsValid; } restakePossible() { - return !this.state.isNanoLedger && !this.state.authzMissing; + return !this.state.isNanoLedger && this.authzSupport(); } operatorForValidator(validatorAddress) { @@ -715,21 +695,21 @@ class Delegations extends React.Component { const alerts = ( <> - {this.state.authzMissing && ( + {!this.authzSupport() && ( {this.props.network.prettyName} doesn't support Authz just yet. You can manually restake for now and REStake is ready when support is enabled )} - {!this.state.authzMissing && !this.props.operators.length && ( + {this.authzSupport() && !this.props.operators.length && ( )} - {!this.state.authzMissing && + {this.authzSupport() && this.props.operators.length > 0 && this.state.isNanoLedger && ( Date: Thu, 31 Mar 2022 10:18:39 +0200 Subject: [PATCH 322/862] fix: allow to properly parse Desmos profile accounts --- package-lock.json | 556 ++++++++++++++++++++++++++---------- package.json | 5 +- src/utils/SigningClient.mjs | 6 +- 3 files changed, 414 insertions(+), 153 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88ae2c2d6..615b4fb3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,9 @@ "dependencies": { "@bugsnag/js": "^7.16.1", "@bugsnag/plugin-react": "^7.16.1", - "@cosmjs/proto-signing": "^0.27.1", - "@cosmjs/stargate": "^0.27.1", + "@cosmjs/proto-signing": "^0.28.1", + "@cosmjs/stargate": "^0.28.1", + "@desmoslabs/desmjs": "^2.0.0-beta.4", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^13.5.0", @@ -468,37 +469,34 @@ } }, "node_modules/@cosmjs/amino": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.27.1.tgz", - "integrity": "sha512-w56ar/nK9+qlvWDpBPRmD0Blk2wfkkLqRi1COs1x7Ll1LF0AtkIBUjbRKplENLbNovK0T3h+w8bHiFm+GBGQOA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.1.tgz", + "integrity": "sha512-7CihIqU3YOE0dEa1e/dWDMetxmAjYa44P0trmU8cU5TU2JnCBPfjF4hZcTPfoZ4KXNKPQrqAWuLxy0YB6o/5/Q==", "dependencies": { - "@cosmjs/crypto": "0.27.1", - "@cosmjs/encoding": "0.27.1", - "@cosmjs/math": "0.27.1", - "@cosmjs/utils": "0.27.1" + "@cosmjs/crypto": "0.28.1", + "@cosmjs/encoding": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/utils": "0.28.1" } }, "node_modules/@cosmjs/crypto": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.27.1.tgz", - "integrity": "sha512-vbcxwSt99tIYJg8Spp00wc3zx72qx+pY3ozGuBN8gAvySnagK9dQ/jHwtWQWdammmdD6oW+75WfIHZ+gNa+Ybg==", - "dependencies": { - "@cosmjs/encoding": "0.27.1", - "@cosmjs/math": "0.27.1", - "@cosmjs/utils": "0.27.1", - "bip39": "^3.0.2", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.1.tgz", + "integrity": "sha512-QLgP+xvd3X4vNU9PPnEGc1PI5qctgg1o6ANivqHgiJdX2bFolsqCqFQDs1rvGf8GWLJ2eGwXZPX1c/QK0bT9+A==", + "dependencies": { + "@cosmjs/encoding": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/utils": "0.28.1", + "@noble/hashes": "^1", "bn.js": "^5.2.0", "elliptic": "^6.5.3", - "js-sha3": "^0.8.0", - "libsodium-wrappers": "^0.7.6", - "ripemd160": "^2.0.2", - "sha.js": "^2.4.11" + "libsodium-wrappers": "^0.7.6" } }, "node_modules/@cosmjs/encoding": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.27.1.tgz", - "integrity": "sha512-rayLsA0ojHeniaRfWWcqSsrE/T1rl1gl0OXVNtXlPwLJifKBeLEefGbOUiAQaT0wgJ8VNGBazVtAZBpJidfDhw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.1.tgz", + "integrity": "sha512-FqKc+P5rBKq8hW2WHF6L8dmSJhy9mVBDhnJNCLUwyiKBywY9m4BZNTa0mPVQSXISx/c5DPbpJ5SChGL72qNgBw==", "dependencies": { "base64-js": "^1.3.0", "bech32": "^1.1.4", @@ -506,59 +504,61 @@ } }, "node_modules/@cosmjs/json-rpc": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.27.1.tgz", - "integrity": "sha512-AKvsllGr6oN5kiroatIeIIxRdCFetLd8LCWV04RRNkoJ2OefDNb46VlWEQ+gI3ay5GgfVjB9qAcfvbJyrcEv+A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.1.tgz", + "integrity": "sha512-yuOxhrE5ldJDcXhSbtdadAB5GBOGZkcDnZe+v70KcRf09qM81XPYiYfoMsxO9rb1QzThLlAbBx/kRebtzZPwDw==", "dependencies": { - "@cosmjs/stream": "0.27.1", + "@cosmjs/stream": "0.28.1", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/math": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.27.1.tgz", - "integrity": "sha512-cHWVjmfIjtRc7f80n7x+J5k8pe+vTVTQ0lA82tIxUgqUvgS6rogPP/TmGtTiZ4+NxWxd11DUISY6gVpr18/VNQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.1.tgz", + "integrity": "sha512-rF5q4BSwNBo0kNBi8asaoHsRx/TchJ/P4IlRjXY8UGCfKCkSRQEID3ffgE8naXf+BDn5x4cSC8da3xy/aCZpAA==", "dependencies": { "bn.js": "^5.2.0" } }, "node_modules/@cosmjs/proto-signing": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.27.1.tgz", - "integrity": "sha512-t7/VvQivMdM1KgKWai/9ZCEcGFXJtr9Xo0hGcPLTn9wGkh9tmOsUXINYVMsf5D/jWIm1MDPbGCYfdb9V1Od4hw==", - "dependencies": { - "@cosmjs/amino": "0.27.1", - "@cosmjs/crypto": "0.27.1", - "@cosmjs/math": "0.27.1", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.1.tgz", + "integrity": "sha512-dmBVV/b3x2mxwiEEdLFBpagr6R/X74R0AJLCxaKDwK4FpI4vSz84pOe5J6c9ffHHXnWp129M+pRBBE2S9tK0Cg==", + "dependencies": { + "@cosmjs/amino": "0.28.1", + "@cosmjs/crypto": "0.28.1", + "@cosmjs/encoding": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/utils": "0.28.1", "cosmjs-types": "^0.4.0", "long": "^4.0.0", "protobufjs": "~6.10.2" } }, "node_modules/@cosmjs/socket": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.27.1.tgz", - "integrity": "sha512-bKCRsaSXh/TA7efxVCogzS2K3cgC40Ge2itFYmTfgpOE+++52FchCblVCsCYwMNDLS497RP4P0GbeC1VEBToMA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.1.tgz", + "integrity": "sha512-fFClGMOdBF3Jsti929AXoywsu6TtEKALxmwPvRYEsbrurLy9Ko28qoWdPFUvQw5WEy5rkzDq+3DKK5tbzsbQNA==", "dependencies": { - "@cosmjs/stream": "0.27.1", + "@cosmjs/stream": "0.28.1", "isomorphic-ws": "^4.0.1", "ws": "^7", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/stargate": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.27.1.tgz", - "integrity": "sha512-7hAIyNd6NbhQA51w9mPVyMYw515Hpj0o7SXMaqbc7nxs3hkJNMONQ9RakyMm0U/WeCd6ObcSaPEcdkqbfkc+mg==", - "dependencies": { - "@confio/ics23": "^0.6.3", - "@cosmjs/amino": "0.27.1", - "@cosmjs/encoding": "0.27.1", - "@cosmjs/math": "0.27.1", - "@cosmjs/proto-signing": "0.27.1", - "@cosmjs/stream": "0.27.1", - "@cosmjs/tendermint-rpc": "0.27.1", - "@cosmjs/utils": "0.27.1", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.1.tgz", + "integrity": "sha512-6mHHI2pkqb/khRTWRATODNieEv3PFhnUKgxkdiw39111oN35TTuSwxpT1c2le/VheoiaSZtBotDgL60wUP9LGA==", + "dependencies": { + "@confio/ics23": "^0.6.8", + "@cosmjs/amino": "0.28.1", + "@cosmjs/encoding": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/proto-signing": "0.28.1", + "@cosmjs/stream": "0.28.1", + "@cosmjs/tendermint-rpc": "0.28.1", + "@cosmjs/utils": "0.28.1", "cosmjs-types": "^0.4.0", "long": "^4.0.0", "protobufjs": "~6.10.2", @@ -566,24 +566,25 @@ } }, "node_modules/@cosmjs/stream": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.27.1.tgz", - "integrity": "sha512-cEyEAVfXEyuUpKYBeEJrOj8Dp/c+M6a0oGJHxvDdhP5gSsaeCPgQXrh7qZFBiUfu3Brmqd+e/bKZm+068l9bBw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.1.tgz", + "integrity": "sha512-lhgPI+vjpnwUOrLJADAA9YReMwbDCohIhuMhXiyXTnJfnUcitTh882LeRueyuynXW7zT1qjzZk1MgdumbDeXiA==", "dependencies": { "xstream": "^11.14.0" } }, "node_modules/@cosmjs/tendermint-rpc": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.27.1.tgz", - "integrity": "sha512-eN1NyBYIiFutDNleEaTfvIJ3S3KA1gP45UHaLhSETm8KyiaUqg/b0Mj6sp7J3h4BhgwLq2zn/TDtIn0k5luedg==", - "dependencies": { - "@cosmjs/crypto": "0.27.1", - "@cosmjs/encoding": "0.27.1", - "@cosmjs/json-rpc": "0.27.1", - "@cosmjs/math": "0.27.1", - "@cosmjs/socket": "0.27.1", - "@cosmjs/stream": "0.27.1", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.1.tgz", + "integrity": "sha512-2Mt3Hd0Lp8BB6jnkiWr0U5oDRwOl3pueVFkCgv6XIUX8OKY48o97KOQrqW1BDAMYSL7QuXF7ULpvmRgfJqIxeA==", + "dependencies": { + "@cosmjs/crypto": "0.28.1", + "@cosmjs/encoding": "0.28.1", + "@cosmjs/json-rpc": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/socket": "0.28.1", + "@cosmjs/stream": "0.28.1", + "@cosmjs/utils": "0.28.1", "axios": "^0.21.2", "readonly-date": "^1.0.0", "xstream": "^11.14.0" @@ -598,9 +599,62 @@ } }, "node_modules/@cosmjs/utils": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.27.1.tgz", - "integrity": "sha512-VG7QPDiMUzVPxRdJahDV8PXxVdnuAHiIuG56hldV4yPnOz/si/DLNd7VAUUA5923b6jS1Hhev0Hr6AhEkcxBMg==" + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.1.tgz", + "integrity": "sha512-PhdsifctdpMUXeWQjbQiHeOCOhWtK/OXdEG3E2PvvYxlmWHNu1faio+u2gZU6PPjL+qgqlAu92sybwsw/TRa+w==" + }, + "node_modules/@desmoslabs/desmjs": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs/-/desmjs-2.0.0-beta.4.tgz", + "integrity": "sha512-Ywrb/fQt8fWKm3Xfcx5n9m1Xf/N+IzSIkvZgZ31v/79D5xX/4zN1cQkZO91eariKEsp70mUeB73vKeoXZs+ejw==", + "dependencies": { + "@cosmjs/amino": "^0.28.1", + "@cosmjs/crypto": "^0.28.1", + "@cosmjs/encoding": "^0.28.1", + "@cosmjs/math": "^0.28.1", + "@cosmjs/proto-signing": "^0.28.1", + "@cosmjs/stargate": "^0.28.1", + "@cosmjs/tendermint-rpc": "^0.28.1", + "@cosmjs/utils": "^0.28.1", + "@desmoslabs/desmjs-types": "2.0.0-beta.4", + "cosmjs-types": "^0.4.1", + "cosmos-wallet": "^1.1.0", + "long": "^4.0.0" + } + }, + "node_modules/@desmoslabs/desmjs-types": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs-types/-/desmjs-types-2.0.0-beta.4.tgz", + "integrity": "sha512-gBdVuUmyqAcirxdKzVXHtmQBS3QfzHglNYTeN0zgT/lH9AFnrpOMrbB8Z0hKiyUzkeJusTZo7pkX92gQ7n0vUA==", + "dependencies": { + "long": "^4.0.0", + "protobufjs": "^6.11.2" + } + }, + "node_modules/@desmoslabs/desmjs-types/node_modules/protobufjs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", + "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } }, "node_modules/@emotion/babel-plugin": { "version": "11.7.2", @@ -6172,6 +6226,81 @@ "pbts": "bin/pbts" } }, + "node_modules/cosmos-wallet": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cosmos-wallet/-/cosmos-wallet-1.1.0.tgz", + "integrity": "sha512-jm5MFRYzvOCA/mhULObpaoE2Na0shBn3lrkv2GDedmRZfuoDvOa63WID4qMoIycteefjRKdxXf+26vDrgd79aQ==", + "dependencies": { + "@cosmjs/amino": "^0.25.4", + "@cosmjs/proto-signing": "^0.25.4" + } + }, + "node_modules/cosmos-wallet/node_modules/@cosmjs/amino": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.25.6.tgz", + "integrity": "sha512-9dXN2W7LHjDtJUGNsQ9ok0DfxeN3ca/TXnxCR3Ikh/5YqBqxI8Gel1J9PQO9L6EheYyh045Wff4bsMaLjyEeqQ==", + "dependencies": { + "@cosmjs/crypto": "^0.25.6", + "@cosmjs/encoding": "^0.25.6", + "@cosmjs/math": "^0.25.6", + "@cosmjs/utils": "^0.25.6" + } + }, + "node_modules/cosmos-wallet/node_modules/@cosmjs/crypto": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.25.6.tgz", + "integrity": "sha512-ec+YcQLrg2ibcxtNrh4FqQnG9kG9IE/Aik2NH6+OXQdFU/qFuBTxSFcKDgzzBOChwlkXwydllM9Jjbp+dgIzRw==", + "dependencies": { + "@cosmjs/encoding": "^0.25.6", + "@cosmjs/math": "^0.25.6", + "@cosmjs/utils": "^0.25.6", + "bip39": "^3.0.2", + "bn.js": "^4.11.8", + "elliptic": "^6.5.3", + "js-sha3": "^0.8.0", + "libsodium-wrappers": "^0.7.6", + "ripemd160": "^2.0.2", + "sha.js": "^2.4.11" + } + }, + "node_modules/cosmos-wallet/node_modules/@cosmjs/encoding": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.25.6.tgz", + "integrity": "sha512-0imUOB8XkUstI216uznPaX1hqgvLQ2Xso3zJj5IV5oJuNlsfDj9nt/iQxXWbJuettc6gvrFfpf+Vw2vBZSZ75g==", + "dependencies": { + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" + } + }, + "node_modules/cosmos-wallet/node_modules/@cosmjs/math": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.25.6.tgz", + "integrity": "sha512-Fmyc9FJ8KMU34n7rdapMJrT/8rx5WhMw2F7WLBu7AVLcBh0yWsXIcMSJCoPHTOnMIiABjXsnrrwEaLrOOBfu6A==", + "dependencies": { + "bn.js": "^4.11.8" + } + }, + "node_modules/cosmos-wallet/node_modules/@cosmjs/proto-signing": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.25.6.tgz", + "integrity": "sha512-JpQ+Vnv9s6i3x8f3Jo0lJZ3VMnj3R5sMgX+8ti1LtB7qEYRR85qbDrEG9hDGIKqJJabvrAuCHnO6hYi0vJEJHA==", + "dependencies": { + "@cosmjs/amino": "^0.25.6", + "long": "^4.0.0", + "protobufjs": "~6.10.2" + } + }, + "node_modules/cosmos-wallet/node_modules/@cosmjs/utils": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.25.6.tgz", + "integrity": "sha512-ofOYiuxVKNo238vCPPlaDzqPXy2AQ/5/nashBo5rvPZJkxt9LciGfUEQWPCOb1BIJDNx2Dzu0z4XCf/dwzl0Dg==" + }, + "node_modules/cosmos-wallet/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -7879,14 +8008,14 @@ } }, "node_modules/libsodium": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.9.tgz", - "integrity": "sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A==" + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz", + "integrity": "sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ==" }, "node_modules/libsodium-wrappers": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz", - "integrity": "sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==", + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", + "integrity": "sha512-pO3F1Q9NPLB/MWIhehim42b/Fwb30JNScCNh8TcQ/kIc+qGLQch8ag8wb0keK3EP5kbGakk1H8Wwo7v+36rNQg==", "dependencies": { "libsodium": "^0.7.0" } @@ -10184,37 +10313,34 @@ } }, "@cosmjs/amino": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.27.1.tgz", - "integrity": "sha512-w56ar/nK9+qlvWDpBPRmD0Blk2wfkkLqRi1COs1x7Ll1LF0AtkIBUjbRKplENLbNovK0T3h+w8bHiFm+GBGQOA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.1.tgz", + "integrity": "sha512-7CihIqU3YOE0dEa1e/dWDMetxmAjYa44P0trmU8cU5TU2JnCBPfjF4hZcTPfoZ4KXNKPQrqAWuLxy0YB6o/5/Q==", "requires": { - "@cosmjs/crypto": "0.27.1", - "@cosmjs/encoding": "0.27.1", - "@cosmjs/math": "0.27.1", - "@cosmjs/utils": "0.27.1" + "@cosmjs/crypto": "0.28.1", + "@cosmjs/encoding": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/utils": "0.28.1" } }, "@cosmjs/crypto": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.27.1.tgz", - "integrity": "sha512-vbcxwSt99tIYJg8Spp00wc3zx72qx+pY3ozGuBN8gAvySnagK9dQ/jHwtWQWdammmdD6oW+75WfIHZ+gNa+Ybg==", - "requires": { - "@cosmjs/encoding": "0.27.1", - "@cosmjs/math": "0.27.1", - "@cosmjs/utils": "0.27.1", - "bip39": "^3.0.2", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.1.tgz", + "integrity": "sha512-QLgP+xvd3X4vNU9PPnEGc1PI5qctgg1o6ANivqHgiJdX2bFolsqCqFQDs1rvGf8GWLJ2eGwXZPX1c/QK0bT9+A==", + "requires": { + "@cosmjs/encoding": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/utils": "0.28.1", + "@noble/hashes": "^1", "bn.js": "^5.2.0", "elliptic": "^6.5.3", - "js-sha3": "^0.8.0", - "libsodium-wrappers": "^0.7.6", - "ripemd160": "^2.0.2", - "sha.js": "^2.4.11" + "libsodium-wrappers": "^0.7.6" } }, "@cosmjs/encoding": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.27.1.tgz", - "integrity": "sha512-rayLsA0ojHeniaRfWWcqSsrE/T1rl1gl0OXVNtXlPwLJifKBeLEefGbOUiAQaT0wgJ8VNGBazVtAZBpJidfDhw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.1.tgz", + "integrity": "sha512-FqKc+P5rBKq8hW2WHF6L8dmSJhy9mVBDhnJNCLUwyiKBywY9m4BZNTa0mPVQSXISx/c5DPbpJ5SChGL72qNgBw==", "requires": { "base64-js": "^1.3.0", "bech32": "^1.1.4", @@ -10222,59 +10348,61 @@ } }, "@cosmjs/json-rpc": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.27.1.tgz", - "integrity": "sha512-AKvsllGr6oN5kiroatIeIIxRdCFetLd8LCWV04RRNkoJ2OefDNb46VlWEQ+gI3ay5GgfVjB9qAcfvbJyrcEv+A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.1.tgz", + "integrity": "sha512-yuOxhrE5ldJDcXhSbtdadAB5GBOGZkcDnZe+v70KcRf09qM81XPYiYfoMsxO9rb1QzThLlAbBx/kRebtzZPwDw==", "requires": { - "@cosmjs/stream": "0.27.1", + "@cosmjs/stream": "0.28.1", "xstream": "^11.14.0" } }, "@cosmjs/math": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.27.1.tgz", - "integrity": "sha512-cHWVjmfIjtRc7f80n7x+J5k8pe+vTVTQ0lA82tIxUgqUvgS6rogPP/TmGtTiZ4+NxWxd11DUISY6gVpr18/VNQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.1.tgz", + "integrity": "sha512-rF5q4BSwNBo0kNBi8asaoHsRx/TchJ/P4IlRjXY8UGCfKCkSRQEID3ffgE8naXf+BDn5x4cSC8da3xy/aCZpAA==", "requires": { "bn.js": "^5.2.0" } }, "@cosmjs/proto-signing": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.27.1.tgz", - "integrity": "sha512-t7/VvQivMdM1KgKWai/9ZCEcGFXJtr9Xo0hGcPLTn9wGkh9tmOsUXINYVMsf5D/jWIm1MDPbGCYfdb9V1Od4hw==", - "requires": { - "@cosmjs/amino": "0.27.1", - "@cosmjs/crypto": "0.27.1", - "@cosmjs/math": "0.27.1", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.1.tgz", + "integrity": "sha512-dmBVV/b3x2mxwiEEdLFBpagr6R/X74R0AJLCxaKDwK4FpI4vSz84pOe5J6c9ffHHXnWp129M+pRBBE2S9tK0Cg==", + "requires": { + "@cosmjs/amino": "0.28.1", + "@cosmjs/crypto": "0.28.1", + "@cosmjs/encoding": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/utils": "0.28.1", "cosmjs-types": "^0.4.0", "long": "^4.0.0", "protobufjs": "~6.10.2" } }, "@cosmjs/socket": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.27.1.tgz", - "integrity": "sha512-bKCRsaSXh/TA7efxVCogzS2K3cgC40Ge2itFYmTfgpOE+++52FchCblVCsCYwMNDLS497RP4P0GbeC1VEBToMA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.1.tgz", + "integrity": "sha512-fFClGMOdBF3Jsti929AXoywsu6TtEKALxmwPvRYEsbrurLy9Ko28qoWdPFUvQw5WEy5rkzDq+3DKK5tbzsbQNA==", "requires": { - "@cosmjs/stream": "0.27.1", + "@cosmjs/stream": "0.28.1", "isomorphic-ws": "^4.0.1", "ws": "^7", "xstream": "^11.14.0" } }, "@cosmjs/stargate": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.27.1.tgz", - "integrity": "sha512-7hAIyNd6NbhQA51w9mPVyMYw515Hpj0o7SXMaqbc7nxs3hkJNMONQ9RakyMm0U/WeCd6ObcSaPEcdkqbfkc+mg==", - "requires": { - "@confio/ics23": "^0.6.3", - "@cosmjs/amino": "0.27.1", - "@cosmjs/encoding": "0.27.1", - "@cosmjs/math": "0.27.1", - "@cosmjs/proto-signing": "0.27.1", - "@cosmjs/stream": "0.27.1", - "@cosmjs/tendermint-rpc": "0.27.1", - "@cosmjs/utils": "0.27.1", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.1.tgz", + "integrity": "sha512-6mHHI2pkqb/khRTWRATODNieEv3PFhnUKgxkdiw39111oN35TTuSwxpT1c2le/VheoiaSZtBotDgL60wUP9LGA==", + "requires": { + "@confio/ics23": "^0.6.8", + "@cosmjs/amino": "0.28.1", + "@cosmjs/encoding": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/proto-signing": "0.28.1", + "@cosmjs/stream": "0.28.1", + "@cosmjs/tendermint-rpc": "0.28.1", + "@cosmjs/utils": "0.28.1", "cosmjs-types": "^0.4.0", "long": "^4.0.0", "protobufjs": "~6.10.2", @@ -10282,24 +10410,25 @@ } }, "@cosmjs/stream": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.27.1.tgz", - "integrity": "sha512-cEyEAVfXEyuUpKYBeEJrOj8Dp/c+M6a0oGJHxvDdhP5gSsaeCPgQXrh7qZFBiUfu3Brmqd+e/bKZm+068l9bBw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.1.tgz", + "integrity": "sha512-lhgPI+vjpnwUOrLJADAA9YReMwbDCohIhuMhXiyXTnJfnUcitTh882LeRueyuynXW7zT1qjzZk1MgdumbDeXiA==", "requires": { "xstream": "^11.14.0" } }, "@cosmjs/tendermint-rpc": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.27.1.tgz", - "integrity": "sha512-eN1NyBYIiFutDNleEaTfvIJ3S3KA1gP45UHaLhSETm8KyiaUqg/b0Mj6sp7J3h4BhgwLq2zn/TDtIn0k5luedg==", - "requires": { - "@cosmjs/crypto": "0.27.1", - "@cosmjs/encoding": "0.27.1", - "@cosmjs/json-rpc": "0.27.1", - "@cosmjs/math": "0.27.1", - "@cosmjs/socket": "0.27.1", - "@cosmjs/stream": "0.27.1", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.1.tgz", + "integrity": "sha512-2Mt3Hd0Lp8BB6jnkiWr0U5oDRwOl3pueVFkCgv6XIUX8OKY48o97KOQrqW1BDAMYSL7QuXF7ULpvmRgfJqIxeA==", + "requires": { + "@cosmjs/crypto": "0.28.1", + "@cosmjs/encoding": "0.28.1", + "@cosmjs/json-rpc": "0.28.1", + "@cosmjs/math": "0.28.1", + "@cosmjs/socket": "0.28.1", + "@cosmjs/stream": "0.28.1", + "@cosmjs/utils": "0.28.1", "axios": "^0.21.2", "readonly-date": "^1.0.0", "xstream": "^11.14.0" @@ -10316,9 +10445,59 @@ } }, "@cosmjs/utils": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.27.1.tgz", - "integrity": "sha512-VG7QPDiMUzVPxRdJahDV8PXxVdnuAHiIuG56hldV4yPnOz/si/DLNd7VAUUA5923b6jS1Hhev0Hr6AhEkcxBMg==" + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.1.tgz", + "integrity": "sha512-PhdsifctdpMUXeWQjbQiHeOCOhWtK/OXdEG3E2PvvYxlmWHNu1faio+u2gZU6PPjL+qgqlAu92sybwsw/TRa+w==" + }, + "@desmoslabs/desmjs": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs/-/desmjs-2.0.0-beta.4.tgz", + "integrity": "sha512-Ywrb/fQt8fWKm3Xfcx5n9m1Xf/N+IzSIkvZgZ31v/79D5xX/4zN1cQkZO91eariKEsp70mUeB73vKeoXZs+ejw==", + "requires": { + "@cosmjs/amino": "^0.28.1", + "@cosmjs/crypto": "^0.28.1", + "@cosmjs/encoding": "^0.28.1", + "@cosmjs/math": "^0.28.1", + "@cosmjs/proto-signing": "^0.28.1", + "@cosmjs/stargate": "^0.28.1", + "@cosmjs/tendermint-rpc": "^0.28.1", + "@cosmjs/utils": "^0.28.1", + "@desmoslabs/desmjs-types": "2.0.0-beta.4", + "cosmjs-types": "^0.4.1", + "cosmos-wallet": "^1.1.0", + "long": "^4.0.0" + } + }, + "@desmoslabs/desmjs-types": { + "version": "2.0.0-beta.4", + "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs-types/-/desmjs-types-2.0.0-beta.4.tgz", + "integrity": "sha512-gBdVuUmyqAcirxdKzVXHtmQBS3QfzHglNYTeN0zgT/lH9AFnrpOMrbB8Z0hKiyUzkeJusTZo7pkX92gQ7n0vUA==", + "requires": { + "long": "^4.0.0", + "protobufjs": "^6.11.2" + }, + "dependencies": { + "protobufjs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", + "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + } + } + } }, "@emotion/babel-plugin": { "version": "11.7.2", @@ -14260,6 +14439,83 @@ } } }, + "cosmos-wallet": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cosmos-wallet/-/cosmos-wallet-1.1.0.tgz", + "integrity": "sha512-jm5MFRYzvOCA/mhULObpaoE2Na0shBn3lrkv2GDedmRZfuoDvOa63WID4qMoIycteefjRKdxXf+26vDrgd79aQ==", + "requires": { + "@cosmjs/amino": "^0.25.4", + "@cosmjs/proto-signing": "^0.25.4" + }, + "dependencies": { + "@cosmjs/amino": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.25.6.tgz", + "integrity": "sha512-9dXN2W7LHjDtJUGNsQ9ok0DfxeN3ca/TXnxCR3Ikh/5YqBqxI8Gel1J9PQO9L6EheYyh045Wff4bsMaLjyEeqQ==", + "requires": { + "@cosmjs/crypto": "^0.25.6", + "@cosmjs/encoding": "^0.25.6", + "@cosmjs/math": "^0.25.6", + "@cosmjs/utils": "^0.25.6" + } + }, + "@cosmjs/crypto": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.25.6.tgz", + "integrity": "sha512-ec+YcQLrg2ibcxtNrh4FqQnG9kG9IE/Aik2NH6+OXQdFU/qFuBTxSFcKDgzzBOChwlkXwydllM9Jjbp+dgIzRw==", + "requires": { + "@cosmjs/encoding": "^0.25.6", + "@cosmjs/math": "^0.25.6", + "@cosmjs/utils": "^0.25.6", + "bip39": "^3.0.2", + "bn.js": "^4.11.8", + "elliptic": "^6.5.3", + "js-sha3": "^0.8.0", + "libsodium-wrappers": "^0.7.6", + "ripemd160": "^2.0.2", + "sha.js": "^2.4.11" + } + }, + "@cosmjs/encoding": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.25.6.tgz", + "integrity": "sha512-0imUOB8XkUstI216uznPaX1hqgvLQ2Xso3zJj5IV5oJuNlsfDj9nt/iQxXWbJuettc6gvrFfpf+Vw2vBZSZ75g==", + "requires": { + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" + } + }, + "@cosmjs/math": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.25.6.tgz", + "integrity": "sha512-Fmyc9FJ8KMU34n7rdapMJrT/8rx5WhMw2F7WLBu7AVLcBh0yWsXIcMSJCoPHTOnMIiABjXsnrrwEaLrOOBfu6A==", + "requires": { + "bn.js": "^4.11.8" + } + }, + "@cosmjs/proto-signing": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.25.6.tgz", + "integrity": "sha512-JpQ+Vnv9s6i3x8f3Jo0lJZ3VMnj3R5sMgX+8ti1LtB7qEYRR85qbDrEG9hDGIKqJJabvrAuCHnO6hYi0vJEJHA==", + "requires": { + "@cosmjs/amino": "^0.25.6", + "long": "^4.0.0", + "protobufjs": "~6.10.2" + } + }, + "@cosmjs/utils": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.25.6.tgz", + "integrity": "sha512-ofOYiuxVKNo238vCPPlaDzqPXy2AQ/5/nashBo5rvPZJkxt9LciGfUEQWPCOb1BIJDNx2Dzu0z4XCf/dwzl0Dg==" + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, "create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -15618,14 +15874,14 @@ } }, "libsodium": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.9.tgz", - "integrity": "sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A==" + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz", + "integrity": "sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ==" }, "libsodium-wrappers": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz", - "integrity": "sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==", + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", + "integrity": "sha512-pO3F1Q9NPLB/MWIhehim42b/Fwb30JNScCNh8TcQ/kIc+qGLQch8ag8wb0keK3EP5kbGakk1H8Wwo7v+36rNQg==", "requires": { "libsodium": "^0.7.0" } diff --git a/package.json b/package.json index 8ddc9cfcc..7bae94eb9 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,9 @@ "dependencies": { "@bugsnag/js": "^7.16.1", "@bugsnag/plugin-react": "^7.16.1", - "@cosmjs/proto-signing": "^0.27.1", - "@cosmjs/stargate": "^0.27.1", + "@cosmjs/proto-signing": "^0.28.1", + "@cosmjs/stargate": "^0.28.1", + "@desmoslabs/desmjs": "^2.0.0-beta.4", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^13.5.0", diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 9124eacbe..1f5d7289b 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -7,12 +7,16 @@ import { assertIsDeliverTxSuccess, GasPrice } from '@cosmjs/stargate' +import {profileFromAny} from "@desmoslabs/desmjs"; const SigningClient = async (rpcUrl, chainId, defaultGasPrice, signer, key) => { const client = rpcUrl && await SigningStargateClient.connectWithSigner( rpcUrl, - signer + signer, + { + accountParser: profileFromAny, + } ) const getAddress = async () => { From 8aa66c1e65dba50dbb15b3a6900cc2ac7c4a19f6 Mon Sep 17 00:00:00 2001 From: Ali Ghasemzadeh Date: Thu, 31 Mar 2022 15:34:42 +0430 Subject: [PATCH 323/862] Add process.env to not use docker. --- package.json | 1 + scripts/base.mjs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8ddc9cfcc..d4a71870d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "bootstrap": "^5.1.3", "buffer": "^6.0.3", "cosmjs-types": "^0.4.1", + "dotenv": "^16.0.0", "fuzzy-search": "^3.2.1", "moment": "^2.29.1", "parse-duration": "^1.0.2", diff --git a/scripts/base.mjs b/scripts/base.mjs index 6ae153c6c..86e970c19 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -14,6 +14,8 @@ import { MsgExec } from "cosmjs-types/cosmos/authz/v1beta1/tx.js"; import fs from 'fs' import _ from 'lodash' +import 'dotenv/config' + export class Autostake { constructor(){ this.mnemonic = process.env.MNEMONIC @@ -49,7 +51,7 @@ export class Autostake { timeStamp('You are using public nodes, script may fail with many delegations. Check the README to use your own') timeStamp('Delaying briefly to reduce load...') await new Promise(r => setTimeout(r, (Math.random() * 31) * 1000)); - } + } try { await this.runNetwork(client) @@ -122,7 +124,7 @@ export class Autostake { if (network.slip44 && network.slip44 !== slip44) { timeStamp("!! You are not using the preferred derivation path !!") timeStamp("!! You should switch to the correct path unless you have grants. Check the README !!") - } + } const operatorData = network.operators.find(el => el.botAddress === botAddress) From e80f929258222242485cc80103616cf4bd7dcd25 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 1 Apr 2022 22:25:02 +0100 Subject: [PATCH 324/862] Update package lock and run audit --- package-lock.json | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88ae2c2d6..4ed5dc3c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "bootstrap": "^5.1.3", "buffer": "^6.0.3", "cosmjs-types": "^0.4.1", + "dotenv": "^16.0.0", "fuzzy-search": "^3.2.1", "moment": "^2.29.1", "parse-duration": "^1.0.2", @@ -6519,6 +6520,14 @@ "webidl-conversions": "^4.0.2" } }, + "node_modules/dotenv": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", + "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==", + "engines": { + "node": ">=12" + } + }, "node_modules/dotenv-expand": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", @@ -8064,9 +8073,9 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "node_modules/moment": { "version": "2.29.1", @@ -14556,6 +14565,11 @@ "webidl-conversions": "^4.0.2" } }, + "dotenv": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", + "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==" + }, "dotenv-expand": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", @@ -15780,9 +15794,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "moment": { "version": "2.29.1", From 690653f4b0fafa826524a1049799255008c27b3c Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 1 Apr 2022 22:50:39 +0100 Subject: [PATCH 325/862] Don't get grants when delegations are cleared --- src/components/Delegations.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 36ee5c693..710a7b09b 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -55,7 +55,9 @@ class Delegations extends React.Component { return this.refresh(); } - const delegationsChanged = _.difference(Object.keys(this.props.delegations || {}), Object.keys(prevProps.delegations || {})).length > 0 + if(!this.props.delegations) return + + const delegationsChanged = _.difference(Object.keys(this.props.delegations), Object.keys(prevProps.delegations || {})).length > 0 if (delegationsChanged) { this.getGrants() } From c4b3969539507d04358aca3ff26a50c5f4da5162 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 2 Apr 2022 00:12:44 +0100 Subject: [PATCH 326/862] Network specific signers --- src/utils/DesmosSigningClient.mjs | 10 +++++ src/utils/Network.mjs | 7 +++- src/utils/SigningClient.mjs | 67 +++++++++++++++---------------- 3 files changed, 48 insertions(+), 36 deletions(-) create mode 100644 src/utils/DesmosSigningClient.mjs diff --git a/src/utils/DesmosSigningClient.mjs b/src/utils/DesmosSigningClient.mjs new file mode 100644 index 000000000..7c274c8bc --- /dev/null +++ b/src/utils/DesmosSigningClient.mjs @@ -0,0 +1,10 @@ +import { profileFromAny } from "@desmoslabs/desmjs"; +import SigningClient from "./SigningClient.mjs"; + +function DesmosSigningClient(rpcUrl, defaultGasPrice, signer, key) { + return SigningClient(rpcUrl, defaultGasPrice, signer, key, { + accountParser: profileFromAny, + }) +} + +export default DesmosSigningClient diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 44c47f176..a466e161c 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -5,8 +5,12 @@ import ApyClient from '../ApyClient.mjs' import Operator from './Operator.mjs' import Chain from './Chain.mjs' import CosmosDirectory from './CosmosDirectory.mjs' +import DesmosSigningClient from './DesmosSigningClient.mjs' const Network = async (data, withoutQueryClient) => { + const SIGNERS = { + desmos: DesmosSigningClient + } const chain = await Chain(data) const directory = CosmosDirectory() @@ -31,7 +35,8 @@ const Network = async (data, withoutQueryClient) => { if(!queryClient) return const gasPrice = data.gasPrice || '0.0025' + chain.denom - return SigningClient(queryClient.rpcUrl, chain.chainId, gasPrice, wallet, key) + const client = SIGNERS[data.name] || SigningClient + return client(queryClient.rpcUrl, gasPrice, wallet, key) } const apyClient = queryClient && ApyClient(chain, queryClient.rpcUrl, queryClient.restUrl) diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 1f5d7289b..ce5a55bf5 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -1,5 +1,3 @@ -import {findAsync} from './Helpers.mjs' - import _ from 'lodash' import { SigningStargateClient, @@ -7,69 +5,69 @@ import { assertIsDeliverTxSuccess, GasPrice } from '@cosmjs/stargate' -import {profileFromAny} from "@desmoslabs/desmjs"; -const SigningClient = async (rpcUrl, chainId, defaultGasPrice, signer, key) => { +async function SigningClient(rpcUrl, defaultGasPrice, signer, key, signerOpts) { const client = rpcUrl && await SigningStargateClient.connectWithSigner( rpcUrl, signer, - { - accountParser: profileFromAny, - } - ) + signerOpts + ); - const getAddress = async () => { - const accounts = await signer.getAccounts() - return accounts[0].address + async function getAddress() { + const accounts = await signer.getAccounts(); + return accounts[0].address; } - const getIsNanoLedger = () => { - return key.isNanoLedger + function getIsNanoLedger() { + return key.isNanoLedger; } - const getFee = (gas, gasPrice) => { - if(!gas) gas = 200_000 - if(!gasPrice) gasPrice = GasPrice.fromString(defaultGasPrice); + function getFee(gas, gasPrice) { + if (!gas) + gas = 200000; + if (!gasPrice) + gasPrice = GasPrice.fromString(defaultGasPrice); return calculateFee(gas, gasPrice); } - const signAndBroadcastWithoutBalanceCheck = (address, msgs, gas, memo, gasPrice) => { - const defaultOptions = _.clone(signer.keplr.defaultOptions) + function signAndBroadcastWithoutBalanceCheck(address, msgs, gas, memo, gasPrice) { + const defaultOptions = _.clone(signer.keplr.defaultOptions); _.merge(signer.keplr.defaultOptions, { sign: { disableBalanceCheck: true } - }) + }); return signAndBroadcast(address, msgs, gas, memo, gasPrice).finally(() => { - signer.keplr.defaultOptions = defaultOptions - }) + signer.keplr.defaultOptions = defaultOptions; + }); } - const signAndBroadcast = async (address, msgs, gas, memo, gasPrice) => { + async function signAndBroadcast(address, msgs, gas, memo, gasPrice) { return new Promise(async (success, reject) => { - let fee + let fee; try { - if(!gas) gas = await simulate(address, msgs, memo) - fee = getFee(gas, gasPrice) + if (!gas) + gas = await simulate(address, msgs, memo); + fee = getFee(gas, gasPrice); } catch (error) { - return reject(error) + return reject(error); } client.signAndBroadcast(address, msgs, fee, memo).then((result) => { try { assertIsDeliverTxSuccess(result); client.disconnect(); - success(result) + success(result); } catch (error) { - reject(error) + reject(error); } }, (error) => { - reject(error) - }) + reject(error); + }); }); } - const simulate = async (address, msgs, memo, modifier) => { - const estimate = await client.simulate(address, msgs, memo) - return (parseInt(estimate * (modifier || 1.5))) + async function simulate(address, msgs, memo, modifier) { + const estimate = await client.simulate(address, msgs, memo); + return (parseInt(estimate * (modifier || 1.5))); } return { @@ -77,14 +75,13 @@ const SigningClient = async (rpcUrl, chainId, defaultGasPrice, signer, key) => { registry: client && client.registry, rpcUrl, client, - chainId, getAddress, getFee, getIsNanoLedger, simulate, signAndBroadcast, signAndBroadcastWithoutBalanceCheck - } + }; } export default SigningClient; From 07723ed453d7a11933ec9aa8febd6549723269b1 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 2 Apr 2022 01:02:18 +0100 Subject: [PATCH 327/862] Handle network change --- src/components/Delegations.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 710a7b09b..cd4ab4036 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -129,6 +129,8 @@ class Delegations extends React.Component { const calls = ordered.map((operator) => { return () => { const { botAddress, address } = operator; + if(!this.props.operators.includes(operator)) return; + return this.props.queryClient.getGrants(botAddress, this.props.address).then( (result) => { let grantValidators; From 50aa347b92891bd816edd8f3398fb2196b179e6b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 2 Apr 2022 12:48:57 +0100 Subject: [PATCH 328/862] Add npm install to update instructions --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d7533de99..8d01e1f11 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Update your local repository and pre-build your Docker containers with the follo ```bash git pull +docker-compose run app --rm npm install docker-compose build --no-cache ``` From 6afe5ba5510b959e33186623a893a8e7b687d241 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 2 Apr 2022 12:50:15 +0100 Subject: [PATCH 329/862] Add npm install to update instructions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d01e1f11..887caab27 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ Update your local repository and pre-build your Docker containers with the follo ```bash git pull -docker-compose run app --rm npm install +docker-compose run --rm app npm install docker-compose build --no-cache ``` From 128f49d358c278ae59a625875c82a25fc8cdcef9 Mon Sep 17 00:00:00 2001 From: Gjermund Bjaanes Date: Tue, 5 Apr 2022 09:49:06 +0200 Subject: [PATCH 330/862] Add generic grant support in script --- scripts/base.mjs | 4 ++++ src/utils/QueryClient.mjs | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/scripts/base.mjs b/scripts/base.mjs index 86e970c19..8e695ac1e 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -197,6 +197,10 @@ export class Autostake { .then( (result) => { if (result.claimGrant && result.stakeGrant) { + if (result.stakeGrant.authorization['@type'] !== "/cosmos.authz.v1beta1.GenericAuthorization") { + return [client.operator.address]; + } + const grantValidators = result.stakeGrant.authorization.allow_list.address if (!grantValidators.includes(client.operator.address)) { timeStamp(delegatorAddress, "Not autostaking for this validator, skipping") diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 1ecedaa81..526b519c5 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -142,6 +142,17 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { } else { return false; } + }) || result.grants.find((el) => { + if ( + el.authorization["@type"] === + "/cosmos.authz.v1beta1.GenericAuthorization" && + el.authorization.msg === + "/cosmos.staking.v1beta1.MsgDelegate" + ) { + return Date.parse(el.expiration) > new Date(); + } else { + return false; + } }); return { claimGrant, From 3330d6c353c35c88ee1ad328bde88e850163649c Mon Sep 17 00:00:00 2001 From: jayjay-crypto <58729187+jayjay-crypto@users.noreply.github.com> Date: Tue, 5 Apr 2022 16:32:31 +0200 Subject: [PATCH 331/862] Add Decentr --- src/networks.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/networks.json b/src/networks.json index ca2fe8b8c..75106884b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -180,5 +180,10 @@ "name": "rizon", "gasPrice": "0.0001uatolo", "authzSupport": true + }, + { + "name": "decentr", + "gasPrice": "0.025udec", + "authzSupport": false } ] From 257ae3d308ec03bf80728b9139a649cef2a31cef Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 5 Apr 2022 20:39:58 +0100 Subject: [PATCH 332/862] Log when using Generic and minor refactor --- scripts/base.mjs | 3 ++- src/utils/QueryClient.mjs | 13 ++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 8e695ac1e..5ea1e9a6e 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -197,7 +197,8 @@ export class Autostake { .then( (result) => { if (result.claimGrant && result.stakeGrant) { - if (result.stakeGrant.authorization['@type'] !== "/cosmos.authz.v1beta1.GenericAuthorization") { + if (result.stakeGrant.authorization['@type'] === "/cosmos.authz.v1beta1.GenericAuthorization") { + timeStamp(delegatorAddress, "Using GenericAuthorization, allowed") return [client.operator.address]; } diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 526b519c5..37fc9aad3 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -136,24 +136,19 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { const stakeGrant = result.grants.find((el) => { if ( el.authorization["@type"] === - "/cosmos.staking.v1beta1.StakeAuthorization" - ) { - return Date.parse(el.expiration) > new Date(); - } else { - return false; - } - }) || result.grants.find((el) => { - if ( + "/cosmos.staking.v1beta1.StakeAuthorization" || ( + // Handle GenericAuthorization for Ledger el.authorization["@type"] === "/cosmos.authz.v1beta1.GenericAuthorization" && el.authorization.msg === "/cosmos.staking.v1beta1.MsgDelegate" + ) ) { return Date.parse(el.expiration) > new Date(); } else { return false; } - }); + }) return { claimGrant, stakeGrant, From 25671f3a85900dcf866541bea5739b5ecdbec738 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 5 Apr 2022 20:17:45 +0100 Subject: [PATCH 333/862] Move NODE_ENV declaration --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b3ec8904c..b84c0c1f6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,12 +3,13 @@ FROM node:alpine RUN apk add --update python3 make g++ && rm -rf /var/cache/apk/* +ENV NODE_ENV=development + WORKDIR /usr/src/app COPY package*.json ./ RUN npm install COPY . ./ -ENV NODE_ENV=development ENV DIRECTORY_PROTOCOL=https ENV DIRECTORY_DOMAIN=cosmos.directory From 6c914338ca8c6185d57fba5d48038e388e12b64a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 5 Apr 2022 20:17:50 +0100 Subject: [PATCH 334/862] Remove old route --- src/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.js b/src/index.js index 1a0650e98..3fb3e9152 100644 --- a/src/index.js +++ b/src/index.js @@ -30,7 +30,6 @@ ReactDOM.render( } /> } /> - } /> From ddda2ca66ae6f1f40e13333b0e1be436894a6f20 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 5 Apr 2022 21:23:41 +0100 Subject: [PATCH 335/862] Enable Sentinel authz --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index ca2fe8b8c..6e1571caf 100644 --- a/src/networks.json +++ b/src/networks.json @@ -47,7 +47,7 @@ { "name": "sentinel", "gasPrice": "0.02udvpn", - "authzSupport": false + "authzSupport": true }, { "name": "dig", From 74c109f3105f5c5914d3daa1ebf661e9a9e5d4ce Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 5 Apr 2022 21:30:27 +0100 Subject: [PATCH 336/862] Delay grant load for 60 seconds --- src/components/Delegations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index cd4ab4036..fa158ba23 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -244,7 +244,7 @@ class Delegations extends React.Component { error: null, validatorLoading: _.set(state.validatorLoading, operator.address, false), })); - setTimeout(() => this.getGrants(), 10_000); + setTimeout(() => this.getGrants(), 60_000); } onRevoke(operator) { From 9dcdeb05bb5a3e62996b205ac0804ff2bf00c5fc Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 5 Apr 2022 21:34:10 +0100 Subject: [PATCH 337/862] Enable Fetchhub Authz --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index ca2fe8b8c..f0d7f50c6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -150,7 +150,7 @@ }, { "name": "fetchhub", - "authzSupport": false + "authzSupport": true }, { "name": "cerberus", From cb1bf6183d483bdfde7259068245901834a738e3 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 7 Apr 2022 13:56:03 +0100 Subject: [PATCH 338/862] Improve address display for smaller screen sizes --- src/components/App.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/App.js b/src/components/App.js index d84519bae..0a8211ab3 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -180,6 +180,12 @@ class App extends React.Component { {this.state.address} +
  3. + this.setCopied()}> + {this.state.address} + +
  4. From 767b6d21afeb9be2815d2d5e53f05841cef919d5 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 00:21:59 +0100 Subject: [PATCH 339/862] Use cosmos.directory API for validators/operators Includes inactive, ranks, speed and UI improvement --- scripts/base.mjs | 11 +- src/ApyClient.mjs | 16 +- src/components/Delegate.js | 245 ++++++++++++++++++------------- src/components/Delegations.js | 107 ++------------ src/components/NetworkFinder.js | 19 +-- src/components/NetworkSelect.js | 15 +- src/components/ValidatorImage.js | 6 +- src/components/ValidatorLink.js | 8 +- src/components/ValidatorName.js | 37 +++++ src/components/Validators.js | 135 ++++++++++------- src/utils/CosmosDirectory.mjs | 7 +- src/utils/Network.mjs | 33 ++--- src/utils/Operator.mjs | 42 +++--- 13 files changed, 355 insertions(+), 326 deletions(-) create mode 100644 src/components/ValidatorName.js diff --git a/scripts/base.mjs b/scripts/base.mjs index 5ea1e9a6e..695df5bda 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -121,14 +121,14 @@ export class Autostake { timeStamp('Bot address is', botAddress) + const operator = network.getOperatorByBotAddress(botAddress) + if (!operator) return timeStamp('Not an operator') + if (network.slip44 && network.slip44 !== slip44) { timeStamp("!! You are not using the preferred derivation path !!") timeStamp("!! You should switch to the correct path unless you have grants. Check the README !!") } - const operatorData = network.operators.find(el => el.botAddress === botAddress) - - if (!operatorData) return timeStamp('Not an operator') if (!network.authzSupport) return timeStamp('No Authz support') network = await Network(data) @@ -138,9 +138,6 @@ export class Autostake { const client = await network.signingClient(wallet) client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) - const validators = await network.getValidators() - const operators = network.getOperators(validators) - const operator = network.getOperatorByBotAddress(operators, botAddress) return { network, @@ -239,7 +236,7 @@ export class Autostake { const perValidatorReward = parseInt(totalRewards / validators.length) - if (perValidatorReward < client.operator.data.minimumReward) { + if (perValidatorReward < client.operator.minimumReward) { timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') return } diff --git a/src/ApyClient.mjs b/src/ApyClient.mjs index 7db5f4b88..bbdc98513 100644 --- a/src/ApyClient.mjs +++ b/src/ApyClient.mjs @@ -16,12 +16,16 @@ const ApyClient = (chain, rpcUrl, restUrl) => { const chainApr = await getChainApr(denom); let validatorApy = {}; for (const [address, validator] of Object.entries(validators)) { - const commission = validator.commission.commission_rates.rate - const operator = operators.find((el) => el.address === address) - const periodPerYear = operator && chain.authzSupport ? operator.runsPerDay() * 365 : 1; - const realApr = chainApr * (1 - commission); - const apy = (1 + realApr / periodPerYear) ** periodPerYear - 1; - validatorApy[address] = apy; + if(validator.jailed || validator.status !== 'BOND_STATUS_BONDED'){ + validatorApy[address] = 0 + }else{ + const commission = validator.commission.commission_rates.rate + const operator = operators.find((el) => el.address === address) + const periodPerYear = operator && chain.authzSupport ? operator.runsPerDay() * 365 : 1; + const realApr = chainApr * (1 - commission); + const apy = (1 + realApr / periodPerYear) ** periodPerYear - 1; + validatorApy[address] = apy; + } } return validatorApy; } diff --git a/src/components/Delegate.js b/src/components/Delegate.js index 798f2eb97..263179f6e 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -5,7 +5,7 @@ import ValidatorLink from './ValidatorLink' import Coins from './Coins' import TooltipIcon from './TooltipIcon' -import React, { useState, useRef } from 'react'; +import React, { useState } from 'react'; import { Dropdown, @@ -14,7 +14,9 @@ import { OverlayTrigger, Tooltip, Table, - Spinner + Spinner, + Tabs, + Tab } from 'react-bootstrap' import { @@ -22,12 +24,14 @@ import { } from 'react-bootstrap-icons' function Delegate(props) { + const { redelegate, undelegate, validator, delegations, operators, network, validators } = props const [show, setShow] = useState(false); - const [selectedValidator, setSelectedValidator] = useState(!props.redelegate && props.validator); + const [selectedValidator, setSelectedValidator] = useState(!redelegate && validator); + const [activeTab, setActiveTab] = useState(props.activeTab || (redelegate || undelegate) ? 'delegate' : 'profile'); const handleOpen = () => { setShow(true) - if (!props.validator || props.redelegate) { + if (!validator || redelegate) { setSelectedValidator(null) } } @@ -38,17 +42,42 @@ function Delegate(props) { } const excludeValidators = () => { - if (props.redelegate) { - return [props.validator.operator_address] - } else if (props.delegations) { - return Object.keys(props.delegations) + if (redelegate) { + return [validator.operator_address] + } else if (delegations) { + return [...Object.keys(delegations), ...operators.map(el => el.address)] } } const operator = () => { - if (!props.operators || !selectedValidator) return + if (!operators || !selectedValidator) return - return props.network.getOperator(props.operators, selectedValidator.operator_address) + return network.getOperator(selectedValidator.operator_address) + } + + const active = () => { + if(!selectedValidator) return false + + return selectedValidator.status === 'BOND_STATUS_BONDED' && !selectedValidator.jailed + } + + const status = () => { + if(!selectedValidator) return + + let status = '' + let className = '' + if(active()){ + status = 'Active' + }else{ + status = 'Inactive' + className = 'text-danger' + } + if(selectedValidator.jailed){ + status += ' (JAILED)' + className = 'text-danger' + } + + return {status} } const website = () => { @@ -67,20 +96,20 @@ function Delegate(props) { if (!selectedValidator) return const amount = parseInt(selectedValidator.tokens) - return + return } const minimumReward = () => { return { - amount: operator().data.minimumReward, - denom: props.network.denom + amount: operator().minimumReward, + denom: network.denom } } const actionText = () => { - if (props.redelegate) return 'Redelegate' - if (props.undelegate) return 'Undelegate' - if (props.validator) { + if (redelegate) return 'Redelegate' + if (undelegate) return 'Undelegate' + if (validator) { return 'Delegate' } else { return 'Add Validator' @@ -103,12 +132,12 @@ function Delegate(props) { ) return ( <> - {props.tooltip && props.validator ? ( + {props.tooltip && validator ? ( + {props.tooltip} } @@ -135,8 +164,8 @@ function Delegate(props) { {selectedValidator ? ( <> - - + + ) : actionText() } @@ -145,96 +174,110 @@ function Delegate(props) { {!selectedValidator && setSelectedValidator(selectedValidator)} />} {selectedValidator && ( - <> - - - {!!website() && ( + setActiveTab(k)} id="validator-tabs" className="mb-3"> + +
    + + {active() && ( + + + + + )} + {!active() && ( + + + + + )} + {!!website() && ( + + + + + )} - - + + - )} - - - - - {props.network.data.apyEnabled !== false && ( + {network.data.apyEnabled !== false && ( + + + + + )} + {!!securityContact() && ( + + + + + )} - - + + - )} - {!!securityContact() && ( - - + + - )} - - - - - - - - - -
    Rank#{selectedValidator.rank}
    Status{status()}
    Website{website()}
    Website{website()}Commission{selectedValidator.commission.commission_rates.rate * 100}%
    Commission{selectedValidator.commission.commission_rates.rate * 100}%
    + APY} + identifier="delegations-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is an estimate and best case scenario.

    +
    +
    +
    + {Object.keys(props.validatorApy).length > 0 + ? props.validatorApy[selectedValidator.operator_address] + ? Math.round(props.validatorApy[selectedValidator.operator_address] * 100) + "%" + : "-" + : ( + + Loading... + + )} +
    Contact{securityContact()}
    - APY} - identifier="delegations-apy" - > -
    -

    Based on commission, compounding frequency and estimated block times.

    -

    This is an estimate and best case scenario.

    -
    -
    -
    - {Object.keys(props.validatorApy).length > 0 - ? props.validatorApy[selectedValidator.operator_address] - ? Math.round(props.validatorApy[selectedValidator.operator_address] * 100) + "%" - : "" - : ( - - Loading... - - )} - Voting power{bondedTokens()}
    Contact{securityContact()}REStake + {!!operator() ? ( + {operator().runTimesString()} ( min) + ) : + } identifier={selectedValidator.operator_address} tooltip="This validator is not a REStake operator" /> + } +
    Voting power{bondedTokens()}
    REStake - {!!operator() ? ( - {operator().runTimesString()} ( min) - ) : - } identifier={selectedValidator.operator_address} tooltip="This validator is not a REStake operator" /> - } -
    -

    {selectedValidator.description.details}

    -
    -
    - {props.redelegate - ? Redelegate from - : actionText() - } -
    - - + + +

    {selectedValidator.description.details}

    + + +
    + {redelegate + ? Redelegate from + : actionText() + } +
    + +
    + )}
    diff --git a/src/components/Delegations.js b/src/components/Delegations.js index fa158ba23..f0e178eb2 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -14,14 +14,13 @@ import TooltipIcon from "./TooltipIcon"; import { Table, Button, Dropdown, Spinner } from "react-bootstrap"; import { CheckCircle, XCircle } from "react-bootstrap-icons"; +import ValidatorName from "./ValidatorName"; class Delegations extends React.Component { constructor(props) { super(props); - this.state = { operatorGrants: {}, validatorLoading: {}, validatorImages: {}, validatorApy: {} }; + this.state = { operatorGrants: {}, validatorLoading: {}, validatorApy: {} }; - this.getValidatorImage = this.getValidatorImage.bind(this); - this.loadValidatorImages = this.loadValidatorImages.bind(this); this.setError = this.setError.bind(this); this.setClaimLoading = this.setClaimLoading.bind(this); this.onClaimRewards = this.onClaimRewards.bind(this); @@ -70,12 +69,6 @@ class Delegations extends React.Component { async refresh() { this.getRewards(); this.calculateApy(); - if(this.props.operators){ - this.loadValidatorImages(this.props.network, _.compact(this.props.operators.map(el => el.validatorData))) - this.loadValidatorImages(this.props.network, _.omit(this.props.validators, this.props.operators.map(el => el.address))) - }else{ - this.loadValidatorImages(this.props.network, this.props.validators) - } this.refreshInterval(); } @@ -114,6 +107,7 @@ class Delegations extends React.Component { ).then(validatorApy => { this.setState({ validatorApy }); }, error => { + console.log(error) this.setState({ error: "Failed to get APY. Please refresh" }); }) } @@ -121,12 +115,7 @@ class Delegations extends React.Component { async getGrants() { if(!this.authzSupport() || !this.props.operators.length) return - const ordered = [...this.props.operators].sort(el => { - if(!this.props.delegations) return 0 - - return this.props.delegations[el.address] ? -1 : 0 - }) - const calls = ordered.map((operator) => { + const calls = this.orderedOperators().map((operator) => { return () => { const { botAddress, address } = operator; if(!this.props.operators.includes(operator)) return; @@ -171,65 +160,6 @@ class Delegations extends React.Component { } } - getValidatorImage(network, validatorAddress, expireCache){ - const images = this.state.validatorImages[network.name] || {} - if(images[validatorAddress]){ - return images[validatorAddress] - } - return this.getValidatorImageCache(validatorAddress, expireCache) - } - - getValidatorImageCache(validatorAddress, expireCache){ - const cache = localStorage.getItem(validatorAddress) - if(!cache) return - - let cacheData = {} - try { - cacheData = JSON.parse(cache) - } catch { - cacheData.url = cache - } - if(!cacheData.url) return - if(!expireCache) return cacheData.url - - const cacheTime = cacheData.time && new Date(cacheData.time) - if(!cacheData.time) return - - const expiry = new Date() - 1000 * 60 * 60 * 24 * 3 - if(cacheTime >= expiry) return cacheData.url - } - - async loadValidatorImages(network, validators) { - this.setState((state, props) => ({ - validatorImages: _.set(state.validatorImages, network.name, state.validatorImages[network.name] || {}) - })); - const calls = Object.values(validators).map(validator => { - return () => { - if(validator.description.identity && !this.getValidatorImage(network, validator.operator_address, true)){ - return fetch("https://keybase.io/_/api/1.0/user/lookup.json?fields=pictures&key_suffix=" + validator.description.identity) - .then((response) => { - return response.json(); - }).then((data) => { - if(data.them && data.them[0] && data.them[0].pictures){ - const imageUrl = data.them[0].pictures.primary.url - this.setState((state, props) => ({ - validatorImages: _.set(state.validatorImages, [network.name, validator.operator_address], imageUrl) - })); - localStorage.setItem(validator.operator_address, JSON.stringify({url: imageUrl, time: +new Date()})) - } - }, error => { }) - }else{ - return null - } - } - }) - const batchCalls = _.chunk(calls, 1); - - for (const batchCall of batchCalls) { - await Promise.allSettled(batchCall.map(call => call())) - } - } - onGrant(operator) { const operatorGrant = { grantsValid: true, @@ -317,9 +247,11 @@ class Delegations extends React.Component { } orderedOperators() { - return _.sortBy(this.props.operators, ({ address }) => - this.props.delegations[address] ? 0 : 1 - ); + return _.sortBy(this.props.operators, ({ address }) => { + if(!this.props.delegations) return 0 + + return this.props.delegations[address] ? 0 : 1 + }); } regularDelegations() { @@ -413,19 +345,16 @@ class Delegations extends React.Component { }; const minimumReward = operator && { - amount: operator.data.minimumReward, + amount: operator.minimumReward, denom: this.props.network.denom, }; return ( + {validator.rank || '-'} @@ -437,12 +366,11 @@ class Delegations extends React.Component { validator={validator} validatorApy={this.state.validatorApy} operators={this.props.operators} - getValidatorImage={this.getValidatorImage} availableBalance={this.props.balance} stargateClient={this.props.stargateClient} onDelegate={this.onClaimRewards} > - {validator.description.moniker} + @@ -504,7 +432,7 @@ class Delegations extends React.Component { {Object.keys(this.state.validatorApy).length > 0 ? this.state.validatorApy[validatorAddress] ? {Math.round(this.state.validatorApy[validatorAddress] * 100) + "%"} - : "" + : "-" : ( Loading... @@ -607,13 +535,13 @@ class Delegations extends React.Component { )}
    @@ -629,7 +557,6 @@ class Delegations extends React.Component { (this.props.delegations[validatorAddress] || {}) .balance } - getValidatorImage={this.getValidatorImage} stargateClient={this.props.stargateClient} onDelegate={this.onClaimRewards} /> @@ -644,7 +571,6 @@ class Delegations extends React.Component { (this.props.delegations[validatorAddress] || {}) .balance } - getValidatorImage={this.getValidatorImage} stargateClient={this.props.stargateClient} onDelegate={this.onClaimRewards} /> @@ -656,13 +582,13 @@ class Delegations extends React.Component { variant="primary" size="sm" tooltip="Delegate to enable REStake" + activeTab="delegate" network={this.props.network} address={this.props.address} validator={validator} validatorApy={this.state.validatorApy} operators={this.props.operators} availableBalance={this.props.balance} - getValidatorImage={this.getValidatorImage} stargateClient={this.props.stargateClient} onDelegate={this.onClaimRewards} /> @@ -744,7 +670,6 @@ class Delegations extends React.Component { operators={this.props.operators} validators={this.props.validators} validatorApy={this.state.validatorApy} - getValidatorImage={this.getValidatorImage} availableBalance={this.props.balance} stargateClient={this.props.stargateClient} onDelegate={this.props.onAddValidator} @@ -761,6 +686,7 @@ class Delegations extends React.Component { + + {isActive && ( + + )} + {!isActive && ( + + )} - {props.network.data.apyEnabled !== false && ( + {isActive && props.network.data.apyEnabled !== false && ( )} + + + + {!active() && ( From b05f20f8864f406bf5b6aaf038dfd56388fc2813 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 00:44:46 +0100 Subject: [PATCH 344/862] Adjust table size --- src/components/Delegate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Delegate.js b/src/components/Delegate.js index 4a0c2acb3..d3c65edff 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -186,7 +186,7 @@ function Delegate(props) { setActiveTab(k)} id="validator-tabs" className="mb-3">
    # Validator REStake @@ -811,7 +737,6 @@ class Delegations extends React.Component { address={this.props.address} validators={this.props.validators} validatorApy={this.state.validatorApy} - getValidatorImage={this.getValidatorImage} delegations={this.props.delegations} availableBalance={this.props.balance} stargateClient={this.props.stargateClient} diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 89efafe4f..e40a9833b 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -39,12 +39,11 @@ function NetworkFinder() { return _.compact(networks).reduce((a, v) => ({ ...a, [v.name]: v}), {}) } - const changeNetwork = (network, validators) => { - const operators = Object.keys(validators).length ? network.getOperators(validators) : [] + const changeNetwork = (network) => { setState({ network: network, - validators: validators, - operators: operators + validators: network.getValidators(), + operators: network.getOperators() }) navigate("/" + network.name); @@ -89,14 +88,10 @@ function NetworkFinder() { if(state.error) return if(!state.network || !state.network.connected) return if(state.network && (!Object.keys(state.validators).length)){ - state.network.getValidators().then(validators => { - setState({ - validators, - operators: state.network.getOperators(validators), - loading: false - }) - }, error => { - setState({validators: {}, loading: false}) + setState({ + validators: state.network.getValidators(), + operators: state.network.getOperators(), + loading: false }) } }, [state.network]) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index c8c5f6c42..0c9413eb8 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -80,19 +80,10 @@ function NetworkSelect(props) { if(data){ setLoading(true) setError(false) - Network(data, true).then(network => { - setSelectedNetwork(network) - }) Network(data).then(network => { setSelectedNetwork(network) - setValidators({}) - network.getValidators().then(data => { - setValidators(data) - setLoading(false) - }, (error) => { - setError('Unable to connect to this network currently. Try again later.') - setLoading(false) - }) + setValidators(network.getValidators()) + setLoading(false) }, (error) => { setError('Unable to connect to this network currently. Try again later.') setLoading(false) @@ -163,7 +154,7 @@ function NetworkSelect(props) { {!error && !selectedNetwork.authzSupport &&

    This network does not support Authz yet. You can manually stake and compound for now

    } - {!error && selectedNetwork.authzSupport && selectedNetwork.getOperators(validators).length < 1 && + {!error && selectedNetwork.authzSupport && selectedNetwork.operators.length < 1 &&

    This network supports Authz but there are no operators just yet. You can manually REStake for now

    } {!loading diff --git a/src/components/ValidatorImage.js b/src/components/ValidatorImage.js index b1c225a17..45d793be7 100644 --- a/src/components/ValidatorImage.js +++ b/src/components/ValidatorImage.js @@ -1,10 +1,12 @@ function ValidatorImage(props) { + const { validator } = props const className = props.className || '' const defaultSize = !props.width || props.width > 30 ? 'lg' : 'sm' + const imageUrl = validator.mintscan_image || validator.keybase_image return ( <> - {props.imageUrl - ? + {imageUrl && imageUrl !== '' + ? : } diff --git a/src/components/ValidatorLink.js b/src/components/ValidatorLink.js index df4757bba..4d9d16e2d 100644 --- a/src/components/ValidatorLink.js +++ b/src/components/ValidatorLink.js @@ -1,6 +1,8 @@ +import ValidatorName from "./ValidatorName" + function ValidatorLink(props) { let validator = props.validator - if(!validator && props.operator) validator = props.operator.validatorData + if(!validator && props.operator) validator = props.operator if(!validator) return props.fallback || null const website = () => { @@ -11,12 +13,12 @@ function ValidatorLink(props) { } if(!website()){ - return props.children || validator.description.moniker + return props.children || } return ( - {props.children || validator.description.moniker} + {props.children || } ); } diff --git a/src/components/ValidatorName.js b/src/components/ValidatorName.js new file mode 100644 index 000000000..d1b547a77 --- /dev/null +++ b/src/components/ValidatorName.js @@ -0,0 +1,37 @@ +import { + OverlayTrigger, + Tooltip +} from 'react-bootstrap' + +function ValidatorName(props) { + const { validator, hideWarning, fallback } = props + if(!validator) return fallback || null + const { moniker } = validator.description + + let warning = false + let warningClass + + if(validator.status === 'BOND_STATUS_UNBONDED') warning = 'Validator is not in the active set' + if(validator.jailed) warning = 'Validator is jailed' + + if(warning && !hideWarning) warningClass = 'text-danger text-decoration-line-through' + + return ( + <> + {warning + ? ( + + {warning} + + } + >{moniker} + ) : moniker } + + ); +} + +export default ValidatorName \ No newline at end of file diff --git a/src/components/Validators.js b/src/components/Validators.js index 056db287d..c26fe8d1a 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -6,62 +6,80 @@ import TooltipIcon from './TooltipIcon' import { Table, - Button + Button, + Spinner, + Tab, + Tabs } from 'react-bootstrap' import { CheckCircle, XCircle } from 'react-bootstrap-icons' +import ValidatorName from './ValidatorName'; function Validators(props) { + const { validators, operators } = props + const [filter, setFilter] = useState() + const [results, setResults] = useState([]) - function filteredOperators(){ - const results = _.pick(props.validators, props.operators.map(el => el.address)) - return Object.entries(filteredResults(results)) - } + useEffect(() => { + if (!validators) return - function filteredValidators(){ - const results = _.omit(props.validators, props.operators.map(el => el.address)) - return Object.entries(filteredResults(results)) - } + setResults(filteredValidators()) + }, [filter, validators, operators]); function filterValidators(event){ setFilter(event.target.value) } - function filteredResults(validators){ - if(!validators) return {} - - if(props.exclude) validators = _.omit(validators, props.exclude) + function filteredValidators(){ + let searchResults = validators + if (props.exclude) searchResults = _.omit(searchResults, props.exclude) - if(!filter || filter === '') return validators + if (!filter || filter === '') return Object.values(searchResults) const searcher = new FuzzySearch( - Object.values(validators), ['description.moniker'], - {sort: true} + Object.values(searchResults), ['description.moniker'], + { sort: true } ) - const results = searcher.search(filter) - return results.reduce((a, v) => ({ ...a, [v.operator_address]: v}), {}) + return searcher.search(filter) + } + + function statusResults(status){ + return results.filter(result => { + if(status === 'active'){ + return result.status === 'BOND_STATUS_BONDED' + }else{ + return result.status !== 'BOND_STATUS_BONDED' + } + }) } - function renderItem(item, isOperator){ + function renderItem(item, isActive){ const address = item.operator_address - let variant = isOperator ? 'warning' : null + const isOperator = operators.map(el => el.address).includes(address) + let variant = null variant = variant ? 'table-' + variant : '' return (
    {item.rank}
    - +
    - {item.description.moniker} +
    {item.jailed ? 'Jailed' : ''} {isOperator ? } identifier={address} @@ -70,7 +88,7 @@ function Validators(props) { tooltip="This validator is not a REStake operator" /> } {Object.keys(props.validatorApy).length > 0 ? props.validatorApy[address] @@ -95,35 +113,48 @@ function Validators(props) { return ( <> - {(filteredOperators().length > 0 || filteredValidators().length > 0) && - - - - - - {props.network.apyEnabled !== false && ( - - )} - - - {filteredOperators().map(([validator_address, item], i) => renderItem(item, true))} - {filteredValidators().map(([validator_address, item], i) => renderItem(item))} - -
    ValidatorREStake - APY} - identifier="validators-apy" - > -
    -

    Based on commission, compounding frequency and estimated block times.

    -

    This is an estimate and best case scenario.

    -
    -
    -
    - } - {filteredOperators().length < 1 && filteredValidators().length < 1 && -

    No results found

    - } + + {['active', 'inactive'].map(status => { + return ( + + {statusResults(status).length > 0 && + + + + {status === 'active' && ( + + )} + + {status !== 'active' && ( + + )} + + {status === 'active' && props.network.apyEnabled !== false && ( + + )} + + + {statusResults(status).map(item => renderItem(item, status === 'active'))} + +
    #ValidatorStatusREStake + APY} + identifier="validators-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is an estimate and best case scenario.

    +
    +
    +
    + } + {statusResults(status).length < 1 && +

    No results found

    + } +
    + ) + })} +
    ) } diff --git a/src/utils/CosmosDirectory.mjs b/src/utils/CosmosDirectory.mjs index e49f9e0f8..38d176801 100644 --- a/src/utils/CosmosDirectory.mjs +++ b/src/utils/CosmosDirectory.mjs @@ -33,10 +33,9 @@ function CosmosDirectory(){ .then(res => res.data) } - function getOperators(chainName){ + function getValidators(chainName){ return axios.get(validatorsUrl + '/chains/' + chainName) - .then(res => res.data) - .then(data => data.validators.filter(el => el.restake )) + .then(res => res.data.validators) } function getOperatorCounts(){ @@ -59,7 +58,7 @@ function CosmosDirectory(){ getChains, getChainData, getTokenData, - getOperators, + getValidators, getOperatorCounts } } diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index a466e161c..5506c652c 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -14,13 +14,9 @@ const Network = async (data, withoutQueryClient) => { const chain = await Chain(data) const directory = CosmosDirectory() - const operators = data.operators || (await directory.getOperators(data.name)).map(el => { - return { - address: el.address, - botAddress: el.restake.address, - runTime: el.restake.run_time, - minimumReward: el.restake.minimum_reward - } + const validators = await directory.getValidators(data.name) + const operators = data.operators || validators.filter(el => el.restake).map(el => { + return Operator(el) }) const rpcUrl = data.rpcUrl || directory.rpcUrl(data.name) @@ -41,19 +37,16 @@ const Network = async (data, withoutQueryClient) => { const apyClient = queryClient && ApyClient(chain, queryClient.rpcUrl, queryClient.restUrl) - const getOperator = (operators, operatorAddress) => { + const getOperator = (operatorAddress) => { return operators.find(elem => elem.address === operatorAddress) } - const getOperatorByBotAddress = (operators, botAddress) => { + const getOperatorByBotAddress = (botAddress) => { return operators.find(elem => elem.botAddress === botAddress) } - const getOperators = (validators) => { - return sortOperators().map(operator => { - const validator = validators[operator.address] - return Operator(operator, validator) - }) + const getOperators = () => { + return sortOperators() } const sortOperators = () => { @@ -66,8 +59,13 @@ const Network = async (data, withoutQueryClient) => { const getValidators = (opts) => { opts = opts || {} - opts.status = opts.status || 'BOND_STATUS_BONDED' - return queryClient.getAllValidators(150, opts) + return validators.filter(validator => { + if(opts.status) return validator.status === opts.status + return true + }).reduce( + (a, v) => ({ ...a, [v.operator_address]: v }), + {} + ) } return { @@ -88,8 +86,9 @@ const Network = async (data, withoutQueryClient) => { testAddress: data.testAddress, restUrl: queryClient && queryClient.restUrl, rpcUrl: queryClient && queryClient.rpcUrl, - operators: operators, authzSupport: chain.authzSupport, + validators, + operators, data, chain, queryClient, diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index 9683c4d02..be5c68039 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -1,10 +1,13 @@ import moment from 'moment' import parse from 'parse-duration' -const Operator = (data, validatorData) => { +const Operator = (data) => { + const { address } = data + const botAddress = data.restake.address + const runTime = data.restake.run_time + const minimumReward = data.restake.minimum_reward function runsPerDay() { - const { runTime } = data if(Array.isArray(runTime)){ return runTime.length }else{ @@ -17,8 +20,8 @@ const Operator = (data, validatorData) => { } function runTimes() { - if(Array.isArray(data.runTime)) return data.runTime - return [data.runTime] + if(Array.isArray(runTime)) return runTime + return [runTime] } function runTimesString(){ @@ -30,29 +33,29 @@ const Operator = (data, validatorData) => { } function frequency() { - if(Array.isArray(data.runTime)){ - return data.runTime.length + 'x per day' + if(Array.isArray(runTime)){ + return runTime.length + 'x per day' }else{ - if(data.runTime.startsWith('every')){ - return data.runTime.replace('every ', '') + if(runTime.startsWith('every')){ + return runTime.replace('every ', '') } return 'daily' } } function nextRun() { - if (!data.runTime) return + if (!runTime) return - if (Array.isArray(data.runTime)) { - return data.runTime + if (Array.isArray(runTime)) { + return runTime .map(el => nextRunFromRuntime(el)) .sort((a, b) => a.valueOf() - b.valueOf()) .find(el => el.isAfter()); } else { - if(data.runTime.startsWith('every')){ - return nextRunFromInterval(data.runTime) + if(runTime.startsWith('every')){ + return nextRunFromInterval(runTime) } - return nextRunFromRuntime(data.runTime) + return nextRunFromRuntime(runTime) } } @@ -69,11 +72,12 @@ const Operator = (data, validatorData) => { } return { - address: data.address, - botAddress: data.botAddress, - moniker: validatorData && validatorData.description.moniker, - description: validatorData && validatorData.description, - validatorData, + address, + botAddress, + runTime, + minimumReward, + moniker: data.description?.moniker, + description: data.description, data, nextRun, frequency, From 4e624f95d30e10409153f45a1d26e8057b149601 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 00:24:06 +0100 Subject: [PATCH 340/862] Error when local JSON is invalid or wrong network --- scripts/base.mjs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 5ea1e9a6e..1abc6c30d 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -26,7 +26,9 @@ export class Autostake { } async run(networkName){ - const calls = this.getNetworksData().map(data => { + const networks = this.getNetworksData() + if(networkName && !networks.map(el => el.name).includes(networkName)) return timeStamp('Invalid network name:', networkName) + const calls = networks.map(data => { return async () => { if(networkName && data.name !== networkName) return if(data.enabled === false) return @@ -335,11 +337,16 @@ export class Autostake { getNetworksData() { const networksData = fs.readFileSync('src/networks.json'); const networks = JSON.parse(networksData); + const networkNames = networks.map(el => el.name) try { const overridesData = fs.readFileSync('src/networks.local.json'); - const overrides = overridesData && JSON.parse(overridesData) + const overrides = overridesData && JSON.parse(overridesData) || {} + Object.keys(overrides).forEach(key => { + if(!networkNames.includes(key)) timeStamp('Invalid key in networks.local.json:', key) + }) return overrideNetworks(networks, overrides) } catch { + timeStamp('Failed to parse networks.local.json, check JSON is valid') return networks } } From f0ab421ccb5118312418ebcaa1958f4e7d72ef88 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 00:24:19 +0100 Subject: [PATCH 341/862] Reduce decimals for larger numbers --- src/components/Coins.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Coins.js b/src/components/Coins.js index 8e0cbaf29..0a8b5d9bd 100644 --- a/src/components/Coins.js +++ b/src/components/Coins.js @@ -5,7 +5,8 @@ function Coins(props) { if (!decimals) { decimals = 6 } - return _.round(coins.amount / Math.pow(10, decimals), 6) + const precision = coins.amount >= (100 * Math.pow(10, decimals)) ? 2 : 6 + return _.round(coins.amount / Math.pow(10, decimals), precision) } function denom(coins){ From 5bca586f3e5575ce9ad540fba4ad956e2dfe291a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 00:35:51 +0100 Subject: [PATCH 342/862] Pre-load network when changing --- src/components/NetworkSelect.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index 0c9413eb8..90af72696 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -80,13 +80,16 @@ function NetworkSelect(props) { if(data){ setLoading(true) setError(false) - Network(data).then(network => { + Network(data, true).then(network => { setSelectedNetwork(network) - setValidators(network.getValidators()) - setLoading(false) - }, (error) => { - setError('Unable to connect to this network currently. Try again later.') - setLoading(false) + Network(data).then(network => { + setSelectedNetwork(network) + setValidators(network.getValidators()) + setLoading(false) + }, (error) => { + setError('Unable to connect to this network currently. Try again later.') + setLoading(false) + }) }) } } From 36a75e4b7e21b6b0b54cadb58c988392e892eb37 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 00:40:24 +0100 Subject: [PATCH 343/862] Show validator address --- src/components/Delegate.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/Delegate.js b/src/components/Delegate.js index 263179f6e..4a0c2acb3 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -193,6 +193,10 @@ function Delegate(props) {
    #{selectedValidator.rank}
    Address{selectedValidator.operator_address}
    Status
    - + {active() && ( @@ -253,7 +253,7 @@ function Delegate(props) { -
    RankREStake {!!operator() ? ( - {operator().runTimesString()} ( min) + {operator().runTimesString()} ( min) ) : } identifier={selectedValidator.operator_address} tooltip="This validator is not a REStake operator" /> } From 75c887ccf4cfa7a30e0a8b3173e8baa2620b1af1 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 00:47:48 +0100 Subject: [PATCH 345/862] Hide rewards for small screen size --- src/components/Delegations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index fa158ba23..7696bb7be 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -520,7 +520,7 @@ class Delegations extends React.Component { /> + {denomRewards && ( Date: Fri, 8 Apr 2022 01:00:13 +0100 Subject: [PATCH 346/862] Improve UX to delegate form --- src/components/Delegate.js | 15 +++++++++++++-- src/components/DelegateForm.js | 14 ++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/components/Delegate.js b/src/components/Delegate.js index d3c65edff..2b6a9b850 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -1,3 +1,5 @@ +import React, { useState, useEffect } from 'react'; + import DelegateForm from './DelegateForm' import Validators from './Validators' import ValidatorImage from './ValidatorImage' @@ -5,8 +7,6 @@ import ValidatorLink from './ValidatorLink' import Coins from './Coins' import TooltipIcon from './TooltipIcon' -import React, { useState } from 'react'; - import { Dropdown, Button, @@ -29,6 +29,12 @@ function Delegate(props) { const [selectedValidator, setSelectedValidator] = useState(!redelegate && validator); const [activeTab, setActiveTab] = useState(props.activeTab || (redelegate || undelegate) ? 'delegate' : 'profile'); + useEffect(() => { + if(props.activeTab != activeTab){ + setActiveTab(props.activeTab) + } + }, [props.activeTab]) + const handleOpen = () => { setShow(true) if (!validator || redelegate) { @@ -262,6 +268,11 @@ function Delegate(props) {

    {selectedValidator.description.details}

    +

    + +

    diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index 8336b3340..cdc4c4388 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -140,12 +140,14 @@ class DelegateForm extends React.Component { Memo - {!this.state.loading - ? - : - } +

    + {!this.state.loading + ? + : + } +

    ) From 7cd0fdff721dee769c40c8e8208bb71273834e57 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 01:50:49 +0100 Subject: [PATCH 347/862] Add powered by cosmos.directory link to footer Only shows if using cosmos.directory REST/RPC --- scripts/base.mjs | 4 +--- src/components/App.js | 13 ++++++++++--- src/utils/Network.mjs | 3 +++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 1741e487e..4121c2b8c 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -42,13 +42,11 @@ export class Autostake { if(!client) return timeStamp('Skipping') - const { restUrl, rpcUrl } = client.network + const { restUrl, rpcUrl, usingDirectory } = client.network timeStamp('Using REST URL', restUrl) timeStamp('Using RPC URL', rpcUrl) - const usingDirectory = !![restUrl, rpcUrl].find(el => el.match("cosmos.directory")) - if(usingDirectory){ timeStamp('You are using public nodes, script may fail with many delegations. Check the README to use your own') timeStamp('Delaying briefly to reduce load...') diff --git a/src/components/App.js b/src/components/App.js index 0a8211ab3..cb5a19a50 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -259,9 +259,16 @@ class App extends React.Component { Powered by Akash - - Built with 💚  by ECO Stake 🌱 - +
    + + Built with 💚 by ECO Stake 🌱 + + {this.props.network?.usingDirectory && ( + + Powered by ⚛ cosmos.directory + + )} +

    Star diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 5506c652c..d59c44a18 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -22,6 +22,8 @@ const Network = async (data, withoutQueryClient) => { const rpcUrl = data.rpcUrl || directory.rpcUrl(data.name) const restUrl = data.restUrl || directory.restUrl(data.name) + const usingDirectory = !![restUrl, rpcUrl].find(el => el.match("cosmos.directory")) + let queryClient if(!withoutQueryClient){ queryClient = await QueryClient(chain.chainId, rpcUrl, restUrl) @@ -92,6 +94,7 @@ const Network = async (data, withoutQueryClient) => { data, chain, queryClient, + usingDirectory, getApy: apyClient && apyClient.getApy, signingClient, getValidators, From 9c8a89d98c16cdbe8920c24206d93d136f83d2d5 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 14:03:37 +0100 Subject: [PATCH 348/862] Move Bugsnag key to env to avoid tracking forks --- src/index.js | 55 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/index.js b/src/index.js index 3fb3e9152..43195106c 100644 --- a/src/index.js +++ b/src/index.js @@ -13,29 +13,40 @@ import NetworkFinder from './components/NetworkFinder' import './index.css'; import reportWebVitals from './utils/reportWebVitals'; -Bugsnag.start({ - apiKey: '5cda10bb1c98f351cd0b722a1535d8c2', - plugins: [new BugsnagPluginReact()], - enabledReleaseStages: [ 'production', 'staging' ], - releaseStage: process.env.NODE_ENV -}) +const app = ( + + + + } /> + } /> + + + +) -const ErrorBoundary = Bugsnag.getPlugin('react') - .createErrorBoundary(React) - -ReactDOM.render( - - - - - } /> - } /> - - - - , - document.getElementById('root') -); +if (process.env.BUGSNAG_KEY) { + Bugsnag.start({ + apiKey: process.env.BUGSNAG_KEY, + plugins: [new BugsnagPluginReact()], + enabledReleaseStages: ['production', 'staging'], + releaseStage: process.env.NODE_ENV + }) + + const ErrorBoundary = Bugsnag.getPlugin('react') + .createErrorBoundary(React) + + ReactDOM.render( + + {app} + , + document.getElementById('root') + ); +}else{ + ReactDOM.render( + app, + document.getElementById('root') + ); +} // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) From 07f0f93b1bb8834fd1986e96df2a0f33ccdcec65 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 14:05:43 +0100 Subject: [PATCH 349/862] Adjust footer --- src/components/App.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index cb5a19a50..10d85f0f6 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -260,12 +260,12 @@ class App extends React.Component {

    From 8d6de3ab46d23f2976148d3fff5b43c5cd080ca5 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Apr 2022 14:51:00 +0100 Subject: [PATCH 350/862] Set env vars at build time --- .github/workflows/publish.yml | 2 +- Dockerfile.production | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6baba427c..eab95b322 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,7 +13,7 @@ jobs: - name: build run: | echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u "${{ secrets.GHCR_USER }}" --password-stdin - docker build -f Dockerfile.production . --tag ghcr.io/${{ github.repository }}:${GITHUB_REF#refs/tags/} + docker build -f Dockerfile.production . --tag ghcr.io/${{ github.repository }}:${GITHUB_REF#refs/tags/} --build-arg BUGSNAG_KEY="${{ secrets.BUGSNAG_KEY }}" docker tag ghcr.io/${{ github.repository }}:${GITHUB_REF#refs/tags/} ghcr.io/${{ github.repository }}:latest docker push ghcr.io/${{ github.repository }}:${GITHUB_REF#refs/tags/} docker push ghcr.io/${{ github.repository }}:latest diff --git a/Dockerfile.production b/Dockerfile.production index 1ba6ac656..f9412c904 100644 --- a/Dockerfile.production +++ b/Dockerfile.production @@ -5,6 +5,9 @@ WORKDIR /app COPY package*.json ./ RUN npm ci COPY . ./ + +ARG BUGSNAG_KEY +ENV BUGSNAG_KEY=${BUGSNAG_KEY} ENV DIRECTORY_PROTOCOL=https ENV DIRECTORY_DOMAIN=cosmos.directory RUN npm run build From 69037f1d8b0b50c279860cfdaea34bb915282f59 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Apr 2022 00:59:50 +0100 Subject: [PATCH 351/862] Avoid JS integers for potentially large numbers Change default fee to a factor of chain decimals --- package-lock.json | 148 +++++++++++++++++++++++++++++++-- package.json | 1 + scripts/base.mjs | 7 +- src/components/ClaimRewards.js | 12 +-- src/components/Delegate.js | 2 +- src/components/DelegateForm.js | 18 ++-- src/components/Delegations.js | 3 +- src/utils/Network.mjs | 6 +- src/utils/SigningClient.mjs | 21 +++-- 9 files changed, 188 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 379b3cb59..a39608d7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "cosmjs-types": "^0.4.1", "dotenv": "^16.0.0", "fuzzy-search": "^3.2.1", + "mathjs": "^10.4.3", "moment": "^2.29.1", "parse-duration": "^1.0.2", "path": "^0.12.7", @@ -343,9 +344,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", - "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -6163,6 +6164,18 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/complex.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.0.tgz", + "integrity": "sha512-RdcrDz7YynXp/YXGwXIZ4MtmxXXniT5WmLFRX93cuXUX+0geWAqB8l1BoLXF+3BkzviVzHlpw27P9ow7MvlcmA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -6521,6 +6534,11 @@ } } }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + }, "node_modules/decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -6745,6 +6763,11 @@ "node": ">=6" } }, + "node_modules/escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -7240,6 +7263,18 @@ "node": ">= 0.12" } }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7704,6 +7739,11 @@ "optional": true, "peer": true }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" + }, "node_modules/jest-diff": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", @@ -8112,6 +8152,28 @@ "lz-string": "bin/bin.js" } }, + "node_modules/mathjs": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.4.3.tgz", + "integrity": "sha512-C50lWorCOplBec9Ik5fzhHuOx4G4+mtdz3r1G2I1/r8yj+CpYFXLXNqTdg59oKmIF1tKcIzpxlC4s2dGL7f3pg==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "complex.js": "^2.1.0", + "decimal.js": "^10.3.1", + "escape-latex": "^1.2.0", + "fraction.js": "^4.2.0", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^2.1.0" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -9277,6 +9339,11 @@ "object-assign": "^4.1.1" } }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -9557,6 +9624,11 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -9666,6 +9738,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", + "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==", + "engines": { + "node": ">= 10" + } + }, "node_modules/uncontrollable": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", @@ -10209,9 +10289,9 @@ } }, "@babel/runtime": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", - "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -14387,6 +14467,11 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "complex.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.0.tgz", + "integrity": "sha512-RdcrDz7YynXp/YXGwXIZ4MtmxXXniT5WmLFRX93cuXUX+0geWAqB8l1BoLXF+3BkzviVzHlpw27P9ow7MvlcmA==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -14715,6 +14800,11 @@ "ms": "2.1.2" } }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -14910,6 +15000,11 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, + "escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -15277,6 +15372,11 @@ "mime-types": "^2.1.12" } }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -15640,6 +15740,11 @@ "optional": true, "peer": true }, + "javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" + }, "jest-diff": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", @@ -15973,6 +16078,22 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=" }, + "mathjs": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.4.3.tgz", + "integrity": "sha512-C50lWorCOplBec9Ik5fzhHuOx4G4+mtdz3r1G2I1/r8yj+CpYFXLXNqTdg59oKmIF1tKcIzpxlC4s2dGL7f3pg==", + "requires": { + "@babel/runtime": "^7.17.8", + "complex.js": "^2.1.0", + "decimal.js": "^10.3.1", + "escape-latex": "^1.2.0", + "fraction.js": "^4.2.0", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^2.1.0" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -16902,6 +17023,11 @@ "object-assign": "^4.1.1" } }, + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -17115,6 +17241,11 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -17204,6 +17335,11 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "typed-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", + "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==" + }, "uncontrollable": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", diff --git a/package.json b/package.json index 6df288b0c..0e965568a 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "cosmjs-types": "^0.4.1", "dotenv": "^16.0.0", "fuzzy-search": "^3.2.1", + "mathjs": "^10.4.3", "moment": "^2.29.1", "parse-duration": "^1.0.2", "path": "^0.12.7", diff --git a/scripts/base.mjs b/scripts/base.mjs index 4121c2b8c..4a09bcf4f 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -6,6 +6,7 @@ import {timeStamp, mapSync, executeSync, overrideNetworks} from '../src/utils/He import { coin } from '@cosmjs/stargate' +import { divide, bignumber, floor } from 'mathjs' import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx.js"; import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx.js"; @@ -234,7 +235,7 @@ export class Autostake { async getAutostakeMessage(client, address, validators) { const totalRewards = await this.totalRewards(client, address, validators) - const perValidatorReward = parseInt(totalRewards / validators.length) + const perValidatorReward = floor(divide(totalRewards, validators.length)) if (perValidatorReward < client.operator.minimumReward) { timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') @@ -303,7 +304,7 @@ export class Autostake { value: MsgDelegate.encode(MsgDelegate.fromPartial({ delegatorAddress: address, validatorAddress: validatorAddress, - amount: coin(amount, denom) + amount: coin(amount.toString(), denom) })).finish() }] } @@ -316,7 +317,7 @@ export class Autostake { const total = Object.values(rewards).reduce((sum, item) => { const reward = item.reward.find(el => el.denom === client.network.denom) if (reward && validators.includes(item.validator_address)) { - return sum + parseInt(reward.amount) + return sum + bignumber(reward.amount) } return sum }, 0) diff --git a/src/components/ClaimRewards.js b/src/components/ClaimRewards.js index ada069ce1..0c38f4ec9 100644 --- a/src/components/ClaimRewards.js +++ b/src/components/ClaimRewards.js @@ -11,6 +11,8 @@ import { Badge } from 'react-bootstrap' +import { add, subtract, multiply, divide, bignumber, floor } from 'mathjs' + function ClaimRewards(props) { async function claim(){ props.setLoading(true) @@ -30,16 +32,16 @@ function ClaimRewards(props) { const fee = props.stargateClient.getFee(gas) const feeAmount = fee.amount[0].amount - const totalReward = props.validatorRewards.reduce((sum, validatorReward) => sum + validatorReward.reward, 0); + const totalReward = props.validatorRewards.reduce((sum, validatorReward) => add(sum, bignumber(validatorReward.reward)), 0); const adjustedValidatorRewards = props.validatorRewards.map(validatorReward => { - const shareOfFee = (validatorReward.reward / totalReward) * feeAmount; // To take a proportional amount from each validator relative to total reward + const shareOfFee = multiply(divide(validatorReward.reward, totalReward), feeAmount); // To take a proportional amount from each validator relative to total reward return { validatorAddress: validatorReward.validatorAddress, - reward: validatorReward.reward - shareOfFee, + reward: subtract(validatorReward.reward, shareOfFee), } }) - signAndBroadcast = props.stargateClient.signAndBroadcastWithoutBalanceCheck + signAndBroadcast = props.stargateClient.signAndBroadcastWithoutBalanceCheck if(!props.commission && adjustedValidatorRewards.some(validatorReward => validatorReward.reward <= 0)) { props.setLoading(false) @@ -96,7 +98,7 @@ function ClaimRewards(props) { value: MsgDelegate.fromPartial({ delegatorAddress: props.address, validatorAddress: validatorReward.validatorAddress, - amount: coin(parseInt(validatorReward.reward), props.network.denom) + amount: coin(floor(validatorReward.reward).toString(), props.network.denom) }) }) } diff --git a/src/components/Delegate.js b/src/components/Delegate.js index 2b6a9b850..7dd449b77 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -101,7 +101,7 @@ function Delegate(props) { const bondedTokens = () => { if (!selectedValidator) return - const amount = parseInt(selectedValidator.tokens) + const amount = selectedValidator.tokens return } diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index cdc4c4388..8158bb369 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -11,6 +11,8 @@ import { Alert } from 'react-bootstrap' +import { pow, multiply, divide, floor, subtract } from 'mathjs' + class DelegateForm extends React.Component { constructor(props) { super(props); @@ -40,7 +42,10 @@ class DelegateForm extends React.Component { const memo = this.state.memo const client = this.props.stargateClient - let messages = this.buildMessages(amount) + const decimals = pow(10, this.props.network.decimals) + const denomAmount = multiply(amount, decimals) + + let messages = this.buildMessages(denomAmount) let gas try { gas = await client.simulate(this.props.address, messages) @@ -63,7 +68,6 @@ class DelegateForm extends React.Component { const address = this.props.address const validatorAddress = this.props.selectedValidator.operator_address let messages = [] - const decimals = Math.pow(10, this.props.network.decimals || 6) if(this.props.redelegate){ messages.push({ typeUrl: "/cosmos.staking.v1beta1.MsgBeginRedelegate", @@ -71,7 +75,7 @@ class DelegateForm extends React.Component { delegatorAddress: address, validatorSrcAddress: this.props.validator.operator_address, validatorDstAddress: validatorAddress, - amount: coin(parseInt(parseFloat(amount) * decimals), this.props.network.denom), + amount: coin(floor(amount).toString(), this.props.network.denom), } }) }else{ @@ -81,7 +85,7 @@ class DelegateForm extends React.Component { value: { delegatorAddress: address, validatorAddress: validatorAddress, - amount: coin(parseInt(parseFloat(amount) * decimals), this.props.network.denom), + amount: coin(floor(amount).toString(), this.props.network.denom), } }) } @@ -90,12 +94,12 @@ class DelegateForm extends React.Component { async setAvailableAmount(){ this.setState({error: undefined}) - const decimals = Math.pow(10, this.props.network.decimals || 6) - const messages = this.buildMessages(parseInt(this.props.availableBalance.amount * 0.95) / decimals) + const messages = this.buildMessages(multiply(this.props.availableBalance.amount, 0.95)) this.props.stargateClient.simulate(this.props.address, messages).then(gas => { const saveTxFeeNum = (this.props.redelegate || this.props.undelegate) ? 0 : 10 const gasPrice = this.props.stargateClient.getFee(gas).amount[0].amount - const amount = (this.props.availableBalance.amount - (gasPrice * saveTxFeeNum)) / decimals + const decimals = pow(10, this.props.network.decimals || 6) + const amount = divide(subtract(this.props.availableBalance.amount, multiply(gasPrice, saveTxFeeNum)), decimals) this.setState({amount: amount > 0 ? amount : 0}) }, error => { diff --git a/src/components/Delegations.js b/src/components/Delegations.js index c8de1a9bd..6b68356a6 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -1,5 +1,6 @@ import React from "react"; import _ from "lodash"; +import { bignumber } from 'mathjs' import { Bech32 } from '@cosmjs/encoding' import AlertMessage from "./AlertMessage"; import Coins from "./Coins"; @@ -306,7 +307,7 @@ class Delegations extends React.Component { const reward = validatorReward.reward.find((el) => el.denom === denom) return { validatorAddress: validator, - reward: reward ? parseInt(reward.amount) : undefined, + reward: reward ? bignumber(reward.amount) : undefined, } }) .filter(validatorReward => { diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index d59c44a18..ce0e2784a 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -1,4 +1,5 @@ import _ from 'lodash' +import { multiply, pow } from 'mathjs' import QueryClient from './QueryClient.mjs' import SigningClient from './SigningClient.mjs' import ApyClient from '../ApyClient.mjs' @@ -31,8 +32,9 @@ const Network = async (data, withoutQueryClient) => { const signingClient = (wallet, key) => { if(!queryClient) return - - const gasPrice = data.gasPrice || '0.0025' + chain.denom + + const defaultGasPrice = multiply(0.000000025, pow(10, chain.decimals)).toString() + chain.denom + const gasPrice = data.gasPrice || defaultGasPrice const client = SIGNERS[data.name] || SigningClient return client(queryClient.rpcUrl, gasPrice, wallet, key) } diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index ce5a55bf5..5f6f9cbe6 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -1,10 +1,11 @@ import _ from 'lodash' import { SigningStargateClient, - calculateFee, assertIsDeliverTxSuccess, - GasPrice + GasPrice, + coins } from '@cosmjs/stargate' +import { multiply, ceil, bignumber } from 'mathjs' async function SigningClient(rpcUrl, defaultGasPrice, signer, key, signerOpts) { @@ -23,12 +24,22 @@ async function SigningClient(rpcUrl, defaultGasPrice, signer, key, signerOpts) { return key.isNanoLedger; } + // vendored to handle large integers + // https://github.com/cosmos/cosmjs/blob/0f0c9d8a754cbf01e17acf51d3f2dbdeaae60757/packages/stargate/src/fee.ts + function calculateFee(gasLimit, gasPrice) { + const processedGasPrice = typeof gasPrice === "string" ? GasPrice.fromString(gasPrice) : gasPrice; + const { denom, amount: gasPriceAmount } = processedGasPrice; + const amount = ceil(multiply(bignumber(gasPriceAmount.toString()), bignumber(gasLimit.toString()))); + return { + amount: coins(amount.toString(), denom), + gas: gasLimit.toString(), + }; + } + function getFee(gas, gasPrice) { if (!gas) gas = 200000; - if (!gasPrice) - gasPrice = GasPrice.fromString(defaultGasPrice); - return calculateFee(gas, gasPrice); + return calculateFee(gas, gasPrice || defaultGasPrice); } function signAndBroadcastWithoutBalanceCheck(address, msgs, gas, memo, gasPrice) { From 1ecc5366449bed3947d92a73ab15f4cfb99b7c6d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Apr 2022 01:00:10 +0100 Subject: [PATCH 352/862] Change sifchain and fetchhub default gas price Matches Keplr (ish) --- src/networks.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 364c93a65..4cf129b9a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -91,7 +91,8 @@ }, { "name": "sifchain", - "authzSupport": false + "authzSupport": false, + "gasPrice": "1500000000000rowan" }, { "name": "lumnetwork", @@ -150,6 +151,7 @@ }, { "name": "fetchhub", + "gasPrice": "5000afet", "authzSupport": true }, { From 363ef6c220af28327b0598543deafb73b78fbbe7 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Apr 2022 01:21:44 +0100 Subject: [PATCH 353/862] Handle bignumber/string minimum reward --- scripts/base.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 4a09bcf4f..b2020ad23 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -237,7 +237,7 @@ export class Autostake { const perValidatorReward = floor(divide(totalRewards, validators.length)) - if (perValidatorReward < client.operator.minimumReward) { + if (perValidatorReward < bignumber(client.operator.minimumReward)) { timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') return } From c39e3466262bd3da58c24db7241d15d7f79b00ae Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Apr 2022 01:22:14 +0100 Subject: [PATCH 354/862] Fix cosmos directory check for custom array URLs --- src/utils/Network.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index ce0e2784a..e4b8ab958 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -23,7 +23,14 @@ const Network = async (data, withoutQueryClient) => { const rpcUrl = data.rpcUrl || directory.rpcUrl(data.name) const restUrl = data.restUrl || directory.restUrl(data.name) - const usingDirectory = !![restUrl, rpcUrl].find(el => el.match("cosmos.directory")) + const usingDirectory = !![restUrl, rpcUrl].find(el => { + const match = el => el.match("cosmos.directory") + if(Array.isArray(el)){ + return el.find(match) + }else{ + return match(el) + } + }) let queryClient if(!withoutQueryClient){ From 3971b7d7699f59dcca52be760181e2f75555e0f0 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Apr 2022 01:22:36 +0100 Subject: [PATCH 355/862] Better handle network connection failure --- src/utils/QueryClient.mjs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 37fc9aad3..59ae367e0 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -2,14 +2,19 @@ import axios from "axios"; import _ from "lodash"; const QueryClient = async (chainId, rpcUrls, restUrls) => { - const rpcUrl = await findAvailableUrl( - Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls], - "rpc" - ); - const restUrl = await findAvailableUrl( - Array.isArray(restUrls) ? restUrls : [restUrls], - "rest" - ); + let rpcUrl, restUrl + try { + rpcUrl = await findAvailableUrl( + Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls], + "rpc" + ); + } catch {} + try { + restUrl = await findAvailableUrl( + Array.isArray(restUrls) ? restUrls : [restUrls], + "rest" + ); + } catch {} const getAllValidators = (pageSize, opts, pageCallback) => { return getAllPages((nextKey) => { From 215a09378f16e9cdffcd54b3b43564140e599871 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Apr 2022 02:07:08 +0100 Subject: [PATCH 356/862] Improve unavailable network check --- src/components/NetworkFinder.js | 8 ++++-- src/components/NetworkSelect.js | 12 ++++++--- src/utils/QueryClient.mjs | 45 +++++++++++++-------------------- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index e40a9833b..73f37c9fb 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -75,8 +75,12 @@ function NetworkFinder() { navigate("/" + networkName); } Network(data).then(network => { - setState({network: network}) - }, (error) => { + if(network.connected){ + setState({ network: network }) + }else{ + throw false + } + }).catch(error => { Network(data, true).then(network => { setState({ network: network, loading: false }) }) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index 90af72696..408c050d8 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -83,10 +83,14 @@ function NetworkSelect(props) { Network(data, true).then(network => { setSelectedNetwork(network) Network(data).then(network => { - setSelectedNetwork(network) - setValidators(network.getValidators()) - setLoading(false) - }, (error) => { + if(network.connected){ + setSelectedNetwork(network) + setValidators(network.getValidators()) + setLoading(false) + }else{ + throw false + } + }).catch(error => { setError('Unable to connect to this network currently. Try again later.') setLoading(false) }) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 59ae367e0..e4b7421ec 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -2,19 +2,14 @@ import axios from "axios"; import _ from "lodash"; const QueryClient = async (chainId, rpcUrls, restUrls) => { - let rpcUrl, restUrl - try { - rpcUrl = await findAvailableUrl( - Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls], - "rpc" - ); - } catch {} - try { - restUrl = await findAvailableUrl( - Array.isArray(restUrls) ? restUrls : [restUrls], - "rest" - ); - } catch {} + const rpcUrl = await findAvailableUrl( + Array.isArray(rpcUrls) ? rpcUrls : [rpcUrls], + "rpc" + ); + const restUrl = await findAvailableUrl( + Array.isArray(restUrls) ? restUrls : [restUrls], + "rest" + ); const getAllValidators = (pageSize, opts, pageCallback) => { return getAllPages((nextKey) => { @@ -187,22 +182,18 @@ const QueryClient = async (chainId, rpcUrls, restUrls) => { return pages; }; - function findAvailableUrl(urls, type) { + async function findAvailableUrl(urls, type) { const path = type === "rest" ? "/blocks/latest" : "/block"; - return Promise.any( - urls.map((url) => { - return axios - .get(url + path, { timeout: 10000 }) + return Promise.any(urls.map(async (url) => { + try { + let data = await axios.get(url + path, { timeout: 10000 }) .then((res) => res.data) - .then((data) => { - if (type === "rpc") data = data.result; - if (!data.block.header.chain_id === chainId) { - throw false; - } - return url; - }); - }) - ); + if (type === "rpc") data = data.result; + if (data.block?.header?.chain_id === chainId) { + return url; + } + } catch { } + })); } return { From 7e470f96cecf5f6a38be702bd65bc542269b13ec Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Apr 2022 19:17:15 +0100 Subject: [PATCH 357/862] Handle even larger integers and standardise --- scripts/base.mjs | 9 +++------ src/components/ClaimRewards.js | 11 +++-------- src/components/DelegateForm.js | 11 ++++------- src/utils/Helpers.mjs | 6 ++++++ src/utils/SigningClient.mjs | 6 +++--- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index b2020ad23..c796be912 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -1,12 +1,9 @@ import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import { Slip10RawIndex, pathToString } from "@cosmjs/crypto"; import Network from '../src/utils/Network.mjs' -import {timeStamp, mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' +import {coin, timeStamp, mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' -import { - coin -} from '@cosmjs/stargate' -import { divide, bignumber, floor } from 'mathjs' +import { divide, bignumber, floor, format } from 'mathjs' import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx.js"; import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx.js"; @@ -304,7 +301,7 @@ export class Autostake { value: MsgDelegate.encode(MsgDelegate.fromPartial({ delegatorAddress: address, validatorAddress: validatorAddress, - amount: coin(amount.toString(), denom) + amount: coin(amount, denom) })).finish() }] } diff --git a/src/components/ClaimRewards.js b/src/components/ClaimRewards.js index 0c38f4ec9..3c0f3f8bb 100644 --- a/src/components/ClaimRewards.js +++ b/src/components/ClaimRewards.js @@ -1,14 +1,9 @@ -import Coins from './Coins' - -import { - coin -} from '@cosmjs/stargate' import { MsgWithdrawDelegatorReward, MsgWithdrawValidatorCommission } from "cosmjs-types/cosmos/distribution/v1beta1/tx"; import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx"; +import { coin } from "../utils/Helpers.mjs"; import { - Dropdown, - Badge + Dropdown } from 'react-bootstrap' import { add, subtract, multiply, divide, bignumber, floor } from 'mathjs' @@ -98,7 +93,7 @@ function ClaimRewards(props) { value: MsgDelegate.fromPartial({ delegatorAddress: props.address, validatorAddress: validatorReward.validatorAddress, - amount: coin(floor(validatorReward.reward).toString(), props.network.denom) + amount: coin(validatorReward.reward, props.network.denom) }) }) } diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index 8158bb369..cab19dc00 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -1,9 +1,6 @@ import React from 'react' import Coins from './Coins' - -import { - coin -} from '@cosmjs/stargate' +import { coin } from '../utils/Helpers.mjs' import { Button, @@ -11,7 +8,7 @@ import { Alert } from 'react-bootstrap' -import { pow, multiply, divide, floor, subtract } from 'mathjs' +import { pow, multiply, divide, floor, subtract, format } from 'mathjs' class DelegateForm extends React.Component { constructor(props) { @@ -75,7 +72,7 @@ class DelegateForm extends React.Component { delegatorAddress: address, validatorSrcAddress: this.props.validator.operator_address, validatorDstAddress: validatorAddress, - amount: coin(floor(amount).toString(), this.props.network.denom), + amount: coin(amount, this.props.network.denom) } }) }else{ @@ -85,7 +82,7 @@ class DelegateForm extends React.Component { value: { delegatorAddress: address, validatorAddress: validatorAddress, - amount: coin(floor(amount).toString(), this.props.network.denom), + amount: coin(amount, this.props.network.denom) } }) } diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 28a92d7ca..3e32d6874 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -1,9 +1,15 @@ import _ from 'lodash' +import { format, floor } from 'mathjs' +import { coin as _coin } from '@cosmjs/stargate' export function timeStamp(...args) { console.log('[' + new Date().toISOString().substring(11, 23) + ']', ...args); } +export function coin(amount, denom){ + return _coin(format(floor(amount), {notation: 'fixed'}), denom) +} + export function overrideNetworks(networks, overrides){ return networks.map(network => { let override = overrides[network.name] diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 5f6f9cbe6..391f549d2 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -2,10 +2,10 @@ import _ from 'lodash' import { SigningStargateClient, assertIsDeliverTxSuccess, - GasPrice, - coins + GasPrice } from '@cosmjs/stargate' import { multiply, ceil, bignumber } from 'mathjs' +import { coin } from './Helpers.mjs' async function SigningClient(rpcUrl, defaultGasPrice, signer, key, signerOpts) { @@ -31,7 +31,7 @@ async function SigningClient(rpcUrl, defaultGasPrice, signer, key, signerOpts) { const { denom, amount: gasPriceAmount } = processedGasPrice; const amount = ceil(multiply(bignumber(gasPriceAmount.toString()), bignumber(gasLimit.toString()))); return { - amount: coins(amount.toString(), denom), + amount: [coin(amount, denom)], gas: gasLimit.toString(), }; } From 5dddc92b25fd5a9aaebd0e12ff4cb0162e81db54 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 10 Apr 2022 00:27:37 +0100 Subject: [PATCH 358/862] Allow about linking --- src/components/About.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/components/About.js b/src/components/About.js index 7fe62653a..6aedace0b 100644 --- a/src/components/About.js +++ b/src/components/About.js @@ -1,11 +1,30 @@ +import React, { useEffect } from 'react'; import { Modal } from 'react-bootstrap' +import { useSearchParams } from 'react-router-dom' + function About(props) { + let [searchParams, setSearchParams] = useSearchParams(); + const aboutParam = searchParams.get('about') + + const show = props.show || aboutParam == 'restake' + + useEffect(() => { + if (show && !aboutParam) { + setSearchParams({ about: 'restake' }) + } + }, [show, aboutParam]); + + function onHide(){ + setSearchParams({}) + props.onHide() + } + return ( <> - props.onHide()}> + onHide()}> About REStake From dd9b682a551c492e46d9d933d4cb28f52fadb385 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 10 Apr 2022 00:28:15 +0100 Subject: [PATCH 359/862] Handle disconnected network --- src/components/Delegations.js | 2 +- src/components/ValidatorName.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 6b68356a6..d333a8e53 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -100,7 +100,7 @@ class Delegations extends React.Component { } async calculateApy() { - if(this.props.network.apyEnabled === false) return + if(this.props.network.apyEnabled === false || !this.props.network.getApy) return this.props.network.getApy( this.props.validators, diff --git a/src/components/ValidatorName.js b/src/components/ValidatorName.js index d1b547a77..0dd928c3d 100644 --- a/src/components/ValidatorName.js +++ b/src/components/ValidatorName.js @@ -6,7 +6,7 @@ import { function ValidatorName(props) { const { validator, hideWarning, fallback } = props if(!validator) return fallback || null - const { moniker } = validator.description + const moniker = validator.description?.moniker || validator.name || 'Unknown' let warning = false let warningClass From ee99e8b12885c65b30ad255cde147946aca5ae19 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 10 Apr 2022 00:29:41 +0100 Subject: [PATCH 360/862] Handle Ledger grant status in UI --- src/components/Delegations.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index d333a8e53..a1456ffd1 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -123,21 +123,22 @@ class Delegations extends React.Component { return this.props.queryClient.getGrants(botAddress, this.props.address).then( (result) => { + const { claimGrant, stakeGrant } = result let grantValidators; - if (result.stakeGrant) { + if (stakeGrant) { grantValidators = - result.stakeGrant.authorization.allow_list.address; + stakeGrant.authorization.allow_list?.address; } const operatorGrant = { - claimGrant: result.claimGrant, - stakeGrant: result.stakeGrant, + claimGrant: claimGrant, + stakeGrant: stakeGrant, validators: grantValidators || [], grantsValid: !!( - result.claimGrant && - result.stakeGrant && - grantValidators.includes(address) + claimGrant && + stakeGrant && + (!grantValidators || grantValidators.includes(address)) ), - grantsExist: !!(result.claimGrant || result.stakeGrant), + grantsExist: !!(claimGrant || stakeGrant), }; this.setState((state, props) => ({ operatorGrants: _.set( @@ -376,13 +377,13 @@ class Delegations extends React.Component { {operator ? ( - this.restakePossible() && delegation ? ( + this.authzSupport() && delegation ? ( this.grantsValid(operator) ? ( - ) : ( + ) : this.restakePossible() ? ( + ) : ( + } + identifier={validatorAddress} + tooltip="This validator can REStake your rewards" + /> ) ) : ( Date: Sun, 10 Apr 2022 00:32:28 +0100 Subject: [PATCH 361/862] Ledger manual CLI instructions Dynamic for network and operator --- src/components/AboutLedger.js | 125 ++++++++++++++++++++++++++++++++++ src/components/App.css | 9 +++ src/components/Delegate.js | 30 ++++++-- src/components/Delegations.js | 19 ++++-- src/utils/Network.mjs | 5 +- 5 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 src/components/AboutLedger.js diff --git a/src/components/AboutLedger.js b/src/components/AboutLedger.js new file mode 100644 index 000000000..889e0b084 --- /dev/null +++ b/src/components/AboutLedger.js @@ -0,0 +1,125 @@ +import React, { useEffect } from 'react'; +import { + Modal +} from 'react-bootstrap' + +import { useSearchParams } from 'react-router-dom' + +function AboutLedger(props) { + let [searchParams, setSearchParams] = useSearchParams(); + const aboutParam = searchParams.get('about') + + const { network, validator } = props + const { daemon_name, chain_id, node_home, codebase } = network.chain.chainData + const { git_repo, binaries } = codebase || {} + + const address = validator?.restake.address || GrantAddress + + const show = props.show || (aboutParam == 'ledger' && network.authzSupport) + + useEffect(() => { + if (show && !aboutParam) { + setSearchParams({ about: 'ledger' }) + } + }, [show, aboutParam]); + + function onHide(){ + setSearchParams({}) + props.onHide() + } + + const content = ( + <> +
    How to use a Ledger with {validator ? validator.name : 'REStake'}
    +

    Right now there is a limitation in Cosmos SDK which prevents using a Ledger device with the REStake UI. It is possible though to send a slightly modified version of the grants using the {network.prettyName} CLI client.

    +

    The commands needed are detailed below thanks to the awesome @gjermundbjaanes who put together a more detailed guide and CLI tool to make the process easier.

    +

    {!validator ? You can find validator specific instructions in a tab on the validator's page, with their REStake address pre-filled. : `${validator.name}'s REStake address has been pre-filled below.`} Instructions are specific to the Network you have selected.

    +

    You should only attempt this if you have a basic understanding of using a command line tool.

    +
    Download CLI client for {network.prettyName}
    +

    Skip this step if you've already installed {daemon_name || network.prettyName}.

    + { + binaries && ( + <> +
    Install from pre-built binaries
    +

    Download one of the pre-built binaries and use that instead of building from source.

    +

    The binary may not have been built with ledger support so you might still need the source method.

    +

    + {Object.entries(binaries).map(([key, item]) => { + return ( + {key}: {item}
    + ) + })}

    +
    Alternative install from source
    + + ) + } + { + git_repo + ? ( + <> +

    Clone and install the {daemon_name || network.prettyName} CLI client from {git_repo}. The standard Tendermint installation process is detailed below, but you might need to check the documentation for project specific install.

    +
    
    +                

    git clone {git_repo} restake_{network.name}

    +

    cd restake_{network.name}

    +

    make install

    +
    + + ) : ( +

    Download and install the {daemon_name || network.prettyName} CLI client from the project's documentation.

    + ) + } +
    Add Ledger as a key
    +

    Skip this step if you already have your ledger setup with {daemon_name || network.prettyName}.

    +

    Add your Ledger as a new key. The below example will use the key name ledger and the default install directory{node_home ? <>({node_home}) : ''}.

    +

    Make sure your Ledger is plugged in, unlocked and the Cosmos app is open.

    +
    
    +        

    {daemon_name ? daemon_name : DaemonName} keys add ledger --ledger --keyring-backend file

    +
    +
    Grant permission to withdraw delegator rewards
    +

    Next you need to grant the first of two permissions required for REStake to function. This is the WithdrawDelegatorReward grant and is identical to the one the REStake UI would set.

    + {!validator &&

    Make sure you enter your validator's REStake address in place of {address}. You can find instructions in a tab on the validator's page with their address pre-filled.

    } + {validator &&

    {validator.name}'s REStake address has been pre-filled below ({address}).

    } +
    
    +        

    {daemon_name ? daemon_name : DaemonName} tx authz grant {address} generic --msg-type /cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward --from ledger --ledger --chain-id {chain_id} --node https://rpc.cosmos.directory:443/{network.name} --keyring-backend file --gas auto --gas-prices {network.gasPrice} --gas-adjustment 1.5

    +
    +
    Grant permission to delegate rewards
    +

    Finally you need to grant the final permission to allow the validator to send Delegate messages. This is different to the REStake UI which also limits the validator address the address can delegate to. The REStake script will handle this difference and it doesn't present much of a security problem.

    + {!validator &&

    Make sure you enter your validator's REStake address in place of {address}. You can find instructions in a tab on the validator's page with their address pre-filled.

    } + {validator &&

    {validator.name}'s REStake address has been pre-filled below ({address}).

    } +
    
    +        

    {daemon_name ? daemon_name : DaemonName} tx authz grant {address} generic --msg-type /cosmos.staking.v1beta1.MsgDelegate --from ledger --ledger --chain-id {chain_id} --node https://rpc.cosmos.directory:443/{network.name} --keyring-backend file --gas auto --gas-prices {network.gasPrice} --gas-adjustment 1.5

    +
    +
    Fin.
    +

    That's it! So long as you have an active delegation with them, your validator should now pick up your ledger's address as a valid REStake user if they're running the latest version. If you don't see the restaking happening, reach out to them to update.

    +

    The UI will show the current state of your grant with the validator, you just won't be able to revoke.

    +

    We will make sure the UI supports these grant types once Ledger integration is possible.

    +

    For more detailed instructions check out @gjermundbjaanes's awesome guide and helper tool.

    +
    +
    Revoke permissions
    +

    Revoking the grants is just another command. You need to revoke each permission individually.

    + {!validator &&

    Make sure you enter your validator's REStake address in place of {address}. You can find instructions in a tab on the validator's page with their address pre-filled.

    } + {validator &&

    {validator.name}'s REStake address has been pre-filled below ({address}).

    } +
    
    +        

    {daemon_name ? daemon_name : DaemonName} tx authz revoke {address} /cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward --from ledger --ledger --chain-id {chain_id} --node https://rpc.cosmos.directory:443/{network.name} --keyring-backend file --gas auto --gas-prices {network.gasPrice} --gas-adjustment 1.5

    +

    {daemon_name ? daemon_name : DaemonName} tx authz revoke {address} /cosmos.staking.v1beta1.MsgDelegate --from ledger --ledger --chain-id {chain_id} --node https://rpc.cosmos.directory:443/{network.name} --keyring-backend file --gas auto --gas-prices {network.gasPrice} --gas-adjustment 1.5

    +
    + + ) + + if(props.modal === false) return
    {content}
    ; + + return ( + <> + onHide()}> + + Ledger + + + {content} + + + + ); +} + +export default AboutLedger diff --git a/src/components/App.css b/src/components/App.css index 809503d5c..a8095f214 100644 --- a/src/components/App.css +++ b/src/components/App.css @@ -26,3 +26,12 @@ header a.moniker{ line-height: 70px; font-size: 1.3rem; } + +code { + padding: 0.5rem; + line-height:1.5rem; +} + +code p{ + margin-bottom: 0.5rem; +} \ No newline at end of file diff --git a/src/components/Delegate.js b/src/components/Delegate.js index 7dd449b77..39d8874ad 100644 --- a/src/components/Delegate.js +++ b/src/components/Delegate.js @@ -6,6 +6,7 @@ import ValidatorImage from './ValidatorImage' import ValidatorLink from './ValidatorLink' import Coins from './Coins' import TooltipIcon from './TooltipIcon' +import AboutLedger from './AboutLedger'; import { Dropdown, @@ -42,6 +43,14 @@ function Delegate(props) { } } + const handleClose = () => { + if(selectedValidator && (!validator || redelegate) && selectedValidator !== validator){ + setSelectedValidator(null) + }else{ + setShow(false) + } + } + const onDelegate = () => { props.onDelegate() setShow(false) @@ -164,7 +173,7 @@ function Delegate(props) { return ( <> {button()} - setShow(false)}> + {selectedValidator @@ -200,9 +209,15 @@ function Delegate(props) { )} - Address - {selectedValidator.operator_address} + Validator Address + {selectedValidator.operator_address} + + {operator() && ( + + REStake Address + {operator().botAddress} + )} {!active() && ( Status @@ -212,7 +227,7 @@ function Delegate(props) { {!!website() && ( Website - {website()} + {website()} )} @@ -270,7 +285,7 @@ function Delegate(props) {

    {selectedValidator.description.details}

    @@ -292,6 +307,11 @@ function Delegate(props) { stargateClient={props.stargateClient} onDelegate={onDelegate} /> + {network.authzSupport && operator() && ( + + + + )} )} diff --git a/src/components/Delegations.js b/src/components/Delegations.js index a1456ffd1..eb8a6c60b 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -11,6 +11,7 @@ import CountdownRestake from "./CountdownRestake"; import Delegate from "./Delegate"; import ValidatorImage from "./ValidatorImage"; import TooltipIcon from "./TooltipIcon"; +import AboutLedger from "./AboutLedger"; import { Table, Button, Dropdown, Spinner } from "react-bootstrap"; @@ -650,13 +651,21 @@ class Delegations extends React.Component { {this.authzSupport() && this.props.operators.length > 0 && this.state.isNanoLedger && ( - + <> + +

    Ledger devices are not supported in the REStake UI currently. Support will be added as soon as it is possible.

    +

    this.setState({ showAboutLedger: true })} role="button" className="text-dark text-decoration-underline">A manual workaround is possible using the CLI

    +
    + )} + {this.props.network && ( + this.setState({ showAboutLedger: false })} network={this.props.network} /> + )} ); diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index e4b8ab958..7f0eb557d 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -32,6 +32,8 @@ const Network = async (data, withoutQueryClient) => { } }) + const defaultGasPrice = multiply(0.000000025, pow(10, chain.decimals)).toString() + chain.denom + let queryClient if(!withoutQueryClient){ queryClient = await QueryClient(chain.chainId, rpcUrl, restUrl) @@ -40,7 +42,6 @@ const Network = async (data, withoutQueryClient) => { const signingClient = (wallet, key) => { if(!queryClient) return - const defaultGasPrice = multiply(0.000000025, pow(10, chain.decimals)).toString() + chain.denom const gasPrice = data.gasPrice || defaultGasPrice const client = SIGNERS[data.name] || SigningClient return client(queryClient.rpcUrl, gasPrice, wallet, key) @@ -88,7 +89,7 @@ const Network = async (data, withoutQueryClient) => { chainId: chain.chainId, prefix: chain.prefix, slip44: chain.slip44, - gasPrice: data.gasPrice, + gasPrice: data.gasPrice || defaultGasPrice, denom: chain.denom, symbol: chain.symbol, decimals: chain.decimals, From 7f34f92af0c0869aee4c1807b1719fdf1c5ac9b1 Mon Sep 17 00:00:00 2001 From: "Sachin S. Kamath" Date: Sun, 10 Apr 2022 12:16:58 +0530 Subject: [PATCH 362/862] security: bump moment due to GHSA-8hfj-j24r-96c4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0e965568a..87854141b 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "dotenv": "^16.0.0", "fuzzy-search": "^3.2.1", "mathjs": "^10.4.3", - "moment": "^2.29.1", + "moment": "^2.29.2", "parse-duration": "^1.0.2", "path": "^0.12.7", "react": "^17.0.2", From 5b1dbe2ddf041157617e7310dfdc33f78b16a85f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 11 Apr 2022 15:38:40 +0100 Subject: [PATCH 363/862] Handle grant max tokens in autostake script --- scripts/base.mjs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index c796be912..b63bf1e74 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -3,7 +3,7 @@ import { Slip10RawIndex, pathToString } from "@cosmjs/crypto"; import Network from '../src/utils/Network.mjs' import {coin, timeStamp, mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' -import { divide, bignumber, floor, format } from 'mathjs' +import { divide, bignumber, floor, smaller } from 'mathjs' import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx.js"; import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx.js"; @@ -64,7 +64,7 @@ export class Autostake { async runNetwork(client){ timeStamp('Running autostake') const balance = await this.checkBalance(client) - if (!balance || balance < 1_000) { + if (!balance || smaller(balance, 1_000)) { timeStamp('Bot balance is too low') return } @@ -173,8 +173,8 @@ export class Autostake { let grantCalls = addresses.map(item => { return async () => { try { - const validators = await this.getGrantValidators(client, item) - return validators ? item : undefined + const grant = await this.getGrants(client, item) + return grant ? { address: item, grant: grant } : undefined } catch (error) { timeStamp(item, 'Failed to get address', error.message) } @@ -186,7 +186,7 @@ export class Autostake { return _.compact(grantedAddresses.flat()) } - getGrantValidators(client, delegatorAddress) { + getGrants(client, delegatorAddress) { let timeout = client.network.data.autostake?.delegatorTimeout || 5000 return client.queryClient.getGrants(client.operator.botAddress, delegatorAddress, { timeout }) .then( @@ -203,7 +203,12 @@ export class Autostake { return } - return grantValidators + const maxTokens = result.stakeGrant.authorization.max_tokens + + return { + maxTokens: maxTokens && bignumber(maxTokens.amount), + validators: grantValidators, + } } }, (error) => { @@ -212,9 +217,9 @@ export class Autostake { ) } - async getAutostakeMessages(client, addresses, validators) { + async getAutostakeMessages(client, grantAddresses, validators) { let batchSize = client.network.data.autostake?.batchQueries || 50 - let calls = addresses.map(item => { + let calls = grantAddresses.map(item => { return async () => { try { return await this.getAutostakeMessage(client, item, validators) @@ -229,16 +234,22 @@ export class Autostake { return _.compact(messages.flat()) } - async getAutostakeMessage(client, address, validators) { + async getAutostakeMessage(client, grantAddress, validators) { + const { address, grant } = grantAddress const totalRewards = await this.totalRewards(client, address, validators) const perValidatorReward = floor(divide(totalRewards, validators.length)) - if (perValidatorReward < bignumber(client.operator.minimumReward)) { + if (smaller(perValidatorReward, bignumber(client.operator.minimumReward))) { timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') return } + if (grant.maxTokens && smaller(grant.maxTokens, perValidatorReward)) { + timeStamp(address, grant.maxTokens, client.network.denom, 'grant balance is too low, skipping') + return + } + let timeout = client.network.data.autostake?.delegatorTimeout || 5000 const withdrawAddress = await client.queryClient.getWithdrawAddress(address, { timeout }) if(withdrawAddress && withdrawAddress !== address){ From 75b752ad01acb820e95706a8f3ac55c33d0012df Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 11 Apr 2022 15:38:14 +0100 Subject: [PATCH 364/862] Initial grant management modal --- src/components/Coins.js | 2 + src/components/Delegations.js | 129 +++++++++------ src/components/GrantRestake.js | 106 ------------- src/components/ManageGrant.js | 271 ++++++++++++++++++++++++++++++++ src/components/RevokeRestake.js | 4 - src/components/ValidatorName.js | 3 +- 6 files changed, 354 insertions(+), 161 deletions(-) delete mode 100644 src/components/GrantRestake.js create mode 100644 src/components/ManageGrant.js diff --git a/src/components/Coins.js b/src/components/Coins.js index 0a8b5d9bd..12abd0bb2 100644 --- a/src/components/Coins.js +++ b/src/components/Coins.js @@ -2,6 +2,8 @@ import _ from 'lodash' function Coins(props) { function amount(coins, decimals){ + if(props.inBaseDenom) return coins.amount + if (!decimals) { decimals = 6 } diff --git a/src/components/Delegations.js b/src/components/Delegations.js index eb8a6c60b..6ffdec57d 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -1,12 +1,12 @@ import React from "react"; import _ from "lodash"; -import { bignumber } from 'mathjs' +import { larger, bignumber } from 'mathjs' import { Bech32 } from '@cosmjs/encoding' import AlertMessage from "./AlertMessage"; import Coins from "./Coins"; import ClaimRewards from "./ClaimRewards"; import RevokeRestake from "./RevokeRestake"; -import GrantRestake from "./GrantRestake"; +import ManageGrant from "./ManageGrant"; import CountdownRestake from "./CountdownRestake"; import Delegate from "./Delegate"; import ValidatorImage from "./ValidatorImage"; @@ -28,6 +28,13 @@ class Delegations extends React.Component { this.onClaimRewards = this.onClaimRewards.bind(this); this.onGrant = this.onGrant.bind(this); this.onRevoke = this.onRevoke.bind(this); + this.defaultGrant = { + claimGrant: null, + stakeGrant: null, + validators: [], + grantsValid: false, + grantsExist: false, + } } async componentDidMount() { @@ -125,21 +132,17 @@ class Delegations extends React.Component { return this.props.queryClient.getGrants(botAddress, this.props.address).then( (result) => { const { claimGrant, stakeGrant } = result - let grantValidators; + let grantValidators, maxTokens; if (stakeGrant) { grantValidators = stakeGrant.authorization.allow_list?.address; + maxTokens = stakeGrant.authorization.max_tokens } const operatorGrant = { claimGrant: claimGrant, stakeGrant: stakeGrant, validators: grantValidators || [], - grantsValid: !!( - claimGrant && - stakeGrant && - (!grantValidators || grantValidators.includes(address)) - ), - grantsExist: !!(claimGrant || stakeGrant), + maxTokens: maxTokens && bignumber(maxTokens.amount) }; this.setState((state, props) => ({ operatorGrants: _.set( @@ -163,10 +166,12 @@ class Delegations extends React.Component { } } - onGrant(operator) { - const operatorGrant = { - grantsValid: true, - grantsExist: true, + onGrant(operator, expired, maxTokens) { + const operatorGrant = expired ? this.defaultGrant : { + claimGrant: {}, + stakeGrant: {}, + validators: [operator.address], + maxTokens: bignumber(maxTokens.amount) }; this.setState((state, props) => ({ operatorGrants: _.set( @@ -181,18 +186,11 @@ class Delegations extends React.Component { } onRevoke(operator) { - const operatorGrant = { - claimGrant: null, - stakeGrant: null, - validators: [], - grantsValid: false, - grantsExist: false, - }; this.setState((state, props) => ({ operatorGrants: _.set( state.operatorGrants, operator.botAddress, - operatorGrant + this.defaultGrant ), error: null, validatorLoading: _.set(state.validatorLoading, operator.address, false), @@ -228,9 +226,19 @@ class Delegations extends React.Component { return this.props.network.authzSupport } - grantsValid(operator) { - const grants = this.state.operatorGrants[operator.botAddress]; - return grants && grants.grantsValid; + operatorGrants(operator) { + const grant = this.state.operatorGrants[operator.botAddress] + if(!grant) return this.defaultGrant; + return { + ...grant, + grantsValid: !!( + grant.claimGrant && + grant.stakeGrant && + (!grant.validators || grant.validators.includes(operator.address)) && + (grant.maxTokens === null || larger(grant.maxTokens, this.validatorReward(operator.address))) + ), + grantsExist: !!(grant.claimGrant || grant.stakeGrant), + } } restakePossible() { @@ -299,22 +307,27 @@ class Delegations extends React.Component { }; } + validatorReward(validatorAddress) { + if (!this.state.rewards) return 0; + const denom = this.props.network.denom; + const validatorReward = this.state.rewards[validatorAddress]; + const reward = validatorReward && validatorReward.reward.find((el) => el.denom === denom) + return reward ? bignumber(reward.amount) : 0 + } + validatorRewards(validators) { if (!this.state.rewards) return []; - const denom = this.props.network.denom; const validatorRewards = Object.keys(this.state.rewards) - .map(validator => { - const validatorReward = this.state.rewards[validator]; - const reward = validatorReward.reward.find((el) => el.denom === denom) - return { - validatorAddress: validator, - reward: reward ? bignumber(reward.amount) : undefined, - } - }) - .filter(validatorReward => { - return validatorReward.reward && (validators === undefined || validators.includes(validatorReward.validatorAddress)) - }); + .map(validator => { + return { + validatorAddress: validator, + reward: this.validatorReward(validator), + } + }) + .filter(validatorReward => { + return validatorReward.reward && (validators === undefined || validators.includes(validatorReward.validatorAddress)) + }); return validatorRewards; } @@ -333,11 +346,12 @@ class Delegations extends React.Component { this.state.rewards && this.state.rewards[validatorAddress]; const denomRewards = rewards && this.denomRewards(rewards); const operator = this.operatorForValidator(validatorAddress); + const grants = operator && this.operatorGrants(operator) let rowVariant = operator && delegation - ? this.grantsValid(operator) + ? grants.grantsValid ? "table-success" - : "table-warning" + : grants.grantsExist ? "table-danger" : "table-warning" : undefined; if(isValidatorOperator) rowVariant = 'table-info' @@ -379,18 +393,21 @@ class Delegations extends React.Component { {operator ? ( this.authzSupport() && delegation ? ( - this.grantsValid(operator) ? ( + this.operatorGrants(operator).grantsValid ? ( ) : this.restakePossible() ? ( - {operator && - this.restakePossible() && - this.grantsValid(operator) && ( + this.restakePossible() && ( <> - - this.setValidatorLoading( - validatorAddress, - loading - ) - } + onGrant={this.onGrant} setError={this.setError} /> + {this.operatorGrants(operator).grantsExist && ( + + this.setValidatorLoading( + validatorAddress, + loading + ) + } + setError={this.setError} + /> + )}
    )} diff --git a/src/components/GrantRestake.js b/src/components/GrantRestake.js deleted file mode 100644 index b7fcfb252..000000000 --- a/src/components/GrantRestake.js +++ /dev/null @@ -1,106 +0,0 @@ -import React, { useState } from 'react'; - -import { GenericAuthorization } from "cosmjs-types/cosmos/authz/v1beta1/authz"; -import { StakeAuthorization } from "cosmjs-types/cosmos/staking/v1beta1/authz"; -import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; - -import { - Button, - OverlayTrigger, - Tooltip -} from 'react-bootstrap' - -function GrantRestake(props) { - const [loading, setLoading] = useState(false); - - function update(){ - setLoading(true) - - const messages = [ - buildGrantMsg("/cosmos.staking.v1beta1.StakeAuthorization", - StakeAuthorization.encode(StakeAuthorization.fromPartial({ - allowList: {address: [props.operator.address]}, - authorizationType: 1 - })).finish(), - ), - buildGrantMsg("/cosmos.authz.v1beta1.GenericAuthorization", - GenericAuthorization.encode(GenericAuthorization.fromPartial({ - msg: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward' - })).finish(), - ) - ] - console.log(messages) - - props.stargateClient.signAndBroadcast(props.address, messages).then((result) => { - console.log("Successfully broadcasted:", result); - setLoading(false) - props.onGrant(props.operator) - }, (error) => { - console.log('Failed to broadcast:', error) - setLoading(false) - props.setError('Failed to broadcast: ' + error.message) - }) - } - - function buildGrantMsg(type, value){ - const dateNow = new Date(); - const expiration = new Date( - dateNow.getFullYear() + 1, - dateNow.getMonth(), - dateNow.getDate() - ) - return { - typeUrl: "/cosmos.authz.v1beta1.MsgGrant", - value: { - granter: props.address, - grantee: props.operator.botAddress, - grant: { - authorization: { - typeUrl: type, - value: value - }, - expiration: Timestamp.fromPartial({ - seconds: expiration.getTime() / 1000, - nanos: 0 - }) - } - }, - } - } - - function button(){ - return ( - - ) - } - - return ( - <> - {!loading - ? ( - <> - {props.tooltip ? ( - - {props.tooltip} - - } - >{button()} - ) : button()} - - ) : ( - - ) - } - - ) -} - -export default GrantRestake; diff --git a/src/components/ManageGrant.js b/src/components/ManageGrant.js new file mode 100644 index 000000000..82041e411 --- /dev/null +++ b/src/components/ManageGrant.js @@ -0,0 +1,271 @@ +import React, { useState, useEffect } from 'react'; +import moment from 'moment' +import {pow, multiply, divide, larger, smaller} from 'mathjs' + +import { GenericAuthorization } from "cosmjs-types/cosmos/authz/v1beta1/authz"; +import { StakeAuthorization } from "cosmjs-types/cosmos/staking/v1beta1/authz"; +import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; + +import { + Button, + OverlayTrigger, + Tooltip, + Modal, + Dropdown, + Form, + Table +} from 'react-bootstrap' + +import Coins from './Coins'; +import { coin } from '../utils/Helpers.mjs'; + +function ManageGrant(props) { + const { grants, operator, address, network } = props + const { claimGrant, stakeGrant, grantsValid, grantsExist } = grants + const defaultExpiry = moment().add(1, 'year') + const [loading, setLoading] = useState(false); + const [show, setShow] = useState(false); + const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry}); + + useEffect(() => { + const maxTokens = stakeGrant?.authorization?.max_tokens + const claimExpiry = claimGrant && claimGrant.expiration && moment(claimGrant.expiration) + const stakeExpiry = stakeGrant && stakeGrant.expiration && moment(stakeGrant.expiration) + const expiryDate = claimExpiry && stakeExpiry && claimExpiry > stakeExpiry ? stakeExpiry : claimExpiry || stakeExpiry + setState({ + ...state, + maxTokens, + expiryDate, + maxTokensValue: maxTokens && state.maxTokensValue === '' ? divide(maxTokens.amount, pow(10, network.decimals)) : state.maxTokensValue, + expiryDateValue: (expiryDate || defaultExpiry).format('YYYY-MM-DD') + }) + }, [grants]) + + function handleOpen() { + setShow(true); + } + + function handleClose() { + setShow(false); + } + + function handleInputChange(e) { + setState({...state, [e.target.name]: e.target.value }); + } + + function maxTokensDenom() { + if(state.maxTokensValue === '') return + + const decimals = pow(10, network.decimals) + return multiply(state.maxTokensValue, decimals) + } + + function maxTokensValid() { + return !maxTokensDenom() || larger(maxTokensDenom(), props.rewards) + } + + function showLoading(isLoading){ + setLoading(isLoading) + props.setLoading && props.setLoading(isLoading) + } + + function handleSubmit(event){ + event.preventDefault() + showLoading(true) + const expiry = moment(state.expiryDateValue) + let maxTokens + if(state.maxTokensValue !== ''){ + maxTokens = coin(maxTokensDenom(), network.denom) + } + + const messages = [ + buildGrantMsg("/cosmos.staking.v1beta1.StakeAuthorization", + StakeAuthorization.encode(StakeAuthorization.fromPartial({ + allowList: {address: [operator.address]}, + maxTokens: maxTokens, + authorizationType: 1 + })).finish(), + expiry + ), + buildGrantMsg("/cosmos.authz.v1beta1.GenericAuthorization", + GenericAuthorization.encode(GenericAuthorization.fromPartial({ + msg: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward' + })).finish(), + expiry + ) + ] + console.log(messages) + + props.stargateClient.signAndBroadcast(address, messages).then((result) => { + console.log("Successfully broadcasted:", result); + showLoading(false) + props.onGrant(operator, moment().diff(expiry, "seconds") <= 0, maxTokens); + setShow(false); + }, (error) => { + console.log('Failed to broadcast:', error) + showLoading(false) + props.setError('Failed to broadcast: ' + error.message) + }) + } + + function buildGrantMsg(type, value, expiryDate){ + return { + typeUrl: "/cosmos.authz.v1beta1.MsgGrant", + value: { + granter: address, + grantee: operator.botAddress, + grant: { + authorization: { + typeUrl: type, + value: value + }, + expiration: Timestamp.fromPartial({ + seconds: expiryDate.unix(), + nanos: 0 + }) + } + }, + } + } + + function button(){ + if(props.dropdownItem){ + return ( + handleOpen()} > + {grants.grantsValid ? 'Manage REStake' : 'Enable REStake'} + + ) + } + return ( + + ) + } + + function loadingButton(){ + return ( + + ) + } + + const minimumReward = () => { + return { + amount: operator.minimumReward, + denom: network.denom + } + } + + return ( + <> + {!loading || props.dropdownItem + ? ( + <> + {props.tooltip ? ( + + {props.tooltip} + + } + >{button()} + ) : button()} + + ) : ( + loadingButton() + ) + } + + + + Manage REStake + + + +
    + + + + + + + + + + + + + + + + + + + + + + + {state.maxTokens && ( + + + + + )} + + + + + +
    Validator{operator.moniker}
    REStake Address{operator.botAddress}
    Frequency + {operator.runTimesString()} +
    Minimum Reward + +
    Current Rewards + +
    Grant Remaining + +
    Grant status + {grantsValid + ? Valid + : grantsExist + ? state.maxTokens && smaller(state.maxTokens.amount, props.rewards) + ? Not enough grant remaining + : Invalid : Missing} +
    + + Maximum delegation +
    +
    + + {network.symbol.toUpperCase()} +
    +
    + Remaining amount this validator can delegate
    Leave empty for unlimited +
    +
    +
    + + Expiry date + +
    Date the grant will expire. After this date you will need to re-grant
    +
    +

    + {!loading + ? + : + } +

    +
    +
    +
    + + ) +} + +export default ManageGrant; diff --git a/src/components/RevokeRestake.js b/src/components/RevokeRestake.js index 4bf96d3a5..9f5d6599f 100644 --- a/src/components/RevokeRestake.js +++ b/src/components/RevokeRestake.js @@ -1,12 +1,8 @@ -import React, { useState } from 'react'; - import { Dropdown } from 'react-bootstrap' function RevokeRestake(props) { - const [loading, setLoading] = useState(false); - function revoke(){ props.setLoading(true) diff --git a/src/components/ValidatorName.js b/src/components/ValidatorName.js index 0dd928c3d..8abd51eb8 100644 --- a/src/components/ValidatorName.js +++ b/src/components/ValidatorName.js @@ -14,7 +14,8 @@ function ValidatorName(props) { if(validator.status === 'BOND_STATUS_UNBONDED') warning = 'Validator is not in the active set' if(validator.jailed) warning = 'Validator is jailed' - if(warning && !hideWarning) warningClass = 'text-danger text-decoration-line-through' + if(warning && !hideWarning) warningClass = 'text-danger' + if(validator.jailed) warningClass = warningClass + ' text-decoration-line-through' return ( <> From be546cc1b8881af3e9a2b9b3d76f5348ed1ac92f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 11 Apr 2022 18:26:27 +0100 Subject: [PATCH 365/862] Refactor ValidatorModal --- src/components/Delegate.js | 323 ---------------------------- src/components/DelegateForm.js | 2 +- src/components/Delegations.js | 167 +++++++------- src/components/ValidatorDelegate.js | 37 ++++ src/components/ValidatorModal.js | 147 +++++++++++++ src/components/ValidatorProfile.js | 158 ++++++++++++++ src/components/Wallet.js | 8 +- 7 files changed, 417 insertions(+), 425 deletions(-) delete mode 100644 src/components/Delegate.js create mode 100644 src/components/ValidatorDelegate.js create mode 100644 src/components/ValidatorModal.js create mode 100644 src/components/ValidatorProfile.js diff --git a/src/components/Delegate.js b/src/components/Delegate.js deleted file mode 100644 index 39d8874ad..000000000 --- a/src/components/Delegate.js +++ /dev/null @@ -1,323 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -import DelegateForm from './DelegateForm' -import Validators from './Validators' -import ValidatorImage from './ValidatorImage' -import ValidatorLink from './ValidatorLink' -import Coins from './Coins' -import TooltipIcon from './TooltipIcon' -import AboutLedger from './AboutLedger'; - -import { - Dropdown, - Button, - Modal, - OverlayTrigger, - Tooltip, - Table, - Spinner, - Tabs, - Tab -} from 'react-bootstrap' - -import { - XCircle -} from 'react-bootstrap-icons' - -function Delegate(props) { - const { redelegate, undelegate, validator, delegations, operators, network, validators } = props - const [show, setShow] = useState(false); - const [selectedValidator, setSelectedValidator] = useState(!redelegate && validator); - const [activeTab, setActiveTab] = useState(props.activeTab || (redelegate || undelegate) ? 'delegate' : 'profile'); - - useEffect(() => { - if(props.activeTab != activeTab){ - setActiveTab(props.activeTab) - } - }, [props.activeTab]) - - const handleOpen = () => { - setShow(true) - if (!validator || redelegate) { - setSelectedValidator(null) - } - } - - const handleClose = () => { - if(selectedValidator && (!validator || redelegate) && selectedValidator !== validator){ - setSelectedValidator(null) - }else{ - setShow(false) - } - } - - const onDelegate = () => { - props.onDelegate() - setShow(false) - } - - const excludeValidators = () => { - if (redelegate) { - return [validator.operator_address] - } else if (delegations) { - return [...Object.keys(delegations), ...operators.map(el => el.address)] - } - } - - const operator = () => { - if (!operators || !selectedValidator) return - - return network.getOperator(selectedValidator.operator_address) - } - - const active = () => { - if(!selectedValidator) return false - - return selectedValidator.status === 'BOND_STATUS_BONDED' && !selectedValidator.jailed - } - - const status = () => { - if(!selectedValidator) return - - let status = '' - let className = '' - if(active()){ - status = 'Active' - }else{ - status = 'Inactive' - className = 'text-danger' - } - if(selectedValidator.jailed){ - status += ' (JAILED)' - className = 'text-danger' - } - - return {status} - } - - const website = () => { - if (!selectedValidator) return - - return selectedValidator.description && selectedValidator.description.website - } - - const securityContact = () => { - if (!selectedValidator) return - - return selectedValidator.description && selectedValidator.description.security_contact - } - - const bondedTokens = () => { - if (!selectedValidator) return - - const amount = selectedValidator.tokens - return - } - - const minimumReward = () => { - return { - amount: operator().minimumReward, - denom: network.denom - } - } - - const actionText = () => { - if (redelegate) return 'Redelegate' - if (undelegate) return 'Undelegate' - if (validator) { - return 'Delegate' - } else { - return 'Add Validator' - } - } - - const button = () => { - if (props.children) { - return ( - - {props.children} - - ) - } else { - if (props.button) { - const button = ( - - ) - return ( - <> - {props.tooltip && validator ? ( - - {props.tooltip} - - } - >{button} - ) : button} - - ) - } else { - return ( - - {actionText()} - - ) - } - } - } - - return ( - <> - {button()} - - - - {selectedValidator - ? ( - <> - - - - ) : actionText() - } - - - - {!selectedValidator && - setSelectedValidator(selectedValidator)} />} - {selectedValidator && ( - setActiveTab(k)} id="validator-tabs" className="mb-3"> - - - - {active() && ( - - - - - )} - - - - - {operator() && ( - - - - - )} - {!active() && ( - - - - - )} - {!!website() && ( - - - - - )} - - - - - {network.data.apyEnabled !== false && ( - - - - - )} - {!!securityContact() && ( - - - - - )} - - - - - - - - - -
    Rank#{selectedValidator.rank}
    Validator Address{selectedValidator.operator_address}
    REStake Address{operator().botAddress}
    Status{status()}
    Website{website()}
    Commission{selectedValidator.commission.commission_rates.rate * 100}%
    - APY} - identifier="delegations-apy" - > -
    -

    Based on commission, compounding frequency and estimated block times.

    -

    This is an estimate and best case scenario.

    -
    -
    -
    - {Object.keys(props.validatorApy).length > 0 - ? props.validatorApy[selectedValidator.operator_address] - ? Math.round(props.validatorApy[selectedValidator.operator_address] * 100) + "%" - : "-" - : ( - - Loading... - - )} -
    Contact{securityContact()}
    Voting power{bondedTokens()}
    REStake - {!!operator() ? ( - {operator().runTimesString()} ( min) - ) : - } identifier={selectedValidator.operator_address} tooltip="This validator is not a REStake operator" /> - } -
    -

    {selectedValidator.description.details}

    -

    - -

    -
    - -
    - {redelegate - ? Redelegate from - : actionText() - } -
    - -
    - {network.authzSupport && operator() && ( - - - - )} -
    - )} -
    -
    - - ); -} - -export default Delegate diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index cab19dc00..95570c323 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -8,7 +8,7 @@ import { Alert } from 'react-bootstrap' -import { pow, multiply, divide, floor, subtract, format } from 'mathjs' +import { pow, multiply, divide, subtract } from 'mathjs' class DelegateForm extends React.Component { constructor(props) { diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 6ffdec57d..1ccd3296e 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -8,12 +8,12 @@ import ClaimRewards from "./ClaimRewards"; import RevokeRestake from "./RevokeRestake"; import ManageGrant from "./ManageGrant"; import CountdownRestake from "./CountdownRestake"; -import Delegate from "./Delegate"; +import ValidatorModal from "./ValidatorModal"; import ValidatorImage from "./ValidatorImage"; import TooltipIcon from "./TooltipIcon"; import AboutLedger from "./AboutLedger"; -import { Table, Button, Dropdown, Spinner } from "react-bootstrap"; +import { Table, Button, Dropdown, Spinner, OverlayTrigger, Tooltip } from "react-bootstrap"; import { CheckCircle, XCircle } from "react-bootstrap-icons"; import ValidatorName from "./ValidatorName"; @@ -21,13 +21,14 @@ import ValidatorName from "./ValidatorName"; class Delegations extends React.Component { constructor(props) { super(props); - this.state = { operatorGrants: {}, validatorLoading: {}, validatorApy: {} }; + this.state = { operatorGrants: {}, validatorLoading: {}, validatorApy: {}, validatorModal: {} }; this.setError = this.setError.bind(this); this.setClaimLoading = this.setClaimLoading.bind(this); this.onClaimRewards = this.onClaimRewards.bind(this); this.onGrant = this.onGrant.bind(this); this.onRevoke = this.onRevoke.bind(this); + this.hideValidatorModal = this.hideValidatorModal.bind(this); this.defaultGrant = { claimGrant: null, stakeGrant: null, @@ -338,6 +339,44 @@ class Delegations extends React.Component { ); } + showValidatorModal(address, opts){ + opts = opts || {} + const validator = address && this.props.validators[address] + this.setState({validatorModal: {show: true, validator: validator, ...opts}}) + } + + hideValidatorModal(opts){ + opts = opts || {} + this.setState((state, props) => { + return {validatorModal: {...state.validatorModal, show: false}} + }) + } + + renderValidatorModal(){ + const validatorModal = this.state.validatorModal + + return ( + + ) + } + renderValidator(validatorAddress, delegation) { const validator = this.props.validators[validatorAddress]; const isValidatorOperator = this.isValidatorOperator(validator) @@ -377,18 +416,9 @@ class Delegations extends React.Component { /> - + this.showValidatorModal(validatorAddress, {activeTab: 'profile'})}> - + {operator ? ( @@ -572,64 +602,30 @@ class Delegations extends React.Component { )}
    - - - + this.showValidatorModal(validatorAddress, {activeTab: 'delegate'})}> + Delegate + + this.showValidatorModal(validatorAddress, {redelegate: true})}> + Redelegate + + this.showValidatorModal(validatorAddress, {undelegate: true})}> + Undelegate + ) : ( - + + Delegate to enable REStake + + } + > + + ) ) : ( + {this.renderValidatorModal()}
  5. ); @@ -776,18 +763,9 @@ class Delegations extends React.Component { )}
    - +
    @@ -837,6 +815,7 @@ class Delegations extends React.Component {
    + {this.renderValidatorModal()} ); } diff --git a/src/components/ValidatorDelegate.js b/src/components/ValidatorDelegate.js new file mode 100644 index 000000000..eed5a7f0d --- /dev/null +++ b/src/components/ValidatorDelegate.js @@ -0,0 +1,37 @@ +import React from 'react'; + +import DelegateForm from './DelegateForm' +import ValidatorLink from './ValidatorLink' + +function ValidatorDelegate(props) { + const {redelegate, undelegate, network, validator, selectedValidator, onDelegate, availableBalance } = props + + const actionText = () => { + if(redelegate) return 'Redelegate' + if(undelegate) return 'Undelegate' + return 'Delegate' + } + + return ( + <> +
    + {redelegate + ? Redelegate from + : actionText() + } +
    + + + ) +} + +export default ValidatorDelegate \ No newline at end of file diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js new file mode 100644 index 000000000..03fdb505e --- /dev/null +++ b/src/components/ValidatorModal.js @@ -0,0 +1,147 @@ +import React, { useState, useEffect } from 'react'; + +import Validators from './Validators' +import ValidatorImage from './ValidatorImage' +import ValidatorLink from './ValidatorLink' +import AboutLedger from './AboutLedger'; + +import { + Button, + Modal, + Tabs, + Tab +} from 'react-bootstrap' + +import ValidatorDelegate from './ValidatorDelegate'; +import ValidatorProfile from './ValidatorProfile'; + +function ValidatorModal(props) { + const { redelegate, undelegate, validator, delegations, operators, network, validators } = props + const [selectedValidator, setSelectedValidator] = useState(); + const [activeTab, setActiveTab] = useState(); + + useEffect(() => { + if(props.activeTab && props.activeTab != activeTab){ + setActiveTab(props.activeTab) + }else if(redelegate || undelegate){ + setActiveTab('delegate') + }else if(!activeTab){ + setActiveTab('profile') + } + }, [props.activeTab, redelegate, undelegate, validator]) + + useEffect(() => { + setSelectedValidator(!redelegate && validator) + }, [validator, redelegate]) + + const handleClose = () => { + if(selectedValidator && (!validator || redelegate) && selectedValidator !== validator){ + setSelectedValidator(null) + }else{ + props.hideModal() + } + } + + const onDelegate = () => { + props.onDelegate() + props.hideModal() + } + + const excludeValidators = () => { + if (redelegate) { + return [validator.operator_address] + } else if (delegations) { + return [...Object.keys(delegations), ...operators.map(el => el.address)] + } + } + + const operator = () => { + if (!operators || !selectedValidator) return + + return network.getOperator(selectedValidator.operator_address) + } + + const availableBalance = () => { + if(redelegate || undelegate){ + return (props.delegations[validator.address] || {}).balance + }else{ + return props.balance + } + } + + const actionText = () => { + if (redelegate) return 'Redelegate' + if (undelegate) return 'Undelegate' + if (validator) { + return 'Delegate' + } else { + return 'Add Validator' + } + } + + return ( + <> + + + + {selectedValidator + ? ( + <> + + + + ) : actionText() + } + + + + {!selectedValidator && + setSelectedValidator(selectedValidator)} />} + {selectedValidator && ( + setActiveTab(k)} id="validator-tabs" className="mb-3"> + + +

    + +

    +
    + + + + {network.authzSupport && operator() && ( + + + + )} +
    + )} +
    +
    + + ); +} + +export default ValidatorModal diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js new file mode 100644 index 000000000..d3f816372 --- /dev/null +++ b/src/components/ValidatorProfile.js @@ -0,0 +1,158 @@ +import React from 'react'; + +import ValidatorLink from './ValidatorLink' +import Coins from './Coins' +import TooltipIcon from './TooltipIcon' + +import { + Table, + Spinner, +} from 'react-bootstrap' + +import { + XCircle +} from 'react-bootstrap-icons' + +function ValidatorProfile(props) { + const { validator, operator, network } = props + + const active = () => { + if(!validator) return false + + return validator.status === 'BOND_STATUS_BONDED' && !validator.jailed + } + + const status = () => { + if(!validator) return + + let status = '' + let className = '' + if(active()){ + status = 'Active' + }else{ + status = 'Inactive' + className = 'text-danger' + } + if(validator.jailed){ + status += ' (JAILED)' + className = 'text-danger' + } + + return {status} + } + + const website = () => { + if (!validator) return + + return validator.description && validator.description.website + } + + const securityContact = () => { + if (!validator) return + + return validator.description && validator.description.security_contact + } + + const bondedTokens = () => { + if (!validator) return + + const amount = validator.tokens + return + } + + const minimumReward = () => { + return { + amount: operator.minimumReward, + denom: network.denom + } + } + return ( + <> + + + {active() && ( + + + + + )} + + + + + {operator && ( + + + + + )} + {!active() && ( + + + + + )} + {!!website() && ( + + + + + )} + + + + + {network.data.apyEnabled !== false && ( + + + + + )} + {!!securityContact() && ( + + + + + )} + + + + + + + + + +
    Rank#{validator.rank}
    Validator Address{validator.operator_address}
    REStake Address{operator.botAddress}
    Status{status()}
    Website{website()}
    Commission{validator.commission.commission_rates.rate * 100}%
    + APY} + identifier="delegations-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is an estimate and best case scenario.

    +
    +
    +
    + {Object.keys(props.validatorApy).length > 0 + ? props.validatorApy[validator.operator_address] + ? Math.round(props.validatorApy[validator.operator_address] * 100) + "%" + : "-" + : ( + + Loading... + + )} +
    Contact{securityContact()}
    Voting power{bondedTokens()}
    REStake + {!!operator ? ( + {operator.runTimesString()} ( min) + ) : + } identifier={validator.operator_address} tooltip="This validator is not a REStake operator" /> + } +
    +

    {validator.description.details}

    + + ) +} + +export default ValidatorProfile \ No newline at end of file diff --git a/src/components/Wallet.js b/src/components/Wallet.js index 2b2809232..4758b4544 100644 --- a/src/components/Wallet.js +++ b/src/components/Wallet.js @@ -10,7 +10,6 @@ class Wallet extends React.Component { super(props); this.state = {} this.getDelegations = this.getDelegations.bind(this); - this.onAddValidator = this.onAddValidator.bind(this); } componentDidMount() { @@ -42,10 +41,6 @@ class Wallet extends React.Component { this.setState({refreshInterval: interval}) } - onAddValidator(){ - setTimeout(() => this.getDelegations(), 3_000) - } - async getDelegations(hideError) { this.props.queryClient.getDelegations(this.props.address) .then( @@ -102,8 +97,7 @@ class Wallet extends React.Component { delegations={this.state.delegations} queryClient={this.props.queryClient} stargateClient={this.props.stargateClient} - getDelegations={this.getDelegations} - onAddValidator={this.onAddValidator} /> + getDelegations={this.getDelegations} />
    ) } From 321a816f447c1ba4ec75094d1095996ae07316ad Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 11 Apr 2022 23:11:31 +0100 Subject: [PATCH 366/862] Move grants into validator modal and complete --- scripts/base.mjs | 2 +- src/components/CountdownRestake.js | 12 +- src/components/DelegateForm.js | 4 +- src/components/Delegations.js | 135 ++++++-------- src/components/ManageGrant.js | 271 ---------------------------- src/components/ManageRestake.js | 64 +++++++ src/components/RevokeRestake.js | 11 +- src/components/ValidatorDelegate.js | 19 ++ src/components/ValidatorGrants.js | 223 +++++++++++++++++++++++ src/components/ValidatorModal.js | 34 +++- src/components/ValidatorProfile.js | 6 - src/utils/Network.mjs | 4 +- src/utils/OperatorGrants.mjs | 54 ++++++ src/utils/SigningClient.mjs | 2 +- 14 files changed, 469 insertions(+), 372 deletions(-) delete mode 100644 src/components/ManageGrant.js create mode 100644 src/components/ManageRestake.js create mode 100644 src/components/ValidatorGrants.js create mode 100644 src/utils/OperatorGrants.mjs diff --git a/scripts/base.mjs b/scripts/base.mjs index b63bf1e74..0b34d4079 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -224,7 +224,7 @@ export class Autostake { try { return await this.getAutostakeMessage(client, item, validators) } catch (error) { - timeStamp(item, 'Failed to get address', error.message) + timeStamp(item.address, 'Failed to get address', error.message) } } }) diff --git a/src/components/CountdownRestake.js b/src/components/CountdownRestake.js index a4eee475d..d9cf3cc80 100644 --- a/src/components/CountdownRestake.js +++ b/src/components/CountdownRestake.js @@ -5,17 +5,20 @@ import TooltipIcon from './TooltipIcon'; import Countdown from 'react-countdown'; import { - ClockHistory + ClockHistory, Clock } from 'react-bootstrap-icons' +import Coins from './Coins'; function CountdownRestake(props) { - const { operator } = props + const { operator, network, maxAmount } = props const nextRun = () => { if(!operator) return null return operator.nextRun() + (60 * 1000) } + const icon = maxAmount ? : + const countdownRenderer = ({ hours, minutes, seconds, completed }) => { if (completed) { return

    Validator is restaking now

    @@ -31,12 +34,15 @@ function CountdownRestake(props) { } } return ( - } identifier={operator.address}> +
    + {maxAmount && ( +

    Grant remaining:

    + )}
    ) diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index 95570c323..cbd556bf0 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -8,7 +8,7 @@ import { Alert } from 'react-bootstrap' -import { pow, multiply, divide, subtract } from 'mathjs' +import { pow, multiply, divide, subtract, bignumber } from 'mathjs' class DelegateForm extends React.Component { constructor(props) { @@ -40,7 +40,7 @@ class DelegateForm extends React.Component { const client = this.props.stargateClient const decimals = pow(10, this.props.network.decimals) - const denomAmount = multiply(amount, decimals) + const denomAmount = bignumber(multiply(amount, decimals)) let messages = this.buildMessages(denomAmount) let gas diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 1ccd3296e..675218feb 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -6,8 +6,6 @@ import AlertMessage from "./AlertMessage"; import Coins from "./Coins"; import ClaimRewards from "./ClaimRewards"; import RevokeRestake from "./RevokeRestake"; -import ManageGrant from "./ManageGrant"; -import CountdownRestake from "./CountdownRestake"; import ValidatorModal from "./ValidatorModal"; import ValidatorImage from "./ValidatorImage"; import TooltipIcon from "./TooltipIcon"; @@ -15,8 +13,8 @@ import AboutLedger from "./AboutLedger"; import { Table, Button, Dropdown, Spinner, OverlayTrigger, Tooltip } from "react-bootstrap"; -import { CheckCircle, XCircle } from "react-bootstrap-icons"; import ValidatorName from "./ValidatorName"; +import ManageRestake from "./ManageRestake"; class Delegations extends React.Component { constructor(props) { @@ -48,12 +46,14 @@ class Delegations extends React.Component { async componentDidUpdate(prevProps) { if (this.props.network !== prevProps.network) { clearInterval(this.state.refreshInterval); + clearInterval(this.state.grantInterval); } if (!this.props.address) return; if (this.props.address !== prevProps.address) { clearInterval(this.state.refreshInterval); + clearInterval(this.state.grantInterval); const isNanoLedger = this.props.stargateClient.getIsNanoLedger(); this.setState({ isNanoLedger: isNanoLedger, @@ -74,6 +74,7 @@ class Delegations extends React.Component { componentWillUnmount() { clearInterval(this.state.refreshInterval); + clearInterval(this.state.grantInterval); } async refresh() { @@ -83,11 +84,14 @@ class Delegations extends React.Component { } refreshInterval() { - const interval = setInterval(() => { + const refreshInterval = setInterval(() => { this.props.getBalance(); this.getRewards(true); }, 15_000); - this.setState({ refreshInterval: interval }); + const grantInterval = setInterval(() => { + this.getGrants(); + }, 60_000); + this.setState({ refreshInterval, grantInterval }); } getRewards(hideError) { @@ -168,11 +172,13 @@ class Delegations extends React.Component { } onGrant(operator, expired, maxTokens) { + clearInterval(this.state.refreshInterval); + clearInterval(this.state.grantInterval); const operatorGrant = expired ? this.defaultGrant : { claimGrant: {}, stakeGrant: {}, validators: [operator.address], - maxTokens: bignumber(maxTokens.amount) + maxTokens: maxTokens ? bignumber(maxTokens.amount) : null }; this.setState((state, props) => ({ operatorGrants: _.set( @@ -183,10 +189,12 @@ class Delegations extends React.Component { error: null, validatorLoading: _.set(state.validatorLoading, operator.address, false), })); - setTimeout(() => this.getGrants(), 60_000); + this.refreshInterval() } onRevoke(operator) { + clearInterval(this.state.refreshInterval); + clearInterval(this.state.grantInterval); this.setState((state, props) => ({ operatorGrants: _.set( state.operatorGrants, @@ -196,6 +204,7 @@ class Delegations extends React.Component { error: null, validatorLoading: _.set(state.validatorLoading, operator.address, false), })); + this.refreshInterval() } onClaimRewards() { @@ -227,19 +236,23 @@ class Delegations extends React.Component { return this.props.network.authzSupport } - operatorGrants(operator) { - const grant = this.state.operatorGrants[operator.botAddress] - if(!grant) return this.defaultGrant; - return { - ...grant, - grantsValid: !!( - grant.claimGrant && - grant.stakeGrant && - (!grant.validators || grant.validators.includes(operator.address)) && - (grant.maxTokens === null || larger(grant.maxTokens, this.validatorReward(operator.address))) - ), - grantsExist: !!(grant.claimGrant || grant.stakeGrant), - } + operatorGrants() { + if(!this.state.operatorGrants) return {} + return this.props.operators.reduce((sum, operator) => { + let grant = this.state.operatorGrants[operator.botAddress] + if (!grant) grant = this.defaultGrant; + sum[operator.botAddress] = { + ...grant, + grantsValid: !!( + grant.claimGrant && + grant.stakeGrant && + (!grant.validators || grant.validators.includes(operator.address)) && + (grant.maxTokens === null || larger(grant.maxTokens, this.validatorReward(operator.address))) + ), + grantsExist: !!(grant.claimGrant || grant.stakeGrant), + } + return sum + }, {}) } restakePossible() { @@ -370,9 +383,13 @@ class Delegations extends React.Component { balance={this.props.balance} rewards={this.state.rewards} delegations={this.props.delegations} + grants={this.operatorGrants()} stargateClient={this.props.stargateClient} hideModal={this.hideValidatorModal} onDelegate={this.onClaimRewards} + onGrant={this.onGrant} + onRevoke={this.onRevoke} + setError={this.setError} /> ) } @@ -385,7 +402,7 @@ class Delegations extends React.Component { this.state.rewards && this.state.rewards[validatorAddress]; const denomRewards = rewards && this.denomRewards(rewards); const operator = this.operatorForValidator(validatorAddress); - const grants = operator && this.operatorGrants(operator) + const grants = operator && this.operatorGrants()[operator.botAddress] let rowVariant = operator && delegation ? grants.grantsValid @@ -421,48 +438,17 @@ class Delegations extends React.Component { - {operator ? ( - this.authzSupport() && delegation ? ( - this.operatorGrants(operator).grantsValid ? ( - - ) : this.restakePossible() ? ( - - ) : ( - } - identifier={validatorAddress} - tooltip="This validator can REStake your rewards" - /> - ) - ) : ( - } - identifier={validatorAddress} - tooltip="This validator can REStake your rewards" - /> - ) - ) : ( - } - identifier={validatorAddress} - tooltip="This validator is not a REStake operator" - /> - )} + this.showValidatorModal(validatorAddress, { activeTab: 'restake' })} + /> {operator && ( @@ -527,23 +513,14 @@ class Delegations extends React.Component { > Manage - {operator && this.restakePossible() && ( <> - - {this.operatorGrants(operator).grantsExist && ( + this.showValidatorModal(validatorAddress, { activeTab: 'restake' })}> + {this.operatorGrants()[operator.botAddress].grantsValid ? 'Manage REStake' : 'Enable REStake'} + + {this.operatorGrants()[operator.botAddress].grantsExist && ( )}
    - this.showValidatorModal(validatorAddress, {activeTab: 'delegate'})}> + this.showValidatorModal(validatorAddress, { activeTab: 'delegate' })}> Delegate - this.showValidatorModal(validatorAddress, {redelegate: true})}> + this.showValidatorModal(validatorAddress, { redelegate: true })}> Redelegate - this.showValidatorModal(validatorAddress, {undelegate: true})}> + this.showValidatorModal(validatorAddress, { undelegate: true })}> Undelegate
    diff --git a/src/components/ManageGrant.js b/src/components/ManageGrant.js deleted file mode 100644 index 82041e411..000000000 --- a/src/components/ManageGrant.js +++ /dev/null @@ -1,271 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import moment from 'moment' -import {pow, multiply, divide, larger, smaller} from 'mathjs' - -import { GenericAuthorization } from "cosmjs-types/cosmos/authz/v1beta1/authz"; -import { StakeAuthorization } from "cosmjs-types/cosmos/staking/v1beta1/authz"; -import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; - -import { - Button, - OverlayTrigger, - Tooltip, - Modal, - Dropdown, - Form, - Table -} from 'react-bootstrap' - -import Coins from './Coins'; -import { coin } from '../utils/Helpers.mjs'; - -function ManageGrant(props) { - const { grants, operator, address, network } = props - const { claimGrant, stakeGrant, grantsValid, grantsExist } = grants - const defaultExpiry = moment().add(1, 'year') - const [loading, setLoading] = useState(false); - const [show, setShow] = useState(false); - const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry}); - - useEffect(() => { - const maxTokens = stakeGrant?.authorization?.max_tokens - const claimExpiry = claimGrant && claimGrant.expiration && moment(claimGrant.expiration) - const stakeExpiry = stakeGrant && stakeGrant.expiration && moment(stakeGrant.expiration) - const expiryDate = claimExpiry && stakeExpiry && claimExpiry > stakeExpiry ? stakeExpiry : claimExpiry || stakeExpiry - setState({ - ...state, - maxTokens, - expiryDate, - maxTokensValue: maxTokens && state.maxTokensValue === '' ? divide(maxTokens.amount, pow(10, network.decimals)) : state.maxTokensValue, - expiryDateValue: (expiryDate || defaultExpiry).format('YYYY-MM-DD') - }) - }, [grants]) - - function handleOpen() { - setShow(true); - } - - function handleClose() { - setShow(false); - } - - function handleInputChange(e) { - setState({...state, [e.target.name]: e.target.value }); - } - - function maxTokensDenom() { - if(state.maxTokensValue === '') return - - const decimals = pow(10, network.decimals) - return multiply(state.maxTokensValue, decimals) - } - - function maxTokensValid() { - return !maxTokensDenom() || larger(maxTokensDenom(), props.rewards) - } - - function showLoading(isLoading){ - setLoading(isLoading) - props.setLoading && props.setLoading(isLoading) - } - - function handleSubmit(event){ - event.preventDefault() - showLoading(true) - const expiry = moment(state.expiryDateValue) - let maxTokens - if(state.maxTokensValue !== ''){ - maxTokens = coin(maxTokensDenom(), network.denom) - } - - const messages = [ - buildGrantMsg("/cosmos.staking.v1beta1.StakeAuthorization", - StakeAuthorization.encode(StakeAuthorization.fromPartial({ - allowList: {address: [operator.address]}, - maxTokens: maxTokens, - authorizationType: 1 - })).finish(), - expiry - ), - buildGrantMsg("/cosmos.authz.v1beta1.GenericAuthorization", - GenericAuthorization.encode(GenericAuthorization.fromPartial({ - msg: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward' - })).finish(), - expiry - ) - ] - console.log(messages) - - props.stargateClient.signAndBroadcast(address, messages).then((result) => { - console.log("Successfully broadcasted:", result); - showLoading(false) - props.onGrant(operator, moment().diff(expiry, "seconds") <= 0, maxTokens); - setShow(false); - }, (error) => { - console.log('Failed to broadcast:', error) - showLoading(false) - props.setError('Failed to broadcast: ' + error.message) - }) - } - - function buildGrantMsg(type, value, expiryDate){ - return { - typeUrl: "/cosmos.authz.v1beta1.MsgGrant", - value: { - granter: address, - grantee: operator.botAddress, - grant: { - authorization: { - typeUrl: type, - value: value - }, - expiration: Timestamp.fromPartial({ - seconds: expiryDate.unix(), - nanos: 0 - }) - } - }, - } - } - - function button(){ - if(props.dropdownItem){ - return ( - handleOpen()} > - {grants.grantsValid ? 'Manage REStake' : 'Enable REStake'} - - ) - } - return ( - - ) - } - - function loadingButton(){ - return ( - - ) - } - - const minimumReward = () => { - return { - amount: operator.minimumReward, - denom: network.denom - } - } - - return ( - <> - {!loading || props.dropdownItem - ? ( - <> - {props.tooltip ? ( - - {props.tooltip} - - } - >{button()} - ) : button()} - - ) : ( - loadingButton() - ) - } - - - - Manage REStake - - - -
    - - - - - - - - - - - - - - - - - - - - - - - {state.maxTokens && ( - - - - - )} - - - - - -
    Validator{operator.moniker}
    REStake Address{operator.botAddress}
    Frequency - {operator.runTimesString()} -
    Minimum Reward - -
    Current Rewards - -
    Grant Remaining - -
    Grant status - {grantsValid - ? Valid - : grantsExist - ? state.maxTokens && smaller(state.maxTokens.amount, props.rewards) - ? Not enough grant remaining - : Invalid : Missing} -
    - - Maximum delegation -
    -
    - - {network.symbol.toUpperCase()} -
    -
    - Remaining amount this validator can delegate
    Leave empty for unlimited -
    -
    -
    - - Expiry date - -
    Date the grant will expire. After this date you will need to re-grant
    -
    -

    - {!loading - ? - : - } -

    -
    -
    -
    - - ) -} - -export default ManageGrant; diff --git a/src/components/ManageRestake.js b/src/components/ManageRestake.js new file mode 100644 index 000000000..4ba56db7a --- /dev/null +++ b/src/components/ManageRestake.js @@ -0,0 +1,64 @@ +import React from "react"; +import _ from "lodash"; +import CountdownRestake from "./CountdownRestake"; +import TooltipIcon from "./TooltipIcon"; + +import { Button, OverlayTrigger, Tooltip } from "react-bootstrap"; + +import { CheckCircle, XCircle } from "react-bootstrap-icons"; + +function ManageRestake(props) { + const { validator, operator, delegation, grants, network, authzSupport, restakePossible } = props + + return ( + <> + {operator ? ( + authzSupport && delegation ? ( + grants.grantsValid ? ( + + + + ) : restakePossible ? ( + + {grants.grantsExist ? 'Update grants to re-enable REStake' : 'Authorize validator to REStake for you'} + + } + > + + + ) : ( + } + identifier={validator.operator_address} + tooltip="This validator can REStake your rewards" + /> + ) + ) : ( + } + identifier={validator.operator_address} + tooltip="This validator can REStake your rewards" + /> + ) + ) : ( + } + identifier={validator.operator_address} + tooltip="This validator is not a REStake operator" + /> + )} + + ) +} + +export default ManageRestake; diff --git a/src/components/RevokeRestake.js b/src/components/RevokeRestake.js index 9f5d6599f..7184fe51d 100644 --- a/src/components/RevokeRestake.js +++ b/src/components/RevokeRestake.js @@ -1,5 +1,6 @@ import { - Dropdown + Dropdown, + Button } from 'react-bootstrap' function RevokeRestake(props) { @@ -34,6 +35,14 @@ function RevokeRestake(props) { } } + if(props.button){ + return ( + + ) + } + return ( revoke()} > Disable REStake diff --git a/src/components/ValidatorDelegate.js b/src/components/ValidatorDelegate.js index eed5a7f0d..65a4a513b 100644 --- a/src/components/ValidatorDelegate.js +++ b/src/components/ValidatorDelegate.js @@ -1,8 +1,13 @@ import React from 'react'; +import Coins from './Coins'; import DelegateForm from './DelegateForm' import ValidatorLink from './ValidatorLink' +import { + Table +} from 'react-bootstrap' + function ValidatorDelegate(props) { const {redelegate, undelegate, network, validator, selectedValidator, onDelegate, availableBalance } = props @@ -20,6 +25,20 @@ function ValidatorDelegate(props) { : actionText() } + + + + + + + + + + + +
    Current Delegation
    Current Rewards + +
    { + const claimExpiry = claimGrant && claimGrant.expiration && moment(claimGrant.expiration) + const stakeExpiry = stakeGrant && stakeGrant.expiration && moment(stakeGrant.expiration) + const expiryDate = claimExpiry && stakeExpiry && claimExpiry > stakeExpiry ? stakeExpiry : claimExpiry || stakeExpiry + setState({ + ...state, + maxTokens, + expiryDate, + expiryDateValue: (expiryDate || defaultExpiry).format('YYYY-MM-DD') + }) + }, [grants, operator]) + + useEffect(() => { + setState({ + ...state, + maxTokensValue: maxTokens && state.maxTokensValue === '' ? divide(maxTokens, pow(10, network.decimals)) : maxTokens ? state.maxTokensValue : '', + }) + }, [operator]) + + function handleInputChange(e) { + setState({ ...state, [e.target.name]: e.target.value }); + } + + function maxTokensDenom() { + if (state.maxTokensValue === '') return + + const decimals = pow(10, network.decimals) + return bignumber(multiply(state.maxTokensValue, decimals)) + } + + function maxTokensValid() { + return !maxTokensDenom() || larger(maxTokensDenom(), props.rewards) + } + + function showLoading(isLoading) { + setLoading(isLoading) + props.setLoading && props.setLoading(isLoading) + } + + function handleSubmit(event) { + event.preventDefault() + showLoading(true) + const expiry = moment(state.expiryDateValue) + let maxTokens + if (state.maxTokensValue !== '') { + maxTokens = coin(maxTokensDenom(), network.denom) + } + + const messages = [ + buildGrantMsg("/cosmos.staking.v1beta1.StakeAuthorization", + StakeAuthorization.encode(StakeAuthorization.fromPartial({ + allowList: { address: [operator.address] }, + maxTokens: maxTokens, + authorizationType: 1 + })).finish(), + expiry + ), + buildGrantMsg("/cosmos.authz.v1beta1.GenericAuthorization", + GenericAuthorization.encode(GenericAuthorization.fromPartial({ + msg: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward' + })).finish(), + expiry + ) + ] + console.log(messages) + + props.stargateClient.signAndBroadcast(address, messages).then((result) => { + console.log("Successfully broadcasted:", result); + showLoading(false) + props.onGrant(operator, moment().diff(expiry, "seconds") >= 0, maxTokens); + }, (error) => { + console.log('Failed to broadcast:', error) + showLoading(false) + props.setError('Failed to broadcast: ' + error.message) + }) + } + + function buildGrantMsg(type, value, expiryDate) { + return { + typeUrl: "/cosmos.authz.v1beta1.MsgGrant", + value: { + granter: address, + grantee: operator.botAddress, + grant: { + authorization: { + typeUrl: type, + value: value + }, + expiration: Timestamp.fromPartial({ + seconds: expiryDate.unix(), + nanos: 0 + }) + } + }, + } + } + + const minimumReward = () => { + return { + amount: operator.minimumReward, + denom: network.denom + } + } + + return ( + <> +
    + + + + + + + + + + + + + + + + + + + {state.maxTokens && ( + + + + + )} + + + + + +
    REStake Address{operator.botAddress}
    Frequency + {operator.runTimesString()} +
    Minimum Reward + +
    Current Rewards + +
    Grant Remaining + +
    Grant status + {grantsValid + ? Active + : grantsExist + ? state.maxTokens && smaller(state.maxTokens, props.rewards) + ? Not enough grant remaining + : Invalid : Inactive} +
    + + Total delegation +
    +
    + + {network.symbol.toUpperCase()} +
    +
    + Reduces with every delegation made by the validator
    Leave empty for unlimited +
    +
    +
    + + Expiry date + +
    Date the grant will expire. After this date you will need to re-grant
    +
    +

    + {!loading + ? ( + <> + {grants.grantsExist && ( + showLoading(loading)} + setError={props.setError} + /> + )} + + + ) + : + } +

    +
    + + ) +} + +export default ValidatorGrants; diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index 03fdb505e..71a174f02 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { bignumber } from 'mathjs' import Validators from './Validators' import ValidatorImage from './ValidatorImage' @@ -14,9 +15,10 @@ import { import ValidatorDelegate from './ValidatorDelegate'; import ValidatorProfile from './ValidatorProfile'; +import ValidatorGrants from './ValidatorGrants'; function ValidatorModal(props) { - const { redelegate, undelegate, validator, delegations, operators, network, validators } = props + const { redelegate, undelegate, validator, delegations, operators, network, validators, grants } = props const [selectedValidator, setSelectedValidator] = useState(); const [activeTab, setActiveTab] = useState(); @@ -69,6 +71,14 @@ function ValidatorModal(props) { } } + const rewards = () => { + if (!rewards) return 0; + const denom = network.denom; + const validatorReward = props.rewards[selectedValidator.address]; + const reward = validatorReward && validatorReward.reward.find((el) => el.denom === denom) + return reward ? bignumber(reward.amount) : 0 + } + const actionText = () => { if (redelegate) return 'Redelegate' if (undelegate) return 'Undelegate' @@ -113,11 +123,6 @@ function ValidatorModal(props) { validator={selectedValidator} operator={operator()} validatorApy={props.validatorApy} /> -

    - -

    + {network.authzSupport && operator() && ( + + + + )} {network.authzSupport && operator() && ( diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js index d3f816372..a2b09a346 100644 --- a/src/components/ValidatorProfile.js +++ b/src/components/ValidatorProfile.js @@ -80,12 +80,6 @@ function ValidatorProfile(props) { Validator Address {validator.operator_address} - {operator && ( - - REStake Address - {operator.botAddress} - - )} {!active() && ( Status diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 7f0eb557d..d9a13ca3c 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -1,5 +1,5 @@ import _ from 'lodash' -import { multiply, pow } from 'mathjs' +import { multiply, pow, format, bignumber } from 'mathjs' import QueryClient from './QueryClient.mjs' import SigningClient from './SigningClient.mjs' import ApyClient from '../ApyClient.mjs' @@ -32,7 +32,7 @@ const Network = async (data, withoutQueryClient) => { } }) - const defaultGasPrice = multiply(0.000000025, pow(10, chain.decimals)).toString() + chain.denom + const defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, chain.decimals))), {notation: 'fixed'}) + chain.denom let queryClient if(!withoutQueryClient){ diff --git a/src/utils/OperatorGrants.mjs b/src/utils/OperatorGrants.mjs new file mode 100644 index 000000000..84048c0ce --- /dev/null +++ b/src/utils/OperatorGrants.mjs @@ -0,0 +1,54 @@ +const OperatorGrants = (grants) => { + let grantValidators, maxTokens; + + const claimGrant = grants.find((el) => { + if ( + el.authorization["@type"] === + "/cosmos.authz.v1beta1.GenericAuthorization" && + el.authorization.msg === + "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward" + ) { + return Date.parse(el.expiration) > new Date(); + } else { + return false; + } + }); + const stakeGrant = grants.find((el) => { + if ( + el.authorization["@type"] === + "/cosmos.staking.v1beta1.StakeAuthorization" || ( + // Handle GenericAuthorization for Ledger + el.authorization["@type"] === + "/cosmos.authz.v1beta1.GenericAuthorization" && + el.authorization.msg === + "/cosmos.staking.v1beta1.MsgDelegate" + ) + ) { + return Date.parse(el.expiration) > new Date(); + } else { + return false; + } + }) + + if (stakeGrant) { + grantValidators = + stakeGrant.authorization.allow_list?.address; + maxTokens = stakeGrant.authorization.max_tokens + } + + return { + claimGrant, + stakeGrant, + validators: grantValidators || [], + maxTokens: maxTokens && bignumber(maxTokens.amount), + grantsValid: !!( + claimGrant && stakeGrant && + (!validators || validators.includes(operator.address)) && + (maxTokens === null || larger(maxTokens, this.validatorReward(operator.address))) + ), + grantsExist: !!(claimGrant || stakeGrant), + } +} + +export default OperatorGrants; + diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 391f549d2..b5e09cfd5 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -29,7 +29,7 @@ async function SigningClient(rpcUrl, defaultGasPrice, signer, key, signerOpts) { function calculateFee(gasLimit, gasPrice) { const processedGasPrice = typeof gasPrice === "string" ? GasPrice.fromString(gasPrice) : gasPrice; const { denom, amount: gasPriceAmount } = processedGasPrice; - const amount = ceil(multiply(bignumber(gasPriceAmount.toString()), bignumber(gasLimit.toString()))); + const amount = ceil(bignumber(multiply(bignumber(gasPriceAmount.toString()), bignumber(gasLimit.toString())))); return { amount: [coin(amount, denom)], gas: gasLimit.toString(), From ecd9fbb3548adffe8902fc14e64e707714da193b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 11 Apr 2022 23:32:01 +0100 Subject: [PATCH 367/862] Deep link to validator profiles --- src/components/App.js | 1 + src/components/Delegations.js | 40 ++++++++++++++++----------- src/components/NetworkFinder.js | 9 ++++++- src/components/ValidatorDelegate.js | 42 ++++++++++++++++++++++++----- src/components/ValidatorGrants.js | 4 +-- src/components/ValidatorModal.js | 16 +++++++++-- src/components/ValidatorProfile.js | 14 +++++----- src/components/Wallet.js | 1 + src/index.js | 1 + 9 files changed, 94 insertions(+), 34 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 10d85f0f6..aeeb0fef2 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -236,6 +236,7 @@ class App extends React.Component { address={this.state.address} operators={this.props.operators} validators={this.props.validators} + validator={this.props.validator} balance={this.state.balance} getBalance={this.getBalance} queryClient={this.state.queryClient} diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 675218feb..bec90a9f6 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -41,6 +41,10 @@ class Delegations extends React.Component { this.setState({ isNanoLedger: isNanoLedger }); this.getGrants() this.refresh(); + + if(this.props.validator){ + this.showValidatorModal(this.props.validator.operator_address) + } } async componentDidUpdate(prevProps) { @@ -70,6 +74,10 @@ class Delegations extends React.Component { if (delegationsChanged) { this.getGrants() } + + if(prevProps.validator !== this.props.validator && !this.state.validatorModal.show){ + this.showValidatorModal(this.props.validator.operator_address) + } } componentWillUnmount() { @@ -452,21 +460,23 @@ class Delegations extends React.Component { {operator && ( - {operator.frequency()}
    } - identifier={operator.address} - > -
    -

    REStakes {operator.runTimesString()}

    -

    - Minimum reward is{" "} - -

    -
    - + this.showValidatorModal(validatorAddress, { activeTab: 'restake' })}> + {operator.frequency()}} + identifier={operator.address} + > +
    +

    REStakes {operator.runTimesString()}

    +

    + Minimum reward is{" "} + +

    +
    +
    +
    )} {this.props.network.apyEnabled !== false && ( diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 73f37c9fb..6fa2c01d9 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -100,6 +100,13 @@ function NetworkFinder() { } }, [state.network]) + useEffect(() => { + const validatorAddresses = state.validators && Object.keys(state.validators) + if(validatorAddresses && validatorAddresses.includes(params.validator)){ + setState({ validator: state.validators[params.validator] }) + } + }, [state.validators, params.validator]) + if (state.error) { return } @@ -115,7 +122,7 @@ function NetworkFinder() { } return changeNetwork(network, validators)} />; } diff --git a/src/components/ValidatorDelegate.js b/src/components/ValidatorDelegate.js index 65a4a513b..c2ba0f30f 100644 --- a/src/components/ValidatorDelegate.js +++ b/src/components/ValidatorDelegate.js @@ -1,11 +1,13 @@ import React from 'react'; import Coins from './Coins'; +import TooltipIcon from './TooltipIcon' import DelegateForm from './DelegateForm' import ValidatorLink from './ValidatorLink' import { - Table + Table, + Spinner } from 'react-bootstrap' function ValidatorDelegate(props) { @@ -19,14 +21,34 @@ function ValidatorDelegate(props) { return ( <> -
    - {redelegate - ? Redelegate from - : actionText() - } -
    + {network.data.apyEnabled !== false && ( + + + + + )} @@ -39,6 +61,12 @@ function ValidatorDelegate(props) {
    + APY} + identifier="delegations-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is an estimate and best case scenario.

    +
    +
    +
    + {Object.keys(props.validatorApy).length > 0 + ? props.validatorApy[selectedValidator.operator_address] + ? {Math.round(props.validatorApy[selectedValidator.operator_address] * 100)}% + : "-" + : ( + + Loading... + + )} +
    Current Delegation
    +
    + {redelegate + ? Redelegate from + : actionText() + } +
    REStake Address - {operator.botAddress} + {operator.botAddress} Frequency @@ -179,7 +179,7 @@ function ValidatorGrants(props) { Total delegation
    - + {network.symbol.toUpperCase()}
    diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index 71a174f02..fbea16d99 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { useParams, useNavigate } from "react-router-dom"; import { bignumber } from 'mathjs' import Validators from './Validators' @@ -19,8 +20,18 @@ import ValidatorGrants from './ValidatorGrants'; function ValidatorModal(props) { const { redelegate, undelegate, validator, delegations, operators, network, validators, grants } = props - const [selectedValidator, setSelectedValidator] = useState(); + const [selectedValidator, setSelectedValidator] = useState(!redelegate && validator); const [activeTab, setActiveTab] = useState(); + const navigate = useNavigate() + const params = useParams(); + + useEffect(() => { + if(props.show && selectedValidator && params.validator !== selectedValidator.operator_address){ + navigate(`/${network.name}/${selectedValidator.operator_address}`) + }else if(params.validator && !props.show){ + navigate(`/${network.name}`) + } + }, [props.show, params.validator, selectedValidator]) useEffect(() => { if(props.activeTab && props.activeTab != activeTab){ @@ -72,7 +83,7 @@ function ValidatorModal(props) { } const rewards = () => { - if (!rewards) return 0; + if (!props.rewards) return 0; const denom = network.denom; const validatorReward = props.rewards[selectedValidator.address]; const reward = validatorReward && validatorReward.reward.find((el) => el.denom === denom) @@ -135,6 +146,7 @@ function ValidatorModal(props) { availableBalance={availableBalance()} delegation={delegations[selectedValidator.operator_address]} rewards={rewards()} + validatorApy={props.validatorApy} stargateClient={props.stargateClient} onDelegate={onDelegate} /> diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js index a2b09a346..044b681af 100644 --- a/src/components/ValidatorProfile.js +++ b/src/components/ValidatorProfile.js @@ -73,12 +73,12 @@ function ValidatorProfile(props) { {active() && ( Rank - #{validator.rank} + #{validator.rank} )} Validator Address - {validator.operator_address} + {validator.operator_address} {!active() && ( @@ -94,13 +94,13 @@ function ValidatorProfile(props) { )} Commission - {validator.commission.commission_rates.rate * 100}% + {validator.commission.commission_rates.rate * 100}% {network.data.apyEnabled !== false && ( APY} + icon={APY} identifier="delegations-apy" >
    @@ -112,7 +112,7 @@ function ValidatorProfile(props) { {Object.keys(props.validatorApy).length > 0 ? props.validatorApy[validator.operator_address] - ? Math.round(props.validatorApy[validator.operator_address] * 100) + "%" + ? {Math.round(props.validatorApy[validator.operator_address] * 100)}% : "-" : ( @@ -130,7 +130,7 @@ function ValidatorProfile(props) { )} Voting power - {bondedTokens()} + {bondedTokens()} REStake @@ -138,7 +138,7 @@ function ValidatorProfile(props) { {!!operator ? ( {operator.runTimesString()} ( min) ) : - } identifier={validator.operator_address} tooltip="This validator is not a REStake operator" /> + } identifier={validator.operator_address} tooltip="This validator is not a REStake operator" /> } diff --git a/src/components/Wallet.js b/src/components/Wallet.js index 4758b4544..f908b98be 100644 --- a/src/components/Wallet.js +++ b/src/components/Wallet.js @@ -93,6 +93,7 @@ class Wallet extends React.Component { balance={this.props.balance} operators={this.props.operators} validators={this.props.validators} + validator={this.props.validator} getBalance={this.props.getBalance} delegations={this.state.delegations} queryClient={this.props.queryClient} diff --git a/src/index.js b/src/index.js index 43195106c..21511b048 100644 --- a/src/index.js +++ b/src/index.js @@ -19,6 +19,7 @@ const app = ( } /> } /> + } /> From a304012e0ac32fd64169e61b3a078d6c1f315bda Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 12 Apr 2022 00:15:06 +0100 Subject: [PATCH 368/862] Add warning if different withdraw address set --- src/components/Delegations.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index bec90a9f6..3a93ace45 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -86,6 +86,7 @@ class Delegations extends React.Component { } async refresh() { + this.getWithdrawAddress(); this.getRewards(); this.calculateApy(); this.refreshInterval(); @@ -102,6 +103,13 @@ class Delegations extends React.Component { this.setState({ refreshInterval, grantInterval }); } + async getWithdrawAddress(){ + const withdraw = await this.props.queryClient.getWithdrawAddress(this.props.address) + if(withdraw !== this.props.address){ + this.setState({error: 'You have a different withdraw address set. REStake WILL NOT WORK!'}) + } + } + getRewards(hideError) { this.props.queryClient .getRewards(this.props.address, this.props.network.denom) From 64ccb9fc66f4db4d8bad08c9ce2ed5c71d1c542d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 12 Apr 2022 00:28:20 +0100 Subject: [PATCH 369/862] Clear validator and don't overwrite expiry date --- src/components/Delegations.js | 2 +- src/components/NetworkFinder.js | 2 ++ src/components/ValidatorGrants.js | 16 ++++++++++------ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 3a93ace45..0fd229e28 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -75,7 +75,7 @@ class Delegations extends React.Component { this.getGrants() } - if(prevProps.validator !== this.props.validator && !this.state.validatorModal.show){ + if(prevProps.validator !== this.props.validator && this.props.validator && !this.state.validatorModal.show){ this.showValidatorModal(this.props.validator.operator_address) } } diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 6fa2c01d9..6b8335ca9 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -104,6 +104,8 @@ function NetworkFinder() { const validatorAddresses = state.validators && Object.keys(state.validators) if(validatorAddresses && validatorAddresses.includes(params.validator)){ setState({ validator: state.validators[params.validator] }) + }else if(state.validator){ + setState({ validator: null }) } }, [state.validators, params.validator]) diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 9df63a574..728b73cac 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -21,23 +21,20 @@ function ValidatorGrants(props) { const { claimGrant, stakeGrant, maxTokens, grantsValid, grantsExist } = grants const defaultExpiry = moment().add(1, 'year') const [loading, setLoading] = useState(false); - const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry }); + const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry.format('YYYY-MM-DD') }); useEffect(() => { - const claimExpiry = claimGrant && claimGrant.expiration && moment(claimGrant.expiration) - const stakeExpiry = stakeGrant && stakeGrant.expiration && moment(stakeGrant.expiration) - const expiryDate = claimExpiry && stakeExpiry && claimExpiry > stakeExpiry ? stakeExpiry : claimExpiry || stakeExpiry setState({ ...state, maxTokens, - expiryDate, - expiryDateValue: (expiryDate || defaultExpiry).format('YYYY-MM-DD') + expiryDate: expiryDate(), }) }, [grants, operator]) useEffect(() => { setState({ ...state, + expiryDateValue: (expiryDate() || defaultExpiry).format('YYYY-MM-DD'), maxTokensValue: maxTokens && state.maxTokensValue === '' ? divide(maxTokens, pow(10, network.decimals)) : maxTokens ? state.maxTokensValue : '', }) }, [operator]) @@ -46,6 +43,13 @@ function ValidatorGrants(props) { setState({ ...state, [e.target.name]: e.target.value }); } + function expiryDate(){ + const claimExpiry = claimGrant && claimGrant.expiration && moment(claimGrant.expiration) + const stakeExpiry = stakeGrant && stakeGrant.expiration && moment(stakeGrant.expiration) + const expiryDate = claimExpiry && stakeExpiry && claimExpiry > stakeExpiry ? stakeExpiry : claimExpiry || stakeExpiry + return expiryDate + } + function maxTokensDenom() { if (state.maxTokensValue === '') return From 2bdaa4c38190dc867d8589f0a6cade9e1270363e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 12 Apr 2022 00:45:41 +0100 Subject: [PATCH 370/862] Explain grants --- src/components/ValidatorGrants.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 728b73cac..94451dce3 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -196,6 +196,10 @@ function ValidatorGrants(props) {
    Date the grant will expire. After this date you will need to re-grant
    +

    The following Authz authorisations will be made, allowing {operator.moniker} to carry out certain transactions on your behalf.

    +

    WidthdrawDelegatorRewards - allowed to withdraw/claim rewards for any validator, however REStake only withdraws from {operator.moniker}'s reward balance.

    +

    Delegate - allowed to delegate {maxTokensDenom() ? : 'any amount'} to only their own validator. REStake only re-delegates {operator.moniker}'s accrued rewards and tries not to touch your balance.

    +

    Both of these grants will expire automatically on {state.expiryDateValue}.

    {!loading ? ( From 3556cfcda338c7bf555ab4e56477fac01c0d13fa Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 12 Apr 2022 01:07:26 +0100 Subject: [PATCH 371/862] Final tweaks --- src/components/ValidatorGrants.js | 6 ++++++ src/components/ValidatorModal.js | 18 +++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 94451dce3..5bbde0ba6 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -15,6 +15,7 @@ import { import Coins from './Coins'; import { coin } from '../utils/Helpers.mjs'; import RevokeRestake from './RevokeRestake'; +import AlertMessage from './AlertMessage'; function ValidatorGrants(props) { const { grants, operator, address, network } = props @@ -133,6 +134,11 @@ function ValidatorGrants(props) { return ( <> + {!props.delegation && ( + + You must delegate to {operator.moniker} before they can REStake for you. + + )}

    diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index fbea16d99..4f6b08978 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -26,7 +26,7 @@ function ValidatorModal(props) { const params = useParams(); useEffect(() => { - if(props.show && selectedValidator && params.validator !== selectedValidator.operator_address){ + if(props.show && selectedValidator && validator.operator_address === selectedValidator.operator_address && params.validator !== selectedValidator.operator_address){ navigate(`/${network.name}/${selectedValidator.operator_address}`) }else if(params.validator && !props.show){ navigate(`/${network.name}`) @@ -41,11 +41,13 @@ function ValidatorModal(props) { }else if(!activeTab){ setActiveTab('profile') } - }, [props.activeTab, redelegate, undelegate, validator]) + }, [props.activeTab, redelegate, undelegate, selectedValidator]) useEffect(() => { - setSelectedValidator(!redelegate && validator) - }, [validator, redelegate]) + if(props.show){ + setSelectedValidator(!redelegate && validator) + } + }, [validator, redelegate, props.show]) const handleClose = () => { if(selectedValidator && (!validator || redelegate) && selectedValidator !== validator){ @@ -55,6 +57,11 @@ function ValidatorModal(props) { } } + const selectValidator = (selectedValidator) => { + setSelectedValidator(selectedValidator) + setActiveTab('profile') + } + const onDelegate = () => { props.onDelegate() props.hideModal() @@ -125,7 +132,7 @@ function ValidatorModal(props) { validators={validators} validatorApy={props.validatorApy} delegations={delegations} - selectValidator={(selectedValidator) => setSelectedValidator(selectedValidator)} />} + selectValidator={(selectedValidator) => selectValidator(selectedValidator)} />} {selectedValidator && ( setActiveTab(k)} id="validator-tabs" className="mb-3"> @@ -157,6 +164,7 @@ function ValidatorModal(props) { network={network} operator={operator()} grants={grants[operator().botAddress]} + delegation={delegations[selectedValidator.operator_address]} rewards={rewards()} stargateClient={props.stargateClient} onGrant={props.onGrant} From 727d8ec2a5eb19b693456bfae4bcac56b2b393e0 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 12 Apr 2022 01:14:46 +0100 Subject: [PATCH 372/862] Update lockfile --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index a39608d7a..ea8ec0eb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "dotenv": "^16.0.0", "fuzzy-search": "^3.2.1", "mathjs": "^10.4.3", - "moment": "^2.29.1", + "moment": "^2.29.2", "parse-duration": "^1.0.2", "path": "^0.12.7", "react": "^17.0.2", @@ -8269,9 +8269,9 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "node_modules/moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", + "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==", "engines": { "node": "*" } @@ -16176,9 +16176,9 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", + "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==" }, "ms": { "version": "2.1.2", From 214b3bc4bedcc493cec6e9266db38755dc259822 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 12 Apr 2022 01:35:31 +0100 Subject: [PATCH 373/862] Handle edge cases --- src/components/Delegations.js | 2 +- src/components/ValidatorModal.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 0fd229e28..966a026d4 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -459,7 +459,7 @@ class Delegations extends React.Component { network={this.props.network} validator={validator} operator={operator} - grants={this.operatorGrants()[operator.botAddress]} + grants={operator && this.operatorGrants()[operator.botAddress]} delegation={delegation} authzSupport={this.authzSupport()} restakePossible={this.restakePossible()} diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index 4f6b08978..52f8128f8 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -26,7 +26,7 @@ function ValidatorModal(props) { const params = useParams(); useEffect(() => { - if(props.show && selectedValidator && validator.operator_address === selectedValidator.operator_address && params.validator !== selectedValidator.operator_address){ + if(props.show && selectedValidator && validator?.operator_address === selectedValidator.operator_address && params.validator !== selectedValidator.operator_address){ navigate(`/${network.name}/${selectedValidator.operator_address}`) }else if(params.validator && !props.show){ navigate(`/${network.name}`) From ba224f844e402f92f6d5fdb48ed0790e002a87ea Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 12 Apr 2022 18:51:38 +0100 Subject: [PATCH 374/862] Validations for grant fields --- src/components/ValidatorGrants.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 5bbde0ba6..83b3102a5 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -189,7 +189,7 @@ function ValidatorGrants(props) { Total delegation
    - + {network.symbol.toUpperCase()}
    @@ -199,7 +199,7 @@ function ValidatorGrants(props) { Expiry date - +
    Date the grant will expire. After this date you will need to re-grant

    The following Authz authorisations will be made, allowing {operator.moniker} to carry out certain transactions on your behalf.

    From dd332a47efaa724a8377e74d71649f7d6311ad63 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 12 Apr 2022 19:40:48 +0100 Subject: [PATCH 375/862] Hide grant background errors --- src/components/Delegations.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 966a026d4..d2e817a80 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -98,7 +98,7 @@ class Delegations extends React.Component { this.getRewards(true); }, 15_000); const grantInterval = setInterval(() => { - this.getGrants(); + this.getGrants(true); }, 60_000); this.setState({ refreshInterval, grantInterval }); } @@ -142,7 +142,7 @@ class Delegations extends React.Component { }) } - async getGrants() { + async getGrants(hideError) { if(!this.authzSupport() || !this.props.operators.length) return const calls = this.orderedOperators().map((operator) => { @@ -174,7 +174,9 @@ class Delegations extends React.Component { })); }, (error) => { - this.setState({ error: "Failed to get grants. Please refresh" }); + if(!hideError){ + this.setState({ error: "Failed to get grants. Please refresh" }); + } } ); } From 53e5b3ec494a81a9d91a840144855e8c761365e2 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 12 Apr 2022 22:21:36 +0100 Subject: [PATCH 376/862] Show correct sommelier authz status --- src/networks.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 4cf129b9a..bdd1efcf6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -142,7 +142,8 @@ "authzSupport": false }, { - "name": "sommelier" + "name": "sommelier", + "authzSupport": true }, { "name": "konstellation", From c5c7eb80f785b2d296bd7a455760ebec20d2bab1 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 13 Apr 2022 15:58:16 +0100 Subject: [PATCH 377/862] Handle bignumber rewards --- scripts/base.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 0b34d4079..7583b7927 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -240,7 +240,7 @@ export class Autostake { const perValidatorReward = floor(divide(totalRewards, validators.length)) - if (smaller(perValidatorReward, bignumber(client.operator.minimumReward))) { + if (smaller(bignumber(perValidatorReward), bignumber(client.operator.minimumReward))) { timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') return } From 88850a51775754f8eb6dde33860fe6af247a9df5 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 16 Apr 2022 23:54:17 +0100 Subject: [PATCH 378/862] Improve grant status for ledger style grants --- src/components/Delegations.js | 52 ++++---- src/components/ValidatorGrants.js | 206 ++++++++++++++++-------------- src/components/ValidatorModal.js | 29 +++-- 3 files changed, 155 insertions(+), 132 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index d2e817a80..fef278954 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -42,7 +42,7 @@ class Delegations extends React.Component { this.getGrants() this.refresh(); - if(this.props.validator){ + if (this.props.validator) { this.showValidatorModal(this.props.validator.operator_address) } } @@ -68,14 +68,14 @@ class Delegations extends React.Component { return this.refresh(); } - if(!this.props.delegations) return + if (!this.props.delegations) return const delegationsChanged = _.difference(Object.keys(this.props.delegations), Object.keys(prevProps.delegations || {})).length > 0 if (delegationsChanged) { this.getGrants() } - if(prevProps.validator !== this.props.validator && this.props.validator && !this.state.validatorModal.show){ + if (prevProps.validator !== this.props.validator && this.props.validator && !this.state.validatorModal.show) { this.showValidatorModal(this.props.validator.operator_address) } } @@ -103,10 +103,10 @@ class Delegations extends React.Component { this.setState({ refreshInterval, grantInterval }); } - async getWithdrawAddress(){ + async getWithdrawAddress() { const withdraw = await this.props.queryClient.getWithdrawAddress(this.props.address) - if(withdraw !== this.props.address){ - this.setState({error: 'You have a different withdraw address set. REStake WILL NOT WORK!'}) + if (withdraw !== this.props.address) { + this.setState({ error: 'You have a different withdraw address set. REStake WILL NOT WORK!' }) } } @@ -129,7 +129,7 @@ class Delegations extends React.Component { } async calculateApy() { - if(this.props.network.apyEnabled === false || !this.props.network.getApy) return + if (this.props.network.apyEnabled === false || !this.props.network.getApy) return this.props.network.getApy( this.props.validators, @@ -143,12 +143,12 @@ class Delegations extends React.Component { } async getGrants(hideError) { - if(!this.authzSupport() || !this.props.operators.length) return + if (!this.authzSupport() || !this.props.operators.length) return const calls = this.orderedOperators().map((operator) => { return () => { const { botAddress, address } = operator; - if(!this.props.operators.includes(operator)) return; + if (!this.props.operators.includes(operator)) return; return this.props.queryClient.getGrants(botAddress, this.props.address).then( (result) => { @@ -162,8 +162,8 @@ class Delegations extends React.Component { const operatorGrant = { claimGrant: claimGrant, stakeGrant: stakeGrant, - validators: grantValidators || [], - maxTokens: maxTokens && bignumber(maxTokens.amount) + validators: grantValidators, + maxTokens: maxTokens ? bignumber(maxTokens.amount) : null }; this.setState((state, props) => ({ operatorGrants: _.set( @@ -174,7 +174,7 @@ class Delegations extends React.Component { })); }, (error) => { - if(!hideError){ + if (!hideError) { this.setState({ error: "Failed to get grants. Please refresh" }); } } @@ -255,7 +255,7 @@ class Delegations extends React.Component { } operatorGrants() { - if(!this.state.operatorGrants) return {} + if (!this.state.operatorGrants) return {} return this.props.operators.reduce((sum, operator) => { let grant = this.state.operatorGrants[operator.botAddress] if (!grant) grant = this.defaultGrant; @@ -291,7 +291,7 @@ class Delegations extends React.Component { orderedOperators() { return _.sortBy(this.props.operators, ({ address }) => { - if(!this.props.delegations) return 0 + if (!this.props.delegations) return 0 return this.props.delegations[address] ? 0 : 1 }); @@ -311,7 +311,7 @@ class Delegations extends React.Component { } isValidatorOperator(validator) { - if(!this.props.address || !validator || !window.atob) return false; + if (!this.props.address || !validator || !window.atob) return false; const prefix = this.props.network.prefix const validatorOperator = Bech32.encode(prefix, Bech32.decode(validator.operator_address).data) @@ -370,20 +370,20 @@ class Delegations extends React.Component { ); } - showValidatorModal(address, opts){ + showValidatorModal(address, opts) { opts = opts || {} const validator = address && this.props.validators[address] - this.setState({validatorModal: {show: true, validator: validator, ...opts}}) + this.setState({ validatorModal: { show: true, validator: validator, ...opts } }) } - hideValidatorModal(opts){ + hideValidatorModal(opts) { opts = opts || {} this.setState((state, props) => { - return {validatorModal: {...state.validatorModal, show: false}} + return { validatorModal: { ...state.validatorModal, show: false } } }) } - renderValidatorModal(){ + renderValidatorModal() { const validatorModal = this.state.validatorModal return ( @@ -402,6 +402,8 @@ class Delegations extends React.Component { rewards={this.state.rewards} delegations={this.props.delegations} grants={this.operatorGrants()} + authzSupport={this.authzSupport()} + restakePossible={this.restakePossible()} stargateClient={this.props.stargateClient} hideModal={this.hideValidatorModal} onDelegate={this.onClaimRewards} @@ -428,7 +430,7 @@ class Delegations extends React.Component { : grants.grantsExist ? "table-danger" : "table-warning" : undefined; - if(isValidatorOperator) rowVariant = 'table-info' + if (isValidatorOperator) rowVariant = 'table-info' const delegationBalance = (delegation && delegation.balance) || { amount: 0, @@ -451,7 +453,7 @@ class Delegations extends React.Component { />
    @@ -491,7 +493,7 @@ class Delegations extends React.Component { {this.props.network.apyEnabled !== false && ( + + + + +
    - this.showValidatorModal(validatorAddress, {activeTab: 'profile'})}> + this.showValidatorModal(validatorAddress, { activeTab: 'profile' })}> - {Object.keys(this.state.validatorApy).length > 0 + {Object.keys(this.state.validatorApy).length > 0 ? this.state.validatorApy[validatorAddress] ? {Math.round(this.state.validatorApy[validatorAddress] * 100) + "%"} : "-" @@ -681,8 +683,8 @@ class Delegations extends React.Component { >

    Ledger devices are not supported in the REStake UI currently. Support will be added as soon as it is possible.

    this.setState({ showAboutLedger: true })} role="button" className="text-dark text-decoration-underline">A manual workaround is possible using the CLI

    - - + + )} {this.props.network && ( diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 83b3102a5..8a5c8bae0 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -19,7 +19,7 @@ import AlertMessage from './AlertMessage'; function ValidatorGrants(props) { const { grants, operator, address, network } = props - const { claimGrant, stakeGrant, maxTokens, grantsValid, grantsExist } = grants + const { claimGrant, stakeGrant, maxTokens, validators, grantsValid, grantsExist } = grants || {} const defaultExpiry = moment().add(1, 'year') const [loading, setLoading] = useState(false); const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry.format('YYYY-MM-DD') }); @@ -27,6 +27,7 @@ function ValidatorGrants(props) { useEffect(() => { setState({ ...state, + validators: validators || (!grantsExist && [operator.address]), maxTokens, expiryDate: expiryDate(), }) @@ -44,7 +45,7 @@ function ValidatorGrants(props) { setState({ ...state, [e.target.name]: e.target.value }); } - function expiryDate(){ + function expiryDate() { const claimExpiry = claimGrant && claimGrant.expiration && moment(claimGrant.expiration) const stakeExpiry = stakeGrant && stakeGrant.expiration && moment(stakeGrant.expiration) const expiryDate = claimExpiry && stakeExpiry && claimExpiry > stakeExpiry ? stakeExpiry : claimExpiry || stakeExpiry @@ -125,6 +126,18 @@ function ValidatorGrants(props) { } } + function grantInformation() { + return ( + <> +

    {operator.moniker} will be able to carry out the following transactions on your behalf.

    +

    WidthdrawDelegatorRewards - allowed to withdraw/claim rewards for any validator, however REStake only withdraws from {operator.moniker}'s reward balance.

    +

    Delegate - allowed to delegate {maxTokensDenom() ? : 'any amount'} to {!state.validators ? 'any validator' : state.validators.length === 1 && state.validators.includes(operator.address) ? 'only their own validator' : 'validators ' + state.validators.join(', ')}.

    +

    REStake only re-delegates {operator.moniker}'s accrued rewards and tries not to touch your balance.

    +

    Both of these grants will expire automatically on {state.expiryDateValue}.

    + + ) + } + const minimumReward = () => { return { amount: operator.minimumReward, @@ -134,102 +147,109 @@ function ValidatorGrants(props) { return ( <> - {!props.delegation && ( - - You must delegate to {operator.moniker} before they can REStake for you. - + {!props.authzSupport && ( + + {props.network.prettyName} doesn't support Authz just yet. + )} - - - - - - - - - - - - - - - - - - - - {state.maxTokens && ( - - - - - )} + {props.restakePossible && !props.delegation && ( + + You must delegate to {operator.moniker} before they can REStake for you. + + )} +
    REStake Address{operator.botAddress}
    Frequency - {operator.runTimesString()} -
    Minimum Reward - -
    Current Rewards - -
    Grant Remaining - -
    + + + + + + + + + + + + + + + + + + {state.maxTokens && ( - - + - -
    REStake Address{operator.botAddress}
    Frequency + {operator.runTimesString()} +
    Minimum Reward + +
    Current Rewards + +
    Grant status - {grantsValid - ? Active - : grantsExist - ? state.maxTokens && smaller(state.maxTokens, props.rewards) - ? Not enough grant remaining - : Invalid : Inactive} + Grant Remaining +
    - - Total delegation -
    -
    - - {network.symbol.toUpperCase()} -
    -
    - Reduces with every delegation made by the validator
    Leave empty for unlimited + )} +
    Grant status + {grantsValid + ? Active + : grantsExist + ? state.maxTokens && smaller(state.maxTokens, props.rewards) + ? Not enough grant remaining + : Invalid : Inactive} +
    + {grantsExist && !props.restakePossible && ( + <>{grantInformation()} + )} + {props.restakePossible && ( + + + Total delegation +
    +
    + + {network.symbol.toUpperCase()} +
    +
    + Reduces with every delegation made by the validator
    Leave empty for unlimited +
    -
    - - - Expiry date - -
    Date the grant will expire. After this date you will need to re-grant
    -
    -

    The following Authz authorisations will be made, allowing {operator.moniker} to carry out certain transactions on your behalf.

    -

    WidthdrawDelegatorRewards - allowed to withdraw/claim rewards for any validator, however REStake only withdraws from {operator.moniker}'s reward balance.

    -

    Delegate - allowed to delegate {maxTokensDenom() ? : 'any amount'} to only their own validator. REStake only re-delegates {operator.moniker}'s accrued rewards and tries not to touch your balance.

    -

    Both of these grants will expire automatically on {state.expiryDateValue}.

    -

    - {!loading - ? ( - <> - {grants.grantsExist && ( - showLoading(loading)} - setError={props.setError} - /> - )} - - - ) - : - } -

    - + + + Expiry date + +
    Date the grant will expire. After this date you will need to re-grant
    +
    + {grantInformation()} +

    + {!loading + ? ( + <> + {grants.grantsExist && ( + showLoading(loading)} + setError={props.setError} + /> + )} + + + ) + : + } +

    + + )} ) } diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index 52f8128f8..02439acf2 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -8,7 +8,6 @@ import ValidatorLink from './ValidatorLink' import AboutLedger from './AboutLedger'; import { - Button, Modal, Tabs, Tab @@ -26,33 +25,33 @@ function ValidatorModal(props) { const params = useParams(); useEffect(() => { - if(props.show && selectedValidator && validator?.operator_address === selectedValidator.operator_address && params.validator !== selectedValidator.operator_address){ + if (props.show && selectedValidator && validator?.operator_address === selectedValidator.operator_address && params.validator !== selectedValidator.operator_address) { navigate(`/${network.name}/${selectedValidator.operator_address}`) - }else if(params.validator && !props.show){ + } else if (params.validator && !props.show) { navigate(`/${network.name}`) } }, [props.show, params.validator, selectedValidator]) useEffect(() => { - if(props.activeTab && props.activeTab != activeTab){ + if (props.activeTab && props.activeTab != activeTab) { setActiveTab(props.activeTab) - }else if(redelegate || undelegate){ + } else if (redelegate || undelegate) { setActiveTab('delegate') - }else if(!activeTab){ + } else if (!activeTab) { setActiveTab('profile') } }, [props.activeTab, redelegate, undelegate, selectedValidator]) useEffect(() => { - if(props.show){ + if (props.show) { setSelectedValidator(!redelegate && validator) } }, [validator, redelegate, props.show]) const handleClose = () => { - if(selectedValidator && (!validator || redelegate) && selectedValidator !== validator){ + if (selectedValidator && (!validator || redelegate) && selectedValidator !== validator) { setSelectedValidator(null) - }else{ + } else { props.hideModal() } } @@ -82,9 +81,9 @@ function ValidatorModal(props) { } const availableBalance = () => { - if(redelegate || undelegate){ + if (redelegate || undelegate) { return (props.delegations[validator.address] || {}).balance - }else{ + } else { return props.balance } } @@ -136,7 +135,7 @@ function ValidatorModal(props) { {selectedValidator && ( setActiveTab(k)} id="validator-tabs" className="mb-3"> - - {network.authzSupport && operator() && ( + {operator() && ( Date: Mon, 18 Apr 2022 00:01:34 +0100 Subject: [PATCH 379/862] Improve network init and skip health check for CD --- scripts/base.mjs | 5 +- scripts/checkAuthz.mjs | 4 +- src/components/App.js | 5 +- src/components/Delegations.js | 1 - src/components/NetworkFinder.js | 20 +++-- src/components/NetworkSelect.js | 83 ++++++++++-------- src/components/Wallet.js | 10 ++- src/utils/Network.mjs | 150 +++++++++++++++----------------- src/utils/QueryClient.mjs | 17 ++-- 9 files changed, 153 insertions(+), 142 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 7583b7927..3183da090 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -89,8 +89,9 @@ export class Autostake { } async getClient(data) { - let network = await Network(data, true) + let network = new Network(data) let slip44 + await network.load() timeStamp('⚛') timeStamp('Starting', network.prettyName) @@ -129,7 +130,7 @@ export class Autostake { if (!network.authzSupport) return timeStamp('No Authz support') - network = await Network(data) + await network.connect() if (!network.rpcUrl) return timeStamp('Could not connect to RPC API') if (!network.restUrl) return timeStamp('Could not connect to REST API') diff --git a/scripts/checkAuthz.mjs b/scripts/checkAuthz.mjs index 98c748370..c917209ed 100644 --- a/scripts/checkAuthz.mjs +++ b/scripts/checkAuthz.mjs @@ -10,7 +10,9 @@ async function checkAuthz(networkName) { return async () => { if(networkName && data.name !== networkName) return try { - const network = await Network(data) + const network = new Network(data) + await network.load() + await network.connect() const support = await testAuthz(network) if (data.authzSupport !== support){ console.log(network.name, 'support is different', support ? 'ENABLED' : 'DISABLED') diff --git a/src/components/App.js b/src/components/App.js index aeeb0fef2..99676a6bf 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -42,7 +42,7 @@ class App extends React.Component { if(this.state.keplr != prevState.keplr){ this.connect() }else if(this.props.network && this.props.network !== prevProps.network){ - this.setState({ balance: undefined }) + this.setState({ balance: undefined, address: undefined }) this.connect() } } @@ -103,7 +103,8 @@ class App extends React.Component { } catch (e) { console.log(e) return this.setState({ - error: 'Failed to connect to signing client. API may be down' + error: 'Failed to connect to signing client. API may be down.', + loading: false }) } } diff --git a/src/components/Delegations.js b/src/components/Delegations.js index fef278954..3358f3dcd 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -678,7 +678,6 @@ class Delegations extends React.Component { <>

    Ledger devices are not supported in the REStake UI currently. Support will be added as soon as it is possible.

    diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 6b8335ca9..272d31447 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -74,16 +74,18 @@ function NetworkFinder() { if(params.network != networkName){ navigate("/" + networkName); } - Network(data).then(network => { - if(network.connected){ - setState({ network: network }) - }else{ - throw false - } - }).catch(error => { - Network(data, true).then(network => { - setState({ network: network, loading: false }) + const network = new Network(data) + network.load().then(() => { + return network.connect().then(() => { + if (network.connected) { + setState({ network: network }) + } else { + throw false + } }) + }).catch(error => { + console.log(error) + setState({ network: network, loading: false }) }) } }, [state.networks, state.network, params.network, navigate]) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index 408c050d8..f3675d21c 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -24,25 +24,57 @@ function NetworkSelect(props) { {networks: [], operators: [], network: {value: ''}} ) - const handleOpen = () => { - setSelectedNetwork(props.network) - setValidators(props.validators) + function handleOpen() { + setSelectedNetwork(props.network); + setValidators(props.validators); CosmosDirectory().getOperatorCounts().then(counts => { - setOperatorCounts(counts) - }) - setShow(true) + setOperatorCounts(counts); + }); + setShow(true); } - const handleClose = () => { - setShow(false) - props.onHide() + function handleClose() { + setShow(false); + props.onHide(); } - const handleSubmit = (event) => { + function handleSubmit(event) { event.preventDefault(); - props.changeNetwork(selectedNetwork, validators) - handleClose() + props.changeNetwork(selectedNetwork, validators); + handleClose(); + } + + function selectNetwork(newValue) { + const data = props.networks[newValue.value]; + if (data) { + setLoading(true); + setError(false); + const network = new Network(data); + network.load().then(() => { + setSelectedNetwork(network); + if (network.usingDirectory && !directoryConnected(data)) { + throw false; + } + network.connect().then(() => { + if (network.connected) { + setValidators(network.getValidators()); + setLoading(false); + } else { + throw false; + } + }); + }).catch(error => { + console.log(error); + setError('Unable to connect to this network currently. Try again later.'); + setLoading(false); + }); + } + } + + function directoryConnected(data) { + // replace with available status when added to directory + return ['rpc', 'rest'].every(type => data['best_apis'][type].length > 0) } useEffect(() => { @@ -69,35 +101,12 @@ function NetworkSelect(props) { value: selectedNetwork.name, label: selectedNetwork.prettyName, image: selectedNetwork.image, - operatorCount: selectedNetwork.operators.length, + operatorCount: selectedNetwork.operators?.length, authz: selectedNetwork.authzSupport } }) }, [props.networks, selectedNetwork, operatorCounts]) - const selectNetwork = (newValue) => { - const data = props.networks[newValue.value] - if(data){ - setLoading(true) - setError(false) - Network(data, true).then(network => { - setSelectedNetwork(network) - Network(data).then(network => { - if(network.connected){ - setSelectedNetwork(network) - setValidators(network.getValidators()) - setLoading(false) - }else{ - throw false - } - }).catch(error => { - setError('Unable to connect to this network currently. Try again later.') - setLoading(false) - }) - }) - } - } - return ( <>
    diff --git a/src/components/ValidatorDelegate.js b/src/components/ValidatorDelegate.js index c2ba0f30f..f4cb5ce65 100644 --- a/src/components/ValidatorDelegate.js +++ b/src/components/ValidatorDelegate.js @@ -51,12 +51,12 @@ function ValidatorDelegate(props) { )} Current Delegation - + Current Rewards - + diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js index 044b681af..23aa10537 100644 --- a/src/components/ValidatorProfile.js +++ b/src/components/ValidatorProfile.js @@ -57,7 +57,7 @@ function ValidatorProfile(props) { if (!validator) return const amount = validator.tokens - return + return } const minimumReward = () => { @@ -136,7 +136,7 @@ function ValidatorProfile(props) { REStake {!!operator ? ( - {operator.runTimesString()} ( min) + {operator.runTimesString()} ( min) ) : } identifier={validator.operator_address} tooltip="This validator is not a REStake operator" /> } From 1721d7e6ef144e483335f24014216aac13599f29 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 18 Apr 2022 01:07:40 +0100 Subject: [PATCH 382/862] Show network status in network select --- src/components/NetworkSelect.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index f3675d21c..37c3f9498 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -89,12 +89,14 @@ function NetworkSelect(props) { const networks = Object.values(props.networks).sort((a, b) => a.name > b.name ? 1 : -1) setOptions({ networks: networks.map(el => { + const network = new Network(el) return { value: el.name, label: el.pretty_name, image: el.image, operatorCount: el.operators?.length || operatorCounts[el.name], - authz: el.authzSupport + authz: el.authzSupport, + online: !network.usingDirectory || directoryConnected(el) } }), network: selectedNetwork && { @@ -102,7 +104,8 @@ function NetworkSelect(props) { label: selectedNetwork.prettyName, image: selectedNetwork.image, operatorCount: selectedNetwork.operators?.length, - authz: selectedNetwork.authzSupport + authz: selectedNetwork.authzSupport, + online: true // modal will show status } }) }, [props.networks, selectedNetwork, operatorCounts]) @@ -143,12 +146,12 @@ function NetworkSelect(props) { options={options.networks} onChange={selectNetwork} formatOptionLabel={network => ( -
    +
    {network.label}
    - {network.label} + {network.label} {!network.online && (Offline)}
    {network.operatorCount > 0 && From 1a0fff9627d46da853d2f5ae6333b40fcf8bcf43 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 18 Apr 2022 02:21:36 +0100 Subject: [PATCH 383/862] Use remaining grant balance if set --- scripts/base.mjs | 42 +++++++++++++++++-------------- src/components/ValidatorGrants.js | 3 ++- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 3183da090..b8e63ee5d 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -3,7 +3,7 @@ import { Slip10RawIndex, pathToString } from "@cosmjs/crypto"; import Network from '../src/utils/Network.mjs' import {coin, timeStamp, mapSync, executeSync, overrideNetworks} from '../src/utils/Helpers.mjs' -import { divide, bignumber, floor, smaller } from 'mathjs' +import { add, bignumber, floor, smaller, smallerEq } from 'mathjs' import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx.js"; import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx.js"; @@ -83,7 +83,7 @@ export class Autostake { timeStamp("Found", grantedAddresses.length, "delegators with valid grants...") - let grantMessages = await this.getAutostakeMessages(client, grantedAddresses, [client.operator.address]) + let grantMessages = await this.getAutostakeMessages(client, grantedAddresses) await this.autostake(client, grantMessages) timeStamp(client.network.prettyName, "finished") } @@ -218,12 +218,12 @@ export class Autostake { ) } - async getAutostakeMessages(client, grantAddresses, validators) { + async getAutostakeMessages(client, grantAddresses) { let batchSize = client.network.data.autostake?.batchQueries || 50 let calls = grantAddresses.map(item => { return async () => { try { - return await this.getAutostakeMessage(client, item, validators) + return await this.getAutostakeMessage(client, item) } catch (error) { timeStamp(item.address, 'Failed to get address', error.message) } @@ -235,20 +235,26 @@ export class Autostake { return _.compact(messages.flat()) } - async getAutostakeMessage(client, grantAddress, validators) { + async getAutostakeMessage(client, grantAddress) { const { address, grant } = grantAddress - const totalRewards = await this.totalRewards(client, address, validators) + const totalRewards = await this.totalRewards(client, address) - const perValidatorReward = floor(divide(totalRewards, validators.length)) + let autostakeAmount = floor(totalRewards) - if (smaller(bignumber(perValidatorReward), bignumber(client.operator.minimumReward))) { - timeStamp(address, perValidatorReward, client.network.denom, 'reward is too low, skipping') + if (smaller(bignumber(autostakeAmount), bignumber(client.operator.minimumReward))) { + timeStamp(address, autostakeAmount, client.network.denom, 'reward is too low, skipping') return } - if (grant.maxTokens && smaller(grant.maxTokens, perValidatorReward)) { - timeStamp(address, grant.maxTokens, client.network.denom, 'grant balance is too low, skipping') - return + if (grant.maxTokens){ + if(smallerEq(grant.maxTokens, 0)) { + timeStamp(address, grant.maxTokens, client.network.denom, 'grant balance is empty, skipping') + return + } + if(smaller(grant.maxTokens, autostakeAmount)) { + autostakeAmount = grant.maxTokens + timeStamp(address, grant.maxTokens, client.network.denom, 'grant balance is too low, using remaining') + } } let timeout = client.network.data.autostake?.delegatorTimeout || 5000 @@ -258,11 +264,9 @@ export class Autostake { return } - timeStamp(address, "Can autostake", perValidatorReward, client.network.denom, validators.length > 1 ? "per validator" : '') + timeStamp(address, "Can autostake", autostakeAmount, client.network.denom) - let messages = validators.map(el => { - return this.buildRestakeMessage(address, el, perValidatorReward, client.network.denom) - }).flat() + let messages = this.buildRestakeMessage(address, client.operator.address, autostakeAmount, client.network.denom) return this.buildExecMessage(client.operator.botAddress, messages) } @@ -318,15 +322,15 @@ export class Autostake { }] } - totalRewards(client, address, validators) { + totalRewards(client, address) { let timeout = client.network.data.autostake?.delegatorTimeout || 5000 return client.queryClient.getRewards(address, { timeout }) .then( (rewards) => { const total = Object.values(rewards).reduce((sum, item) => { const reward = item.reward.find(el => el.denom === client.network.denom) - if (reward && validators.includes(item.validator_address)) { - return sum + bignumber(reward.amount) + if (reward && item.validator_address === client.operator.address) { + return add(sum, bignumber(reward.amount)) } return sum }, 0) diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 8a5c8bae0..28f2351e0 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -197,7 +197,8 @@ function ValidatorGrants(props) { : grantsExist ? state.maxTokens && smaller(state.maxTokens, props.rewards) ? Not enough grant remaining - : Invalid : Inactive} + : Invalid / total delegation reached + : Inactive} From 803fccaaec17fec54cc73966b85cf64f4529bb76 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 18 Apr 2022 13:55:02 +0100 Subject: [PATCH 384/862] Handle unbonding status and hide warning properly --- src/components/ValidatorName.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/ValidatorName.js b/src/components/ValidatorName.js index 8abd51eb8..ca5210f59 100644 --- a/src/components/ValidatorName.js +++ b/src/components/ValidatorName.js @@ -11,11 +11,13 @@ function ValidatorName(props) { let warning = false let warningClass - if(validator.status === 'BOND_STATUS_UNBONDED') warning = 'Validator is not in the active set' + if(validator.status !== 'BOND_STATUS_BONDED') warning = 'Validator is not in the active set' if(validator.jailed) warning = 'Validator is jailed' - if(warning && !hideWarning) warningClass = 'text-danger' - if(validator.jailed) warningClass = warningClass + ' text-decoration-line-through' + if(!hideWarning){ + if (warning) warningClass = 'text-danger' + if (validator.jailed) warningClass = warningClass + ' text-decoration-line-through' + } return ( <> From 4efa8e83063fc276271d80403688dca05a538a86 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 18 Apr 2022 15:40:33 +0100 Subject: [PATCH 385/862] Handle missing stakeGrant correctly --- src/components/ValidatorGrants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 28f2351e0..48264643b 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -27,7 +27,7 @@ function ValidatorGrants(props) { useEffect(() => { setState({ ...state, - validators: validators || (!grantsExist && [operator.address]), + validators: validators || (!stakeGrant && [operator.address]), maxTokens, expiryDate: expiryDate(), }) From 0a65bebdf145a8b130d11514d015080beeaf7d83 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 18 Apr 2022 19:11:17 +0100 Subject: [PATCH 386/862] Add AssetMantle (APY disabled for now) --- src/networks.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/networks.json b/src/networks.json index b6fc48962..2ab82d123 100644 --- a/src/networks.json +++ b/src/networks.json @@ -188,5 +188,11 @@ "name": "decentr", "gasPrice": "0.025udec", "authzSupport": false + }, + { + "name": "assetmantle", + "gasPrice": "0.025umntl", + "authzSupport": true, + "apyEnabled": false } ] From 77275dd3fcf1aca393b9985762e730eefef3b361 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 18 Apr 2022 19:11:44 +0100 Subject: [PATCH 387/862] Adjust network change callbacks --- src/components/Delegations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index ac6e07942..01b3356aa 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -65,7 +65,7 @@ class Delegations extends React.Component { validatorApy: {}, operatorGrants: {} }); - return this.refresh(); + this.refresh(); } if (!this.props.delegations) return @@ -231,7 +231,7 @@ class Delegations extends React.Component { this.props.getBalance(); this.props.getDelegations(); this.getRewards(); - }, 6_000); + }, 3_000); } setClaimLoading(value) { From 682c68a99931c50379dbb13f6aeb4dadc839a22c Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 18 Apr 2022 19:55:33 +0100 Subject: [PATCH 388/862] Set AssetMantle owner address --- src/networks.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/networks.json b/src/networks.json index 2ab82d123..3075a0cf6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -192,6 +192,7 @@ { "name": "assetmantle", "gasPrice": "0.025umntl", + "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd", "authzSupport": true, "apyEnabled": false } From 0207e50693788f0bb5c18cc2a11be842a3eec744 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 18 Apr 2022 20:12:46 +0100 Subject: [PATCH 389/862] Enable AssetMantle APY --- src/components/Delegations.js | 3 +++ src/networks.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 01b3356aa..2604e3f8c 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -66,6 +66,9 @@ class Delegations extends React.Component { operatorGrants: {} }); this.refresh(); + if(this.props.delegations){ + return this.getGrants() + } } if (!this.props.delegations) return diff --git a/src/networks.json b/src/networks.json index 3075a0cf6..88ca09966 100644 --- a/src/networks.json +++ b/src/networks.json @@ -194,6 +194,6 @@ "gasPrice": "0.025umntl", "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd", "authzSupport": true, - "apyEnabled": false + "apyEnabled": true } ] From 6d935739a933e68b4709aae2080e6ab4a35700d8 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 19 Apr 2022 17:58:24 +0100 Subject: [PATCH 390/862] Allow custom gasPriceStep configuration --- src/components/App.js | 3 ++- src/utils/Network.mjs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/App.js b/src/components/App.js index 99676a6bf..6b65e1ea7 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -142,7 +142,8 @@ class App extends React.Component { bech32PrefixConsPub: network.prefix + "valconspub" }, currencies: [currency], - feeCurrencies: [currency] + feeCurrencies: [currency], + gasPriceStep: network.gasPriceStep }) } diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 43b64b89e..5698d90ae 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -51,6 +51,7 @@ class Network { this.authzSupport = this.chain.authzSupport const defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, this.chain.decimals))), { notation: 'fixed' }) + this.chain.denom this.gasPrice = this.data.gasPrice || defaultGasPrice + this.gasPriceStep = this.data.gasPriceStep } async connect() { From aa2ac9e7a8a0a91b6e8111b307d0bf55a4d558db Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 19 Apr 2022 17:58:32 +0100 Subject: [PATCH 391/862] Set AssetMantle free low gas fee --- src/networks.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/networks.json b/src/networks.json index 88ca09966..4becaba80 100644 --- a/src/networks.json +++ b/src/networks.json @@ -192,6 +192,11 @@ { "name": "assetmantle", "gasPrice": "0.025umntl", + "gasPriceStep": { + "low": 0, + "average": 0.025, + "high": 0.04 + }, "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd", "authzSupport": true, "apyEnabled": true From 042a0a4d2934c13905631fa33f03c6e16b70a3c6 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 20 Apr 2022 13:25:23 +0100 Subject: [PATCH 392/862] Add Crescent --- src/networks.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 4becaba80..dc0e17e65 100644 --- a/src/networks.json +++ b/src/networks.json @@ -198,7 +198,17 @@ "high": 0.04 }, "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd", + "authzSupport": true + }, + { + "name": "crescent", + "gasPrice": "0.025ucre", + "gasPriceStep": { + "low": 0, + "average": 0.025, + "high": 0.04 + }, "authzSupport": true, - "apyEnabled": true + "apyEnabled": false } ] From d8ac69f9e9736fd9b208a8c3947db3b040ca1c09 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 21 Apr 2022 17:42:57 +0100 Subject: [PATCH 393/862] Pin major node version --- .nvmrc | 1 + Dockerfile | 2 +- Dockerfile.production | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..98d9bcb75 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +17 diff --git a/Dockerfile b/Dockerfile index b84c0c1f6..28897e7cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # dev env -FROM node:alpine +FROM node:17-alpine RUN apk add --update python3 make g++ && rm -rf /var/cache/apk/* diff --git a/Dockerfile.production b/Dockerfile.production index f9412c904..0330c99fd 100644 --- a/Dockerfile.production +++ b/Dockerfile.production @@ -1,5 +1,5 @@ # build env -FROM node:alpine as build +FROM node:17-alpine as build WORKDIR /app COPY package*.json ./ From 108e30da90f632df6002d21aaca2668a9075d75d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 23 Apr 2022 16:28:12 +0100 Subject: [PATCH 394/862] Update Sifchain owner address --- src/networks.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index dc0e17e65..a9bff94e6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -92,7 +92,8 @@ { "name": "sifchain", "authzSupport": false, - "gasPrice": "1500000000000rowan" + "gasPrice": "1500000000000rowan", + "ownerAddress": "sifvaloper19t5nk5ceq5ga75epwdqhnupwg0v9339p096ydz" }, { "name": "lumnetwork", From 56b7e43b5b4334c0c65b31035e08cd2e55820b0f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 23 Apr 2022 23:27:38 +0100 Subject: [PATCH 395/862] Allow UI gas price override --- src/components/App.js | 14 ++++++++++---- src/components/DelegateForm.js | 2 +- src/networks.json | 2 ++ src/utils/Network.mjs | 7 ++++--- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 6b65e1ea7..a5e39afbb 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -74,20 +74,26 @@ class App extends React.Component { error: 'Could not connect to any available API servers' }) } - const chainId = this.props.network.chainId + const { network } = this.props + const chainId = network.chainId try { if (window.keplr) { + if(network.gasPricePrefer){ + window.keplr.defaultOptions = { + sign: { preferNoSetFee: true } + } + } await window.keplr.enable(chainId); } } catch (e) { console.log(e.message, e) - await this.suggestChain(this.props.network) + await this.suggestChain(network) } if (window.getOfflineSigner) { try { const offlineSigner = await window.getOfflineSignerAuto(chainId) const key = await window.keplr.getKey(chainId); - const stargateClient = await this.props.network.signingClient(offlineSigner, key) + const stargateClient = await network.signingClient(offlineSigner, key, network.gasPricePrefer) const address = await stargateClient.getAddress() @@ -96,7 +102,7 @@ class App extends React.Component { this.setState({ address: address, stargateClient: stargateClient, - queryClient: this.props.network.queryClient, + queryClient: network.queryClient, error: false }) this.getBalance() diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index cbd556bf0..83f5d435b 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -127,7 +127,7 @@ class DelegateForm extends React.Component { Amount
    - + {this.denom()}
    {this.props.availableBalance && diff --git a/src/networks.json b/src/networks.json index dc0e17e65..42e606048 100644 --- a/src/networks.json +++ b/src/networks.json @@ -134,6 +134,7 @@ { "name": "impacthub", "gasPrice": "0.025uixo", + "gasPricePrefer": "0.1uixo", "authzSupport": false }, { @@ -167,6 +168,7 @@ { "name": "secretnetwork", "gasPrice": "0.025uscrt", + "gasPricePrefer": "0.05uscrt", "authzSupport": true }, { diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 5698d90ae..20f8d6046 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -49,9 +49,10 @@ class Network { this.image = this.chain.image this.coinGeckoId = this.chain.coinGeckoId this.authzSupport = this.chain.authzSupport - const defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, this.chain.decimals))), { notation: 'fixed' }) + this.chain.denom + const defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed' }) + this.denom this.gasPrice = this.data.gasPrice || defaultGasPrice this.gasPriceStep = this.data.gasPriceStep + this.gasPricePrefer = this.data.gasPricePrefer } async connect() { @@ -68,12 +69,12 @@ class Network { } } - signingClient(wallet, key) { + signingClient(wallet, key, gasPrice) { if (!this.queryClient) return const client = this.SIGNERS[this.data.name] || SigningClient - return client(this.queryClient.rpcUrl, this.gasPrice, wallet, key) + return client(this.queryClient.rpcUrl, gasPrice || this.gasPrice, wallet, key) } getOperator(operatorAddress) { From b3c63cd049ea9e421f140c6e851ad29b34c3e3eb Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 24 Apr 2022 01:22:37 +0100 Subject: [PATCH 396/862] Add healthchecks.io integration --- README.md | 22 +++++++++++++- scripts/base.mjs | 36 ++++++++++++----------- src/utils/AutostakeHealth.mjs | 54 +++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 18 deletions(-) create mode 100644 src/utils/AutostakeHealth.mjs diff --git a/README.md b/README.md index 887caab27..34e2bd272 100644 --- a/README.md +++ b/README.md @@ -240,7 +240,27 @@ Arrays will be replaced and not merged. The file is `.gitignore`'d so it won't a Note that REStake requires a node with indexing enabled and minimum gas prices matching the `networks.json` gas price (or your local override). -## Submiting your operator +### Monitoring + +The REStake autostaking script can integrate with [healthchecks.io](https://healthchecks.io/) to report the script status for each network. healthchecks.io can then integrate with many notification platforms to make sure you know about any failures. + +REStake will ping [healthchecks.io](https://healthchecks.io/) when the script starts, succeeds, or fails. It will include relevant error information in the check log and is simple to configure. + +Setup a Check for each network you run the script for, and configure the expected schedule. E.g. add a check for Osmosis every 12 hours, Cerberus every 1 hour etc. + +Add your Check UUID to the relevant network in your `networks.local.json` config as below. You can also optionally set the `address` attribute if you want to [self-host the healthchecks.io platform](https://healthchecks.io/docs/self_hosted/). + +```JSON +{ + "osmosis": { + "healthCheck": { + "uuid": "77f02efd-c521-46cb-70g8-fa5v275au873" + } + } +} +``` + +## Submitting your operator ### Setup your REStake operator diff --git a/scripts/base.mjs b/scripts/base.mjs index b8e63ee5d..733283eae 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -13,6 +13,7 @@ import fs from 'fs' import _ from 'lodash' import 'dotenv/config' +import AutostakeHealth from "../src/utils/AutostakeHealth.mjs"; export class Autostake { constructor(){ @@ -32,13 +33,15 @@ export class Autostake { if(data.enabled === false) return let client + let health = new AutostakeHealth(data.healthCheck) + health.started('⚛') try { - client = await this.getClient(data) + client = await this.getClient(data, health) } catch (error) { - return timeStamp('Failed to connect', error.message) + return health.failed('Failed to connect', error.message) } - if(!client) return timeStamp('Skipping') + if(!client) return health.failed('Skipping') const { restUrl, rpcUrl, usingDirectory } = client.network @@ -54,7 +57,7 @@ export class Autostake { try { await this.runNetwork(client) } catch (error) { - return timeStamp('Autostake failed, skipping network', error.message) + return health.failed('Autostake failed, skipping network', error.message) } } }) @@ -63,10 +66,10 @@ export class Autostake { async runNetwork(client){ timeStamp('Running autostake') + const { network, health } = client const balance = await this.checkBalance(client) if (!balance || smaller(balance, 1_000)) { - timeStamp('Bot balance is too low') - return + return health.failed('Bot balance is too low') } timeStamp('Finding delegators...') @@ -85,15 +88,14 @@ export class Autostake { let grantMessages = await this.getAutostakeMessages(client, grantedAddresses) await this.autostake(client, grantMessages) - timeStamp(client.network.prettyName, "finished") + health.complete(network.prettyName, "finished") } - async getClient(data) { + async getClient(data, health) { let network = new Network(data) let slip44 await network.load() - timeStamp('⚛') timeStamp('Starting', network.prettyName) if(network.data.autostake?.correctSlip44){ @@ -137,10 +139,10 @@ export class Autostake { const client = await network.signingClient(wallet) client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) - return { network, operator, + health, signingClient: client, queryClient: network.queryClient } @@ -154,7 +156,7 @@ export class Autostake { return balance.amount }, (error) => { - timeStamp("ERROR:", error.message || error) + client.health.error("Failed to get balance:", error.message || error) } ) } @@ -164,7 +166,7 @@ export class Autostake { return client.queryClient.getAllValidatorDelegations(client.operator.address, batchSize, (pages) => { timeStamp("...batch", pages.length) }).catch(error => { - timeStamp("ERROR:", error.message || error) + client.health.error("Failed to get delegations:", error.message || error) return [] }) } @@ -177,7 +179,7 @@ export class Autostake { const grant = await this.getGrants(client, item) return grant ? { address: item, grant: grant } : undefined } catch (error) { - timeStamp(item, 'Failed to get address', error.message) + client.health.error(item, 'Failed to get address', error.message) } } }) @@ -213,7 +215,7 @@ export class Autostake { } }, (error) => { - timeStamp(delegatorAddress, "ERROR skipping this run:", error.message || error) + client.health.error(delegatorAddress, "ERROR skipping this run:", error.message || error) } ) } @@ -225,7 +227,7 @@ export class Autostake { try { return await this.getAutostakeMessage(client, item) } catch (error) { - timeStamp(item.address, 'Failed to get address', error.message) + client.health.error(item.address, 'Failed to get address', error.message) } } }) @@ -285,10 +287,10 @@ export class Autostake { await client.signingClient.signAndBroadcast(client.operator.botAddress, batch, undefined, memo).then((result) => { timeStamp("Successfully broadcasted"); }, (error) => { - timeStamp('Failed to broadcast:', error.message) + client.health.error('Failed to broadcast:', error.message) }) } catch (error) { - timeStamp('ERROR: Skipping batch:', error.message) + client.health.error('ERROR: Skipping batch:', error.message) } } }) diff --git a/src/utils/AutostakeHealth.mjs b/src/utils/AutostakeHealth.mjs new file mode 100644 index 000000000..5b4fd1f6a --- /dev/null +++ b/src/utils/AutostakeHealth.mjs @@ -0,0 +1,54 @@ +import _ from 'lodash' +import axios from 'axios' +import { timeStamp } from './Helpers.mjs' + +class AutostakeHealth { + constructor(config) { + const { address, uuid } = config || {} + this.address = address || 'https://hc-ping.com' + this.uuid = uuid + this.config = config + } + + started(...args){ + timeStamp(...args) + if(this.uuid) timeStamp('Starting health', this.uuid, this.address) + this.ping('start', ...args) + } + + error(...args){ + this.errors = [...this.errors, "\n", ...args] + } + + complete(...args){ + if(this.errors){ + this.failed(...[...this.errors, "\n", ...args]) + }else{ + this.success(...args) + } + } + + success(...args){ + timeStamp(...args) + this.ping(undefined, ...args) + } + + failed(...args){ + timeStamp(...args) + this.ping('fail', ...args) + } + + ping(action, ...args){ + if(!this.uuid) return + + axios.request({ + method: 'POST', + url: _.compact([this.address, this.uuid, action]).join('/'), + data: args.join(' ') + }).catch(error => { + timeStamp('Health ping failed', error.message) + }) + } +} + +export default AutostakeHealth \ No newline at end of file From dd8d017242f647a398e99102cef72926fddceddf Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 24 Apr 2022 01:38:09 +0100 Subject: [PATCH 397/862] README updates --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 34e2bd272..a0c889d11 100644 --- a/README.md +++ b/README.md @@ -38,15 +38,14 @@ Right now, the REStake autostaking script uses the standard 118 derivation path As there are existing operators using the 118 path, operators will need to opt in to the correct path when they want to upgrade. **New operators should use the correct path before they get grants**. -The correct path can be set in one of two ways using a [config override](#overriding-networks-config-locallyuse-your-own-node) file. You should use `"correctSlip44": true` if possible. +The correct path can be set in one of two ways using a [config override](#overriding-networks-config-locallyuse-your-own-node) file. `"correctSlip44": true` will use the slip44 defined in the Chain Registry. Alternatively set a specific path using `"slip44": 69`. You should use `"correctSlip44": true` if possible. ```jsonc { "desmos": { "prettyName": "Desmos 852", "autostake": { - "correctSlip44": true, // Use the correct slip44 path from chain-registry - "slip44": 852 // Alternatively set a specific slip44 path + "correctSlip44": true } } } @@ -242,7 +241,7 @@ Note that REStake requires a node with indexing enabled and minimum gas prices m ### Monitoring -The REStake autostaking script can integrate with [healthchecks.io](https://healthchecks.io/) to report the script status for each network. healthchecks.io can then integrate with many notification platforms to make sure you know about any failures. +The REStake autostaking script can integrate with [healthchecks.io](https://healthchecks.io/) to report the script status for each network. [healthchecks.io](https://healthchecks.io/) can then integrate with many notification platforms like email, Discord and Slack to make sure you know about any failures. REStake will ping [healthchecks.io](https://healthchecks.io/) when the script starts, succeeds, or fails. It will include relevant error information in the check log and is simple to configure. From 74b56ecdcfdea361014fb3871a0777433db0e9d9 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 24 Apr 2022 01:55:45 +0100 Subject: [PATCH 398/862] Fix error handling --- src/utils/AutostakeHealth.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/AutostakeHealth.mjs b/src/utils/AutostakeHealth.mjs index 5b4fd1f6a..42ec693e6 100644 --- a/src/utils/AutostakeHealth.mjs +++ b/src/utils/AutostakeHealth.mjs @@ -17,7 +17,7 @@ class AutostakeHealth { } error(...args){ - this.errors = [...this.errors, "\n", ...args] + this.errors = [...(this.errors || []), "\n", ...args] } complete(...args){ From da9ddc090dfdfc2fbf695dd47ab93fdde06d3505 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 24 Apr 2022 02:00:42 +0100 Subject: [PATCH 399/862] Readme tweaks --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a0c889d11..64cb53162 100644 --- a/README.md +++ b/README.md @@ -243,9 +243,9 @@ Note that REStake requires a node with indexing enabled and minimum gas prices m The REStake autostaking script can integrate with [healthchecks.io](https://healthchecks.io/) to report the script status for each network. [healthchecks.io](https://healthchecks.io/) can then integrate with many notification platforms like email, Discord and Slack to make sure you know about any failures. -REStake will ping [healthchecks.io](https://healthchecks.io/) when the script starts, succeeds, or fails. It will include relevant error information in the check log and is simple to configure. +Once configured, REStake will ping [healthchecks.io](https://healthchecks.io/) when the script starts, succeeds, or fails. It will include relevant error information in the check log and is simple to configure. -Setup a Check for each network you run the script for, and configure the expected schedule. E.g. add a check for Osmosis every 12 hours, Cerberus every 1 hour etc. +Setup a Check for each network you run the script for, and configure the expected schedule. E.g. add a check for Osmosis every 12 hours, Akash every 1 hour etc. Set a timeout in the region of 5 minutes, or slightly longer than you expect the script to run. Add your Check UUID to the relevant network in your `networks.local.json` config as below. You can also optionally set the `address` attribute if you want to [self-host the healthchecks.io platform](https://healthchecks.io/docs/self_hosted/). From e491d9d885e7d8cdacc95d7991b8b3acacda4966 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 24 Apr 2022 02:12:48 +0100 Subject: [PATCH 400/862] Report success when skipping without error --- scripts/base.mjs | 6 +++--- src/utils/AutostakeHealth.mjs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/base.mjs b/scripts/base.mjs index 733283eae..8680678b0 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -41,7 +41,7 @@ export class Autostake { return health.failed('Failed to connect', error.message) } - if(!client) return health.failed('Skipping') + if(!client) return health.success('Skipping') const { restUrl, rpcUrl, usingDirectory } = client.network @@ -133,8 +133,8 @@ export class Autostake { if (!network.authzSupport) return timeStamp('No Authz support') await network.connect() - if (!network.rpcUrl) return timeStamp('Could not connect to RPC API') - if (!network.restUrl) return timeStamp('Could not connect to REST API') + if (!network.rpcUrl) throw new Error('Could not connect to RPC API') + if (!network.restUrl) throw new Error('Could not connect to REST API') const client = await network.signingClient(wallet) client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) diff --git a/src/utils/AutostakeHealth.mjs b/src/utils/AutostakeHealth.mjs index 42ec693e6..f4cf8b093 100644 --- a/src/utils/AutostakeHealth.mjs +++ b/src/utils/AutostakeHealth.mjs @@ -12,7 +12,7 @@ class AutostakeHealth { started(...args){ timeStamp(...args) - if(this.uuid) timeStamp('Starting health', this.uuid, this.address) + if(this.uuid) timeStamp('Starting health', [this.address, this.uuid].join('/')) this.ping('start', ...args) } From a4474bc2cc0edaa6f124258a1fc34c3b0e9b2f15 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 24 Apr 2022 13:34:12 +0100 Subject: [PATCH 401/862] Ensure autostake errors are always output --- src/utils/AutostakeHealth.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/AutostakeHealth.mjs b/src/utils/AutostakeHealth.mjs index f4cf8b093..20f9623a6 100644 --- a/src/utils/AutostakeHealth.mjs +++ b/src/utils/AutostakeHealth.mjs @@ -17,14 +17,16 @@ class AutostakeHealth { } error(...args){ + timeStamp(...args) this.errors = [...(this.errors || []), "\n", ...args] } complete(...args){ + timeStamp(...args) if(this.errors){ - this.failed(...[...this.errors, "\n", ...args]) + this.ping('fail', ...[...this.errors, "\n", ...args]) }else{ - this.success(...args) + this.ping(undefined, ...args) } } From 0ba89b5580dca4ab6d1f08115965dc31b86806e1 Mon Sep 17 00:00:00 2001 From: TS-Joe0x Date: Fri, 22 Apr 2022 11:31:38 +0200 Subject: [PATCH 402/862] Updated documentation for systemd execution --- .gitignore | 1 + README.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++- restake.iml | 9 ++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 restake.iml diff --git a/.gitignore b/.gitignore index 644c9b9b9..5dacfa6a2 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ yarn-debug.log* yarn-error.log* .vim +.iml # Parcel related files dist/ diff --git a/README.md b/README.md index 887caab27..ecf906757 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ In the future, `correctSlip44` will become the default and you will need to set You can run the autostaking script using `docker-compose` or using `npm` directly. In both cases you will need to provide your mnemonic in a `MNEMONIC` environment variable. -Instructions are provided for Docker Compose and will be expanded later. +## Instructions for Docker Compose ### Install Docker and Docker Compose @@ -95,6 +95,52 @@ Pass a network name to run the script for a single network at a time. ```bash docker-compose run --rm app npm run autostake osmosis ``` +## Instructions for systemd service + +### Install nodejs@v17 + +```bash +curl -sL https://deb.nodesource.com/setup_17.x -o /tmp/nodesource_setup.sh +# read the script file and when you're sure it's safe run it +chmod +x /tmp/nodesource_setup.sh +/tmp/nodesource_setup.sh +apt install nodejs -y +node --version +> v17.9.0 +npm --version +> 8.5.5 + +``` +### Clone the repository and build it + +```bash +git clone https://github.com/eco-stake/restake +cd restake +npm install && npm run build +``` + +### Create systemd service + +``` +$ cat /etc/systemd/system/restake.service +[Unit] +Description=Restake service with timer +After=network-online.target +Wants=restake.timer # keep this line if you setup the restake.timer service below, remove for cron + +[Service] +Type=oneshot +User=cosmos +WorkingDirectory=/path/to/cloned/repo +Environment=MNEMONIC="your hot wallet mnemonic here" +Environment=NODE_ENV=production +Environment=DIRECTORY_DOMAIN=cosmos.directory +Environment=DIRECTORY_PROTOCOL=https +ExecStart=/usr/bin/npm run autostake + +[Install] +WantedBy=multi-user.target +``` ### Updating your local version @@ -107,6 +153,11 @@ git pull docker-compose run --rm app npm install docker-compose build --no-cache ``` +or without docker +```bash +git pull +npm install && npm run build +``` ### Setting up Cron to make sure the script runs daily @@ -124,6 +175,13 @@ crontab -e 0 21 * * * /bin/bash -c "cd restake && docker-compose run --rm app npm run autostake" > ./restake.log 2>&1 ``` +or without docker +```bash +crontab -e + +0 21 * * * /bin/bash -c "systemctl start restake" +``` + #### Using `systemd-timer` diff --git a/restake.iml b/restake.iml new file mode 100644 index 000000000..8021953ed --- /dev/null +++ b/restake.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file From d680cef3b276320648e925f5ee5c6c028d6f6082 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 24 Apr 2022 17:43:00 +0100 Subject: [PATCH 403/862] Automatic dark theme and persistent switcher --- src/assets/logo-white.png | Bin 0 -> 10928 bytes src/assets/logo-white@2x.png | Bin 0 -> 20194 bytes src/assets/logo-white@3x.png | Bin 0 -> 29845 bytes src/assets/powered-by-akash-white.svg | 26 ++++++ src/components/App.css | 15 +++ src/components/App.js | 68 ++++++++++++-- src/components/Delegations.js | 13 ++- src/components/NetworkFinder.js | 45 ++++++++- src/components/NetworkSelect.js | 126 ++++++++++++++------------ src/components/TooltipIcon.js | 2 +- src/components/ValidatorLink.js | 2 +- src/index.html | 15 +-- src/index.js | 3 +- 13 files changed, 225 insertions(+), 90 deletions(-) create mode 100644 src/assets/logo-white.png create mode 100644 src/assets/logo-white@2x.png create mode 100644 src/assets/logo-white@3x.png create mode 100644 src/assets/powered-by-akash-white.svg diff --git a/src/assets/logo-white.png b/src/assets/logo-white.png new file mode 100644 index 0000000000000000000000000000000000000000..04de898b08280c862e54830e09e3d48cecde3b72 GIT binary patch literal 10928 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

    eXkMR2jT1|m;BBvoy&#T_PE6LZ- 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 literal 0 HcmV?d00001 diff --git a/src/assets/powered-by-akash-white.svg b/src/assets/powered-by-akash-white.svg new file mode 100644 index 000000000..ab58ef062 --- /dev/null +++ b/src/assets/powered-by-akash-white.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/App.css b/src/components/App.css index a8095f214..7a4f04769 100644 --- a/src/components/App.css +++ b/src/components/App.css @@ -1,3 +1,18 @@ +body{ + --bs-font-sans-serif: Lato, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + "Helvetica Neue", + Arial, + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol"; + --bs-body-font-family: var(--bs-font-sans-serif); +} + header a.moniker{ margin-left: 15px; } diff --git a/src/components/App.js b/src/components/App.js index a5e39afbb..58e4acf51 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -15,13 +15,23 @@ import { Button, Badge, } from 'react-bootstrap'; +import { + Droplet, + DropletFill, + DropletHalf +} from 'react-bootstrap-icons' import {CopyToClipboard} from 'react-copy-to-clipboard'; import GitHubButton from 'react-github-btn' import Logo from '../assets/logo.png' import Logo2x from '../assets/logo@2x.png' import Logo3x from '../assets/logo@3x.png' +import LogoWhite from '../assets/logo-white.png' +import LogoWhite2x from '../assets/logo-white@2x.png' +import LogoWhite3x from '../assets/logo-white@3x.png' import PoweredByAkash from '../assets/powered-by-akash.svg' +import PoweredByAkashWhite from '../assets/powered-by-akash-white.svg' +import TooltipIcon from './TooltipIcon'; class App extends React.Component { constructor(props) { @@ -171,13 +181,40 @@ class App extends React.Component { }, 2000) } + themeIcon() { + const { theme, themeChoice, themeDefault, setThemeChoice } = this.props + let icon, switchTo + let iconProps = { + size: '1.4em', + className: 'me-3', + role: 'button', + onClick: () => setThemeChoice(switchTo) + } + if(themeChoice === 'auto'){ + icon = + switchTo = theme === 'dark' ? 'light' : 'dark' + }else{ + icon = themeChoice === 'dark' ? : + switchTo = themeDefault !== theme ? 'auto' : theme === 'dark' ? 'light' : 'dark' + } + const tooltip = `Switch to ${switchTo} mode` + return ( + + ) + } + render() { return (
    -
    - this.setState({ showAbout: true })} role="button" className="text-dark text-decoration-none"> - REStake +
    + this.setState({ showAbout: true })} role="button" className="text-reset text-decoration-none"> + {this.props.theme === 'light' + ? ( + REStake + ) : ( + REStake + )}
    {this.state.address && @@ -211,7 +248,8 @@ class App extends React.Component { )} } -
    +
    + {this.themeIcon()} { this.setState({ showNetworkSelect: false }) }} networks={this.props.networks} network={this.props.network} validators={this.props.validators} @@ -253,7 +291,7 @@ class App extends React.Component { }

    - Enabling REStake will authorize the validator to send WithdrawDelegatorReward and Delegate transactions on your behalf for 1 year using Authz.
    + Enabling REStake will authorize the validator to send WithdrawDelegatorReward and Delegate transactions on your behalf for 1 year using Authz.
    They will only be authorized to delegate to their own validator. You can revoke the authorization at any time and everything is open source.

    @@ -265,22 +303,32 @@ class App extends React.Component {

    this.setState({ showAbout: false })} /> diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 2604e3f8c..03f9b60b2 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -293,10 +293,15 @@ class Delegations extends React.Component { } orderedOperators() { - return _.sortBy(this.props.operators, ({ address }) => { + const grants = this.operatorGrants() + return _.sortBy(this.props.operators, ({ address, botAddress }) => { if (!this.props.delegations) return 0 - return this.props.delegations[address] ? 0 : 1 + if(this.props.delegations[address]){ + return grants[botAddress]?.grantsExist ? -1 : 0 + }else{ + return 1 + } }); } @@ -430,7 +435,7 @@ class Delegations extends React.Component { operator && delegation ? grants.grantsValid ? "table-success" - : grants.grantsExist ? "table-danger" : "table-warning" + : grants.grantsExist ? "table-danger" : undefined : undefined; if (isValidatorOperator) rowVariant = 'table-info' @@ -685,7 +690,7 @@ class Delegations extends React.Component { dismissible={false} >

    Ledger devices are not supported in the REStake UI currently. Support will be added as soon as it is possible.

    -

    this.setState({ showAboutLedger: true })} role="button" className="text-dark text-decoration-underline">A manual workaround is possible using the CLI

    +

    this.setState({ showAboutLedger: true })} role="button" className="text-reset text-decoration-underline">A manual workaround is possible using the CLI

    )} diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 272d31447..a9bdbc4be 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -1,5 +1,5 @@ import _ from 'lodash' -import React, { useEffect, useReducer } from 'react'; +import React, { useEffect, useState, useReducer } from 'react'; import { useParams, useNavigate } from "react-router-dom"; import Network from '../utils/Network.mjs' import CosmosDirectory from '../utils/CosmosDirectory.mjs' @@ -12,12 +12,21 @@ import { import networksData from '../networks.json'; +const LIGHT_THEME = 'cosmo' +const DARK_THEME = 'superhero' + function NetworkFinder() { const params = useParams(); const navigate = useNavigate() const directory = CosmosDirectory() + const LS_THEME_KEY = "restake-theme"; + const LS_THEME = localStorage.getItem(LS_THEME_KEY) + + const [theme, setTheme] = useState() + const [themeChoice, setThemeChoice] = useState(LS_THEME || 'auto') + const [themeDefault, setThemeDefault] = useState('light') const [state, setState] = useReducer( (state, newState) => ({...state, ...newState}), {loading: true, networks: {}, operators: [], validators: {}} @@ -49,6 +58,39 @@ function NetworkFinder() { navigate("/" + network.name); } + useEffect(() => { + const setThemeEvent = (event) => { + setTheme(event.matches ? "dark" : "light") + setThemeDefault(event.matches ? "dark" : "light") + } + const matchMedia = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)') + if (themeChoice !== 'auto') { + setTheme(themeChoice) + } else if (matchMedia) { + matchMedia.addEventListener('change', setThemeEvent); + setThemeEvent(matchMedia) + } else { + setTheme('light') + } + + if(localStorage.getItem(LS_THEME_KEY) !== themeChoice){ + localStorage.setItem(LS_THEME_KEY, themeChoice) + } + + return () => { + matchMedia && matchMedia.removeEventListener('change', setThemeEvent) + } + }, [themeChoice]) + + useEffect(() => { + if(theme){ + const themeLink = document.getElementById("theme-style"); + const themeName = theme === 'dark' ? DARK_THEME : LIGHT_THEME + themeLink.setAttribute("href", `https://cdn.jsdelivr.net/npm/bootswatch@5.1.3/dist/${themeName}/bootstrap.min.css`); + document.body.classList.toggle('dark-theme', theme === 'dark'); + } + }, [theme]) + useEffect(() => { if(state.error) return if(!Object.keys(state.networks).length){ @@ -128,6 +170,7 @@ function NetworkFinder() { return changeNetwork(network, validators)} + theme={theme} themeChoice={themeChoice} themeDefault={themeDefault} setThemeChoice={setThemeChoice} />; } diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index 37c3f9498..8a9df447e 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -20,8 +20,8 @@ function NetworkSelect(props) { const [validators, setValidators] = useState([]); const [operatorCounts, setOperatorCounts] = useState({}); const [options, setOptions] = useReducer( - (state, newState) => ({...state, ...newState}), - {networks: [], operators: [], network: {value: ''}} + (state, newState) => ({ ...state, ...newState }), + { networks: [], operators: [], network: { value: '' } } ) function handleOpen() { @@ -78,9 +78,9 @@ function NetworkSelect(props) { } useEffect(() => { - if(props.show && !show){ + if (props.show && !show) { handleOpen() - }else if(!props.show && show){ + } else if (!props.show && show) { handleClose() } }, [props.show]) @@ -91,10 +91,10 @@ function NetworkSelect(props) { networks: networks.map(el => { const network = new Network(el) return { - value: el.name, - label: el.pretty_name, + value: el.name, + label: el.pretty_name, image: el.image, - operatorCount: el.operators?.length || operatorCounts[el.name], + operatorCount: el.operators?.length || operatorCounts[el.name], authz: el.authzSupport, online: !network.usingDirectory || directoryConnected(el) } @@ -112,7 +112,7 @@ function NetworkSelect(props) { return ( <> - + : ( + + )} + } diff --git a/src/components/TooltipIcon.js b/src/components/TooltipIcon.js index 88b05aa89..3d1f0e86d 100644 --- a/src/components/TooltipIcon.js +++ b/src/components/TooltipIcon.js @@ -10,7 +10,7 @@ function TooltipIcon(props) { {(props.children || props.tooltip) ? ( {props.children || props.tooltip} diff --git a/src/components/ValidatorLink.js b/src/components/ValidatorLink.js index 4d9d16e2d..81a82aa36 100644 --- a/src/components/ValidatorLink.js +++ b/src/components/ValidatorLink.js @@ -17,7 +17,7 @@ function ValidatorLink(props) { } return ( - + {props.children || } ); diff --git a/src/index.html b/src/index.html index 30d6e4ca1..ce38c1484 100644 --- a/src/index.html +++ b/src/index.html @@ -10,26 +10,13 @@ content="REStake allows validators to auto-compound your staking rewards for you" /> - REStake | Auto-compound your staking rewards +
    - diff --git a/src/index.js b/src/index.js index 21511b048..929f5177e 100644 --- a/src/index.js +++ b/src/index.js @@ -10,9 +10,10 @@ import { } from "react-router-dom"; import NetworkFinder from './components/NetworkFinder' -import './index.css'; import reportWebVitals from './utils/reportWebVitals'; +import './index.css'; + const app = ( From 777fbd89e530735b48b83a87ddb442e527ee4894 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 25 Apr 2022 01:56:24 +0100 Subject: [PATCH 404/862] Move APY and authz checks to cosmos.directory --- package.json | 3 +- scripts/checkAuthz.mjs | 42 ------------ src/ApyClient.mjs | 105 ------------------------------ src/components/Delegations.js | 13 ++-- src/components/NetworkSelect.js | 4 +- src/networks.json | 110 +++++++++++--------------------- src/utils/Chain.mjs | 62 ++++-------------- src/utils/CosmosDirectory.mjs | 4 +- src/utils/Network.mjs | 22 ++++++- 9 files changed, 78 insertions(+), 287 deletions(-) delete mode 100644 scripts/checkAuthz.mjs delete mode 100644 src/ApyClient.mjs diff --git a/package.json b/package.json index 87854141b..02133bcb0 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,7 @@ "start": "npx parcel src/index.html", "prebuild": "rm -rf dist/", "build": "npx parcel build src/index.html", - "autostake": "node scripts/autostake.mjs", - "checkAuthz": "node scripts/checkAuthz.mjs" + "autostake": "node scripts/autostake.mjs" }, "eslintConfig": { "extends": [ diff --git a/scripts/checkAuthz.mjs b/scripts/checkAuthz.mjs deleted file mode 100644 index c917209ed..000000000 --- a/scripts/checkAuthz.mjs +++ /dev/null @@ -1,42 +0,0 @@ -import fs from 'fs' -import _ from 'lodash' -import Network from '../src/utils/Network.mjs' -import { executeSync } from '../src/utils/Helpers.mjs' - -async function checkAuthz(networkName) { - const networksData = fs.readFileSync('src/networks.json'); - const networks = JSON.parse(networksData); - const calls = networks.map(data => { - return async () => { - if(networkName && data.name !== networkName) return - try { - const network = new Network(data) - await network.load() - await network.connect() - const support = await testAuthz(network) - if (data.authzSupport !== support){ - console.log(network.name, 'support is different', support ? 'ENABLED' : 'DISABLED') - } - } catch (error) { - console.log(data.name, error.message) - } - } - }) - await executeSync(calls, 5) -} -async function testAuthz(network) { - return network.queryClient - .getGrants() - .then( - (result) => { }, - (error) => { - if (error.response && error.response.status === 501) { - return false - } - return true - } - ); -} - -const networkName = process.argv[2] -checkAuthz(networkName) \ No newline at end of file diff --git a/src/ApyClient.mjs b/src/ApyClient.mjs deleted file mode 100644 index bbdc98513..000000000 --- a/src/ApyClient.mjs +++ /dev/null @@ -1,105 +0,0 @@ -import axios from "axios"; -import { - setupStakingExtension, - QueryClient as CosmjsQueryClient, - setupBankExtension, - setupDistributionExtension, - setupMintExtension, - setupGovExtension, -} from "@cosmjs/stargate"; -import { Tendermint34Client } from "@cosmjs/tendermint-rpc"; - -const ApyClient = (chain, rpcUrl, restUrl) => { - const { chainId, denom } = chain - - async function getApy(validators, operators) { - const chainApr = await getChainApr(denom); - let validatorApy = {}; - for (const [address, validator] of Object.entries(validators)) { - if(validator.jailed || validator.status !== 'BOND_STATUS_BONDED'){ - validatorApy[address] = 0 - }else{ - const commission = validator.commission.commission_rates.rate - const operator = operators.find((el) => el.address === address) - const periodPerYear = operator && chain.authzSupport ? operator.runsPerDay() * 365 : 1; - const realApr = chainApr * (1 - commission); - const apy = (1 + realApr / periodPerYear) ** periodPerYear - 1; - validatorApy[address] = apy; - } - } - return validatorApy; - } - - function duration(epochs, epochIdentifier) { - const epoch = epochs.find((epoch) => epoch.identifier === epochIdentifier); - if (!epoch) { - return 0; - } - - // Actually, the date type of golang protobuf is returned by the unit of seconds. - return parseInt(epoch.duration.replace("s", "")); - } - - async function makeClient() { - const tmClient = await Tendermint34Client.connect(rpcUrl); - return CosmjsQueryClient.withExtensions( - tmClient, - setupStakingExtension, - setupBankExtension, - setupDistributionExtension, - setupMintExtension, - setupGovExtension - ); - } - - async function getChainApr(denom) { - const client = await makeClient(); - const pool = await client.staking.pool(); - const supply = await client.bank.supplyOf(denom); - const bondedTokens = pool.pool.bondedTokens; - const totalSupply = supply.amount; - if (chainId.startsWith("osmosis")) { - const apr = await osmosisApr(totalSupply, bondedTokens); - return apr; - } else if (chainId.startsWith("sifchain")) { - const aprRequest = await axios.get( - "https://data.sifchain.finance/beta/validator/stakingRewards" - ); - const apr = aprRequest.data.rate; - return apr; - } else { - const req = await client.mint.inflation(); - const baseInflation = req.toFloatApproximation(); - const ratio = bondedTokens / totalSupply; - const apr = baseInflation / ratio; - return apr; - } - } - - async function osmosisApr(totalSupply, bondedTokens) { - const mintParams = await axios.get( - restUrl + "/osmosis/mint/v1beta1/params" - ); - const osmosisEpochs = await axios.get( - restUrl + "/osmosis/epochs/v1beta1/epochs" - ); - const epochProvisions = await axios.get( - restUrl + "/osmosis/mint/v1beta1/epoch_provisions" - ); - const { params } = mintParams.data; - const { epochs } = osmosisEpochs.data; - const { epoch_provisions } = epochProvisions.data; - const mintingEpochProvision = parseFloat(params.distribution_proportions.staking) * epoch_provisions; - const epochDuration = duration(epochs, params.epoch_identifier); - const yearMintingProvision = (mintingEpochProvision * (365 * 24 * 3600)) / epochDuration; - const baseInflation = yearMintingProvision / totalSupply; - const bondedRatio = bondedTokens / totalSupply; - const apr = baseInflation / bondedRatio; - return apr; - } - - return { - getApy - }; -} -export default ApyClient; \ No newline at end of file diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 03f9b60b2..23ad0ae2c 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -431,13 +431,8 @@ class Delegations extends React.Component { const denomRewards = rewards && this.denomRewards(rewards); const operator = this.operatorForValidator(validatorAddress); const grants = operator && this.operatorGrants()[operator.botAddress] - let rowVariant = - operator && delegation - ? grants.grantsValid - ? "table-success" - : grants.grantsExist ? "table-danger" : undefined - : undefined; + let rowVariant if (isValidatorOperator) rowVariant = 'table-info' const delegationBalance = (delegation && delegation.balance) || { @@ -471,7 +466,7 @@ class Delegations extends React.Component { network={this.props.network} validator={validator} operator={operator} - grants={operator && this.operatorGrants()[operator.botAddress]} + grants={grants} delegation={delegation} authzSupport={this.authzSupport()} restakePossible={this.restakePossible()} @@ -549,9 +544,9 @@ class Delegations extends React.Component { this.restakePossible() && ( <> this.showValidatorModal(validatorAddress, { activeTab: 'restake' })}> - {this.operatorGrants()[operator.botAddress].grantsValid ? 'Manage REStake' : 'Enable REStake'} + {grants.grantsValid ? 'Manage REStake' : 'Enable REStake'} - {this.operatorGrants()[operator.botAddress].grantsExist && ( + {grants.grantsExist && ( { + return network.connect().then(() => { if (network.connected) { setValidators(network.getValidators()); setLoading(false); @@ -95,7 +95,7 @@ function NetworkSelect(props) { label: el.pretty_name, image: el.image, operatorCount: el.operators?.length || operatorCounts[el.name], - authz: el.authzSupport, + authz: el.params?.authz, online: !network.usingDirectory || directoryConnected(el) } }), diff --git a/src/networks.json b/src/networks.json index 64146b89b..a9174be82 100644 --- a/src/networks.json +++ b/src/networks.json @@ -2,160 +2,130 @@ { "name": "osmosis", "gasPrice": "0.0025uosmo", - "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", - "authzSupport": true + "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t" }, { "name": "juno", "apyEnabled": false, - "gasPrice": "0.0025ujuno", - "authzSupport": true + "gasPrice": "0.0025ujuno" }, { "name": "cosmoshub", - "gasPrice": "0.0025uatom", - "authzSupport": true + "gasPrice": "0.0025uatom" }, { "name": "akash", - "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", - "authzSupport": false + "ownerAddress": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf" }, { "name": "chihuahua", "gasPrice": "0.025uhuahua", - "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", - "authzSupport": false + "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz" }, { "name": "gravitybridge", - "gasPrice": "0.025ugraviton", - "authzSupport": true + "gasPrice": "0.025ugraviton" }, { "name": "regen", "gasPrice": "0.03uregen", - "ownerAddress": "regenvaloper1c4y3j05qx652rnxm5mg4yesqdkmhz2f6dl7hhk", - "authzSupport": true + "ownerAddress": "regenvaloper1c4y3j05qx652rnxm5mg4yesqdkmhz2f6dl7hhk" }, { "name": "terra", "apyEnabled": false, - "gasPrice": "0.015uluna", - "authzSupport": true + "gasPrice": "0.015uluna" }, { "name": "sentinel", - "gasPrice": "0.02udvpn", - "authzSupport": true + "gasPrice": "0.02udvpn" }, { "name": "dig", "gasPrice": "0.01udig", - "ownerAddress": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay", - "authzSupport": true + "ownerAddress": "digvaloper136avwnuvvy94dqmtnaue2nfvjes8xr37h9rzay" }, { "name": "bitcanna", - "gasPrice": "0.001ubcna", - "authzSupport": false + "gasPrice": "0.001ubcna" }, { "name": "emoney", "apyEnabled": false, - "gasPrice": "0.08ungm", - "authzSupport": false + "gasPrice": "0.08ungm" }, { "name": "kava", "apyEnabled": false, - "gasPrice": "0.00008ukava", - "authzSupport": false + "gasPrice": "0.00008ukava" }, { "name": "desmos", "apyEnabled": false, - "gasPrice": "0.001udsm", - "authzSupport": true + "gasPrice": "0.001udsm" }, { "name": "cryptoorgchain", - "gasPrice": "0.025basecro", - "authzSupport": true + "gasPrice": "0.025basecro" }, { "name": "evmos", "enabled": false, - "apyEnabled": false, - "authzSupport": true + "apyEnabled": false }, { "name": "sifchain", - "authzSupport": false, "gasPrice": "1500000000000rowan", "ownerAddress": "sifvaloper19t5nk5ceq5ga75epwdqhnupwg0v9339p096ydz" }, { - "name": "lumnetwork", - "authzSupport": true + "name": "lumnetwork" }, { "name": "stargaze", - "apyEnabled": false, - "authzSupport": true + "apyEnabled": false }, { "name": "comdex", "apyEnabled": false, - "ownerAddress": "comdexvaloper17f70yjkvmvld379904jaddx9h0f74n32pjtmp6", - "authzSupport": false + "ownerAddress": "comdexvaloper17f70yjkvmvld379904jaddx9h0f74n32pjtmp6" }, { "name": "cheqd", - "gasPrice": "25ncheq", - "authzSupport": true + "gasPrice": "25ncheq" }, { - "name": "umee", - "authzSupport": true + "name": "umee" }, { - "name": "bitsong", - "authzSupport": true + "name": "bitsong" }, { - "name": "persistence", - "authzSupport": true + "name": "persistence" }, { "name": "agoric", - "apyEnabled": false, - "authzSupport": true + "apyEnabled": false }, { "name": "impacthub", "gasPrice": "0.025uixo", - "gasPricePrefer": "0.1uixo", - "authzSupport": false + "gasPricePrefer": "0.1uixo" }, { "name": "kichain", - "gasPrice": "0.025uxki", - "authzSupport": false + "gasPrice": "0.025uxki" }, { - "name": "sommelier", - "authzSupport": true + "name": "sommelier" }, { "name": "konstellation", - "image": "https://raw.githubusercontent.com/Konstellation/DARC_token/main/darctoken.svg", - "authzSupport": false + "image": "https://raw.githubusercontent.com/Konstellation/DARC_token/main/darctoken.svg" }, { "name": "fetchhub", - "gasPrice": "5000afet", - "authzSupport": true + "gasPrice": "5000afet" }, { "name": "cerberus", @@ -163,34 +133,28 @@ "autostake": { "batchTxs": 100 }, - "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f", - "authzSupport": true + "ownerAddress": "cerberusvaloper1tat2cy3f9djtq9z7ly262sqngcarvaktr0w78f" }, { "name": "secretnetwork", "gasPrice": "0.025uscrt", - "gasPricePrefer": "0.05uscrt", - "authzSupport": true + "gasPricePrefer": "0.05uscrt" }, { "name": "bostrom", - "gasPrice": "0boot", - "authzSupport": true + "gasPrice": "0boot" }, { "name": "starname", - "gasPrice": "10uvoi", - "authzSupport": false + "gasPrice": "10uvoi" }, { "name": "rizon", - "gasPrice": "0.0001uatolo", - "authzSupport": true + "gasPrice": "0.0001uatolo" }, { "name": "decentr", - "gasPrice": "0.025udec", - "authzSupport": false + "gasPrice": "0.025udec" }, { "name": "assetmantle", @@ -200,8 +164,7 @@ "average": 0.025, "high": 0.04 }, - "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd", - "authzSupport": true + "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd" }, { "name": "crescent", @@ -211,7 +174,6 @@ "average": 0.025, "high": 0.04 }, - "authzSupport": true, "apyEnabled": false } ] diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index 4489caf24..447571af0 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -6,56 +6,22 @@ const Chain = async (data) => { const chainData = await directory.getChainData(data.name); const tokenData = await directory.getTokenData(data.name); - const getChainInfo = () => { - return { - prettyName: data.prettyName || chainData.pretty_name, - chainId: data.chainId || chainData.chain_id, - prefix: data.prefix || chainData.bech32_prefix, - slip44: data.slip44 || chainData.slip44 || 118, - authzSupport: !!data.authzSupport - } - } - - const getTokenInfo = () => { - const asset = tokenData.assets[0] - const base = asset.denom_units.find(el => el.denom === asset.base) - const token = asset.denom_units.find(el => el.denom === asset.display) - return { - denom: data.denom || base.denom, - symbol: data.symbol || token.denom, - decimals: data.decimals || token.exponent || 6, - image: data.image || (asset.logo_URIs && (asset.logo_URIs.png || asset.logo_URIs.svg)), - coinGeckoId: asset.coingecko_id - } - } - - const { - prettyName, - chainId, - prefix, - slip44, - authzSupport - } = getChainInfo() - - const { - denom, - symbol, - decimals, - image, - coinGeckoId - } = getTokenInfo() + const asset = tokenData.assets[0] + const base = asset.denom_units.find(el => el.denom === asset.base) + const token = asset.denom_units.find(el => el.denom === asset.display) return { - prettyName, - chainId, - prefix, - slip44, - authzSupport, - denom, - symbol, - decimals, - image, - coinGeckoId, + prettyName: data.prettyName || chainData.pretty_name, + chainId: data.chainId || chainData.chain_id, + prefix: data.prefix || chainData.bech32_prefix, + slip44: data.slip44 || chainData.slip44 || 118, + estimatedApr: chainData.params?.calculated_apr, + authzSupport: data.authzSupport ?? chainData.params?.authz, + denom: data.denom || base.denom, + symbol: data.symbol || token.denom, + decimals: data.decimals || token.exponent || 6, + image: data.image || (asset.logo_URIs && (asset.logo_URIs.png || asset.logo_URIs.svg)), + coinGeckoId: asset.coingecko_id, chainData, tokenData } diff --git a/src/utils/CosmosDirectory.mjs b/src/utils/CosmosDirectory.mjs index 38d176801..30d969295 100644 --- a/src/utils/CosmosDirectory.mjs +++ b/src/utils/CosmosDirectory.mjs @@ -24,8 +24,8 @@ function CosmosDirectory(){ } function getChainData(name) { - return axios.get([chainsUrl, name, 'chain'].join('/')) - .then(res => res.data) + return axios.get([chainsUrl, name].join('/')) + .then(res => res.data.chain) } async function getTokenData(name) { diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 20f8d6046..443e948fe 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -2,7 +2,6 @@ import _ from 'lodash' import { multiply, pow, format, bignumber } from 'mathjs' import QueryClient from './QueryClient.mjs' import SigningClient from './SigningClient.mjs' -import ApyClient from '../ApyClient.mjs' import Operator from './Operator.mjs' import Chain from './Chain.mjs' import CosmosDirectory from './CosmosDirectory.mjs' @@ -48,6 +47,7 @@ class Network { this.decimals = this.chain.decimals this.image = this.chain.image this.coinGeckoId = this.chain.coinGeckoId + this.estimatedApr = this.chain.estimatedApr this.authzSupport = this.chain.authzSupport const defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed' }) + this.denom this.gasPrice = this.data.gasPrice || defaultGasPrice @@ -58,10 +58,8 @@ class Network { async connect() { try { this.queryClient = await QueryClient(this.chain.chainId, this.rpcUrl, this.restUrl) - this.apyClient = ApyClient(this.chain, this.queryClient.rpcUrl, this.queryClient.restUrl) this.restUrl = this.queryClient.restUrl this.rpcUrl = this.queryClient.rpcUrl - this.getApy = this.apyClient.getApy this.connected = this.queryClient.connected } catch (error) { console.log(error) @@ -69,6 +67,24 @@ class Network { } } + async getApy(validators, operators){ + const chainApr = this.chain.estimatedApr + let validatorApy = {}; + for (const [address, validator] of Object.entries(validators)) { + if(validator.jailed || validator.status !== 'BOND_STATUS_BONDED'){ + validatorApy[address] = 0 + }else{ + const commission = validator.commission.commission_rates.rate + const operator = operators.find((el) => el.address === address) + const periodPerYear = operator && this.chain.authzSupport ? operator.runsPerDay() * 365 : 1; + const realApr = chainApr * (1 - commission); + const apy = (1 + realApr / periodPerYear) ** periodPerYear - 1; + validatorApy[address] = apy; + } + } + return validatorApy; + } + signingClient(wallet, key, gasPrice) { if (!this.queryClient) return From 6fd35adebba1270b819147429c69d4b217537836 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 25 Apr 2022 02:53:52 +0100 Subject: [PATCH 405/862] Show validator modal sooner --- src/components/Delegations.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 23ad0ae2c..01e7751d2 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -53,6 +53,10 @@ class Delegations extends React.Component { clearInterval(this.state.grantInterval); } + if (prevProps.validator !== this.props.validator && this.props.validator && !this.state.validatorModal.show) { + this.showValidatorModal(this.props.validator.operator_address) + } + if (!this.props.address) return; if (this.props.address !== prevProps.address) { @@ -71,15 +75,11 @@ class Delegations extends React.Component { } } - if (!this.props.delegations) return - - const delegationsChanged = _.difference(Object.keys(this.props.delegations), Object.keys(prevProps.delegations || {})).length > 0 - if (delegationsChanged) { - this.getGrants() - } - - if (prevProps.validator !== this.props.validator && this.props.validator && !this.state.validatorModal.show) { - this.showValidatorModal(this.props.validator.operator_address) + if (this.props.delegations && prevProps.delegations){ + const delegationsChanged = _.difference(Object.keys(this.props.delegations), Object.keys(prevProps.delegations || {})).length > 0 + if (delegationsChanged) { + this.getGrants() + } } } From 9c26efda9a84536e60934a6feb9db3ef0f8dacd3 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 25 Apr 2022 12:24:49 +0100 Subject: [PATCH 406/862] Enable APR/APY for Juno, Desmos and Comdex --- src/networks.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/networks.json b/src/networks.json index a9174be82..3033a97f9 100644 --- a/src/networks.json +++ b/src/networks.json @@ -6,7 +6,6 @@ }, { "name": "juno", - "apyEnabled": false, "gasPrice": "0.0025ujuno" }, { @@ -61,7 +60,6 @@ }, { "name": "desmos", - "apyEnabled": false, "gasPrice": "0.001udsm" }, { @@ -87,7 +85,6 @@ }, { "name": "comdex", - "apyEnabled": false, "ownerAddress": "comdexvaloper17f70yjkvmvld379904jaddx9h0f74n32pjtmp6" }, { From f12c4c6ed25d737597b1fc31ae4952bf528c7718 Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Tue, 26 Apr 2022 16:53:16 -0500 Subject: [PATCH 407/862] Add MEME Network --- src/networks.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/networks.json b/src/networks.json index 3033a97f9..a30e469d5 100644 --- a/src/networks.json +++ b/src/networks.json @@ -172,5 +172,9 @@ "high": 0.04 }, "apyEnabled": false + }, + { + "name": "meme", + "gasPrice": "0.025umeme" } ] From 47ab472578c79bb81236f0b4dcb30645d5fcd95a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 29 Apr 2022 00:17:14 +0100 Subject: [PATCH 408/862] Handle all networks, revamp network select --- src/components/App.js | 2 +- src/components/Delegations.js | 16 ++--- src/components/NetworkFinder.js | 13 ++-- src/components/NetworkSelect.js | 107 +++++++++++++++++++++++--------- src/components/TooltipIcon.js | 1 - src/utils/Network.mjs | 1 + 6 files changed, 94 insertions(+), 46 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 58e4acf51..2effc156a 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -119,7 +119,7 @@ class App extends React.Component { } catch (e) { console.log(e) return this.setState({ - error: 'Failed to connect to signing client. API may be down.', + error: 'Failed to connect to signing client: ' + e.message, loading: false }) } diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 01e7751d2..65b712c64 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -663,18 +663,14 @@ class Delegations extends React.Component { const alerts = ( <> {!this.authzSupport() && ( - - {this.props.network.prettyName} doesn't support Authz just yet. You - can manually restake for now and REStake is ready when support is - enabled + + {this.props.network.prettyName} doesn't support Authz just yet. You can stake and compound manaully for now and REStake will update automatically when support is added. )} - {this.authzSupport() && !this.props.operators.length && ( - + {this.props.network.experimental && ( + + This network was added to REStake automatically and has not been thoroughly tested yet. Raise an issue if you have any problems. + )} {this.authzSupport() && this.props.operators.length > 0 && diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index a9bdbc4be..bb358adda 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -41,11 +41,16 @@ function NetworkFinder() { return {} } - const networks = networksData.filter(el => el.enabled !== false).map(data => { - const registryData = registryNetworks[data.name] || {} - return {...registryData, ...data} + const networks = Object.values(registryNetworks).map(data => { + const networkData = networksData.find(el => el.name === data.path) + if(networkData && networkData.enabled === false) return + if(!data.image) return + + if(!networkData) data.experimental = true + + return {...data, ...networkData} }) - return _.compact(networks).reduce((a, v) => ({ ...a, [v.name]: v}), {}) + return _.compact(networks).reduce((a, v) => ({ ...a, [v.path]: v}), {}) } const changeNetwork = (network) => { diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index 411870617..3a4fdaab0 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -2,6 +2,7 @@ import React, { useState, useReducer, useEffect } from 'react'; import CosmosDirectory from '../utils/CosmosDirectory.mjs'; import Network from '../utils/Network.mjs' +import TooltipIcon from './TooltipIcon.js'; import { Button, @@ -10,6 +11,8 @@ import { Badge } from 'react-bootstrap' +import { CheckCircle, XCircle } from "react-bootstrap-icons"; + import Select from 'react-select'; function NetworkSelect(props) { @@ -66,7 +69,7 @@ function NetworkSelect(props) { }); }).catch(error => { console.log(error); - setError('Unable to connect to this network currently. Try again later.'); + setError('Could not connect to this network. Try again later'); setLoading(false); }); } @@ -77,6 +80,30 @@ function NetworkSelect(props) { return ['rpc', 'rest'].every(type => data['best_apis'][type].length > 0) } + function renderCheck({ title, failTitle, description, failDescription, state, successClass, failClass, identifier }){ + const className = state ? (successClass || 'success') : (failClass || 'warning') + + const content = ( +
    + {state ? ( + + ) : ( + + )}{state ? title : (failTitle || title)} +
    + ) + + return ( +
  6. + +
  7. + ) + b} + useEffect(() => { if (props.show && !show) { handleOpen() @@ -96,17 +123,11 @@ function NetworkSelect(props) { image: el.image, operatorCount: el.operators?.length || operatorCounts[el.name], authz: el.params?.authz, - online: !network.usingDirectory || directoryConnected(el) + online: !network.usingDirectory || directoryConnected(el), + experimental: network.experimental } }), - network: selectedNetwork && { - value: selectedNetwork.name, - label: selectedNetwork.prettyName, - image: selectedNetwork.image, - operatorCount: selectedNetwork.operators?.length, - authz: selectedNetwork.authzSupport, - online: true // modal will show status - } + network: selectedNetwork && selectedNetwork.name }) }, [props.networks, selectedNetwork, operatorCounts]) @@ -140,7 +161,7 @@ function NetworkSelect(props) {
    - - {['active', 'inactive'].map(status => { - return ( - - {statusResults(status).length > 0 && - - - - {status === 'active' && ( - - )} - - {status !== 'active' && ( - - )} - - {status === 'active' && props.network.apyEnabled !== false && ( - - )} - - - {statusResults(status).map(item => renderItem(item, status === 'active'))} - -
    #ValidatorStatusREStake - APY} - identifier="validators-apy" - > -
    -

    Based on commission, compounding frequency and estimated block times.

    -

    This is an estimate and best case scenario.

    -
    -
    -
    - } - {statusResults(status).length < 1 && -

    No results found

    - } -
    - ) - })} -
    +
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + {/* */} +
    +
    + {results.length > 0 && + + + + + + + + + {network.apyEnabled !== false && ( + + )} + + + + + + + {results.map(item => renderValidator(item))} + + + + + + + + + + + + + +
    #ValidatorREStake + Frequency + + APY} + identifier="delegations-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is a best case scenario and may not be 100% accurate.

    +
    +
    +
    DelegationRewards
    + + { + const delegation = delegations[result.operator_address] + if(!delegation) return sum + + return add(sum, delegation.balance.amount) + }, 0), + denom: network.denom + }} + decimals={network.decimals} /> + + + {props.rewards && ( + + { + const reward = props.rewards[result.operator_address]?.reward?.find(el => el.denom === network.denom) + if(!reward) return sum + + return add(sum, reward.amount) + }, 0), + denom: network.denom + }} + decimals={network.decimals} /> + + )} +
    + } + {results.length < 1 && +

    No validators found

    + } ) } diff --git a/src/components/Wallet.js b/src/components/Wallet.js deleted file mode 100644 index 19c0537b4..000000000 --- a/src/components/Wallet.js +++ /dev/null @@ -1,109 +0,0 @@ -import React from 'react' -import Delegations from './Delegations' -import AlertMessage from './AlertMessage'; - -import { - Spinner -} from 'react-bootstrap'; - -class Wallet extends React.Component { - constructor(props) { - super(props); - this.state = {} - this.getDelegations = this.getDelegations.bind(this); - } - - componentDidMount() { - this.getDelegations() - this.refreshInterval() - } - - componentDidUpdate(prevProps) { - if(this.props.network !== prevProps.network){ - clearInterval(this.state.refreshInterval); - this.setState({ delegations: undefined }) - } - - if (!this.props.address) return - if(this.props.address !== prevProps.address){ - this.getDelegations() - this.refreshInterval() - } - } - - componentWillUnmount(){ - clearInterval(this.state.refreshInterval); - } - - refreshInterval(){ - const interval = setInterval(() => { - this.getDelegations(true) - }, 30_000) - this.setState({refreshInterval: interval}) - } - - async getDelegations(hideError) { - this.props.queryClient.getDelegations(this.props.address) - .then( - (delegations) => { - const orderedAddresses = Object.keys(this.props.validators) - delegations = orderedAddresses.reduce((sum, address) => { - if(delegations[address]) sum[address] = delegations[address] - return sum - }, {}) - this.setState({ - isLoaded: true, - delegations: delegations, - }); - }, - (error) => { - if([404, 500].includes(error.response && error.response.status)){ - this.setState({ - delegations: {}, - isLoaded: true - }); - }else if(!hideError){ - this.setState({ - error: 'Failed to load delegations. API may be down.', - isLoaded: true - }); - } - } - ) - } - - render() { - if (!this.state.isLoaded) { - return ( -
    - - Loading... - -
    - ) - } - if (this.state.error) { - return ( - - ) - } - return ( -
    - -
    - ) - } -} - -export default Wallet; diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 2b71ef9da..138768818 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -51,8 +51,8 @@ class Network { this.coinGeckoId = this.chain.coinGeckoId this.estimatedApr = this.chain.estimatedApr this.authzSupport = this.chain.authzSupport - const defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed' }) + this.denom - this.gasPrice = this.data.gasPrice || defaultGasPrice + this.defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed' }) + this.denom + this.gasPrice = this.data.gasPrice || this.defaultGasPrice this.gasPriceStep = this.data.gasPriceStep this.gasPricePrefer = this.data.gasPricePrefer this.gasModifier = this.data.gasModifier || 1.3 From cb583636b51fa019099f3c19e181ae143da7611c Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 17 Jun 2022 20:31:04 +0100 Subject: [PATCH 469/862] Add simple governance features --- package-lock.json | 1849 +++++++--------------------- package.json | 1 + src/components/AlertMessage.js | 2 +- src/components/App.js | 143 ++- src/components/Delegations.js | 7 +- src/components/Governance.js | 222 ++++ src/components/NetworkFinder.js | 36 +- src/components/ProposalDetails.js | 120 ++ src/components/ProposalModal.js | 41 + src/components/ProposalProgress.js | 54 + src/components/Proposals.js | 175 +++ src/components/ValidatorModal.js | 2 +- src/components/Validators.js | 84 +- src/components/VoteForm.js | 150 +++ src/index.js | 2 + src/utils/Network.mjs | 2 +- src/utils/Proposal.mjs | 33 + src/utils/QueryClient.mjs | 31 + src/utils/SigningClient.mjs | 14 +- src/utils/Vote.mjs | 27 + 20 files changed, 1500 insertions(+), 1495 deletions(-) create mode 100644 src/components/Governance.js create mode 100644 src/components/ProposalDetails.js create mode 100644 src/components/ProposalModal.js create mode 100644 src/components/ProposalProgress.js create mode 100644 src/components/Proposals.js create mode 100644 src/components/VoteForm.js create mode 100644 src/utils/Proposal.mjs create mode 100644 src/utils/Vote.mjs diff --git a/package-lock.json b/package-lock.json index e5520e678..7e6cebf9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "react-countdown": "^2.3.2", "react-dom": "^17.0.2", "react-github-btn": "^1.2.1", + "react-moment": "^1.1.2", "react-router-dom": "^6.2.1", "react-select": "^5.2.2", "stream": "^0.0.2", @@ -473,24 +474,24 @@ } }, "node_modules/@cosmjs/amino": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.1.tgz", - "integrity": "sha512-7CihIqU3YOE0dEa1e/dWDMetxmAjYa44P0trmU8cU5TU2JnCBPfjF4hZcTPfoZ4KXNKPQrqAWuLxy0YB6o/5/Q==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.7.tgz", + "integrity": "sha512-NTCUS3+u9bxwW8moC0N1RS4Gk/fs3JPHTKcp7ksH39V4+7uOKM2rGsyFAekiHNxYngnQ+1hU5x2tddS25soK6Q==", "dependencies": { - "@cosmjs/crypto": "0.28.1", - "@cosmjs/encoding": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/utils": "0.28.1" + "@cosmjs/crypto": "0.28.7", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/utils": "0.28.7" } }, "node_modules/@cosmjs/crypto": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.1.tgz", - "integrity": "sha512-QLgP+xvd3X4vNU9PPnEGc1PI5qctgg1o6ANivqHgiJdX2bFolsqCqFQDs1rvGf8GWLJ2eGwXZPX1c/QK0bT9+A==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.7.tgz", + "integrity": "sha512-Fuq90nnqXQb6VvUadGtPy1X6arXY+yhqB95VzN8GM/ZdBLeigu6a3XjOvFki4lKO94QdLn2e9huD8dm4tDQDsA==", "dependencies": { - "@cosmjs/encoding": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/utils": "0.28.1", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/utils": "0.28.7", "@noble/hashes": "^1", "bn.js": "^5.2.0", "elliptic": "^6.5.3", @@ -498,9 +499,9 @@ } }, "node_modules/@cosmjs/encoding": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.1.tgz", - "integrity": "sha512-FqKc+P5rBKq8hW2WHF6L8dmSJhy9mVBDhnJNCLUwyiKBywY9m4BZNTa0mPVQSXISx/c5DPbpJ5SChGL72qNgBw==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.7.tgz", + "integrity": "sha512-m2OuRhxux0YacvtjTocEOHyjnKO/KKGjqXlAY5HXGJpyntr+PIlJFdoS9tHax+1rNRrblZXwYIT+UqOs+kSk5Q==", "dependencies": { "base64-js": "^1.3.0", "bech32": "^1.1.4", @@ -508,87 +509,111 @@ } }, "node_modules/@cosmjs/json-rpc": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.1.tgz", - "integrity": "sha512-yuOxhrE5ldJDcXhSbtdadAB5GBOGZkcDnZe+v70KcRf09qM81XPYiYfoMsxO9rb1QzThLlAbBx/kRebtzZPwDw==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.7.tgz", + "integrity": "sha512-2qgRL/9ih/ZYU/8LmtDQopaCKJBKqsuoSXfb2XO3yv6EkE28yiA8YAwLW5IrXjl1tfSiolHaBWarEASS/8Q5xA==", "dependencies": { - "@cosmjs/stream": "0.28.1", + "@cosmjs/stream": "0.28.7", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/math": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.1.tgz", - "integrity": "sha512-rF5q4BSwNBo0kNBi8asaoHsRx/TchJ/P4IlRjXY8UGCfKCkSRQEID3ffgE8naXf+BDn5x4cSC8da3xy/aCZpAA==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.7.tgz", + "integrity": "sha512-wSXIOOGVzgtRFsGwScpvQ6C6DUx97K7Ez3KyvRALNzspEnIoKPDgTXf438zhZnb+0+ERAwgdkb/6zWaDoVBfUA==", "dependencies": { "bn.js": "^5.2.0" } }, "node_modules/@cosmjs/proto-signing": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.1.tgz", - "integrity": "sha512-dmBVV/b3x2mxwiEEdLFBpagr6R/X74R0AJLCxaKDwK4FpI4vSz84pOe5J6c9ffHHXnWp129M+pRBBE2S9tK0Cg==", - "dependencies": { - "@cosmjs/amino": "0.28.1", - "@cosmjs/crypto": "0.28.1", - "@cosmjs/encoding": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/utils": "0.28.1", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.7.tgz", + "integrity": "sha512-cOwDQVv95KnpHWlkrtxZ+2Q+z2Qp8Co//g+bwNc+YZ403iGHXSK5PRe3YsPa4f3elKJkJpHVi5oyzp4DB8p7mA==", + "dependencies": { + "@cosmjs/amino": "0.28.7", + "@cosmjs/crypto": "0.28.7", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/utils": "0.28.7", "cosmjs-types": "^0.4.0", - "long": "^4.0.0", - "protobufjs": "~6.10.2" + "long": "^4.0.0" } }, "node_modules/@cosmjs/socket": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.1.tgz", - "integrity": "sha512-fFClGMOdBF3Jsti929AXoywsu6TtEKALxmwPvRYEsbrurLy9Ko28qoWdPFUvQw5WEy5rkzDq+3DKK5tbzsbQNA==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.7.tgz", + "integrity": "sha512-+sCR5AzjjsKlA0K7m8YdxldMvgJa3Tqnx0AAyQXlNw2VXmW1zu9DkKZWW475XFhwO9UYfrdIsOe1vbhnUjQb5g==", "dependencies": { - "@cosmjs/stream": "0.28.1", + "@cosmjs/stream": "0.28.7", "isomorphic-ws": "^4.0.1", "ws": "^7", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/stargate": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.1.tgz", - "integrity": "sha512-6mHHI2pkqb/khRTWRATODNieEv3PFhnUKgxkdiw39111oN35TTuSwxpT1c2le/VheoiaSZtBotDgL60wUP9LGA==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.7.tgz", + "integrity": "sha512-3nY7czFUaY74gacbQAZuNji1qJWWQjv2C1cxYNqe7qAZAvCiz6A9adJVUmCJRL4peeG7tKiOny1J5IFbsgRu0g==", "dependencies": { "@confio/ics23": "^0.6.8", - "@cosmjs/amino": "0.28.1", - "@cosmjs/encoding": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/proto-signing": "0.28.1", - "@cosmjs/stream": "0.28.1", - "@cosmjs/tendermint-rpc": "0.28.1", - "@cosmjs/utils": "0.28.1", + "@cosmjs/amino": "0.28.7", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/proto-signing": "0.28.7", + "@cosmjs/stream": "0.28.7", + "@cosmjs/tendermint-rpc": "0.28.7", + "@cosmjs/utils": "0.28.7", "cosmjs-types": "^0.4.0", "long": "^4.0.0", - "protobufjs": "~6.10.2", + "protobufjs": "~6.11.3", "xstream": "^11.14.0" } }, + "node_modules/@cosmjs/stargate/node_modules/protobufjs": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, "node_modules/@cosmjs/stream": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.1.tgz", - "integrity": "sha512-lhgPI+vjpnwUOrLJADAA9YReMwbDCohIhuMhXiyXTnJfnUcitTh882LeRueyuynXW7zT1qjzZk1MgdumbDeXiA==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.7.tgz", + "integrity": "sha512-zTMZadlpmxAMtKUHX/dnYs1STung392+P50WgSUAjllG7CZYl7SAY03AKgc+aoF1ohYQcgH9H7VSgabEgLPhmg==", "dependencies": { "xstream": "^11.14.0" } }, "node_modules/@cosmjs/tendermint-rpc": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.1.tgz", - "integrity": "sha512-2Mt3Hd0Lp8BB6jnkiWr0U5oDRwOl3pueVFkCgv6XIUX8OKY48o97KOQrqW1BDAMYSL7QuXF7ULpvmRgfJqIxeA==", - "dependencies": { - "@cosmjs/crypto": "0.28.1", - "@cosmjs/encoding": "0.28.1", - "@cosmjs/json-rpc": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/socket": "0.28.1", - "@cosmjs/stream": "0.28.1", - "@cosmjs/utils": "0.28.1", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.7.tgz", + "integrity": "sha512-xhIVJL3ol+fZxywP76Ik9pHqCvBdU/BGAw6p48mhla3L3xNcFN2Nf+EnJWcIZPqZl8bHm5QHzPk9VqVFVYCMCw==", + "dependencies": { + "@cosmjs/crypto": "0.28.7", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/json-rpc": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/socket": "0.28.7", + "@cosmjs/stream": "0.28.7", + "@cosmjs/utils": "0.28.7", "axios": "^0.21.2", "readonly-date": "^1.0.0", "xstream": "^11.14.0" @@ -603,42 +628,42 @@ } }, "node_modules/@cosmjs/utils": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.1.tgz", - "integrity": "sha512-PhdsifctdpMUXeWQjbQiHeOCOhWtK/OXdEG3E2PvvYxlmWHNu1faio+u2gZU6PPjL+qgqlAu92sybwsw/TRa+w==" + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.7.tgz", + "integrity": "sha512-0ya5mRaDY956n87dKjx6wfqgH/uEfrZyMswIhcEPqQPegwp4FDd6cwJtSdv/d5S7fgvoEs2UABvUFNzCT5C0hg==" }, "node_modules/@desmoslabs/desmjs": { - "version": "2.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs/-/desmjs-2.0.0-beta.4.tgz", - "integrity": "sha512-Ywrb/fQt8fWKm3Xfcx5n9m1Xf/N+IzSIkvZgZ31v/79D5xX/4zN1cQkZO91eariKEsp70mUeB73vKeoXZs+ejw==", - "dependencies": { - "@cosmjs/amino": "^0.28.1", - "@cosmjs/crypto": "^0.28.1", - "@cosmjs/encoding": "^0.28.1", - "@cosmjs/math": "^0.28.1", - "@cosmjs/proto-signing": "^0.28.1", - "@cosmjs/stargate": "^0.28.1", - "@cosmjs/tendermint-rpc": "^0.28.1", - "@cosmjs/utils": "^0.28.1", - "@desmoslabs/desmjs-types": "2.0.0-beta.4", + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs/-/desmjs-2.0.0-beta.5.tgz", + "integrity": "sha512-K1ltOTVVC3HehcRZJoPcqauhNnBNmVab6WUByjZnM9ufK7jNokkIQjemqo47cqEAaQo4UPvIcd2X7KNXfOqTZg==", + "dependencies": { + "@cosmjs/amino": "^0.28.4", + "@cosmjs/crypto": "^0.28.4", + "@cosmjs/encoding": "^0.28.4", + "@cosmjs/math": "^0.28.4", + "@cosmjs/proto-signing": "^0.28.4", + "@cosmjs/stargate": "^0.28.4", + "@cosmjs/tendermint-rpc": "^0.28.4", + "@cosmjs/utils": "^0.28.4", + "@desmoslabs/desmjs-types": "2.0.0-beta.5", "cosmjs-types": "^0.4.1", "cosmos-wallet": "^1.1.0", "long": "^4.0.0" } }, "node_modules/@desmoslabs/desmjs-types": { - "version": "2.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs-types/-/desmjs-types-2.0.0-beta.4.tgz", - "integrity": "sha512-gBdVuUmyqAcirxdKzVXHtmQBS3QfzHglNYTeN0zgT/lH9AFnrpOMrbB8Z0hKiyUzkeJusTZo7pkX92gQ7n0vUA==", + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs-types/-/desmjs-types-2.0.0-beta.5.tgz", + "integrity": "sha512-ZsZBDIomOn8bceVjT4Sw0iDGgec4kKHTfLZLfrmhFqSaNQCzHQMbnzlmUpMWRuZ55nQMCTn5DMLzZaDlWW16GA==", "dependencies": { "long": "^4.0.0", "protobufjs": "^6.11.2" } }, "node_modules/@desmoslabs/desmjs-types/node_modules/protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -2192,61 +2217,6 @@ "postcss": "^8.2.15" } }, - "node_modules/@parcel/optimizer-cssnano/node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/@parcel/optimizer-cssnano/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/@parcel/optimizer-cssnano/node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/@parcel/optimizer-cssnano/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, "node_modules/@parcel/optimizer-cssnano/node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -2928,82 +2898,6 @@ "postcss": "^8.2.15" } }, - "node_modules/@parcel/optimizer-htmlnano/node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/@parcel/optimizer-htmlnano/node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@parcel/optimizer-htmlnano/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/@parcel/optimizer-htmlnano/node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/@parcel/optimizer-htmlnano/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/@parcel/optimizer-htmlnano/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/@parcel/optimizer-htmlnano/node_modules/htmlnano": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.0.tgz", @@ -3051,25 +2945,6 @@ } } }, - "node_modules/@parcel/optimizer-htmlnano/node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "node_modules/@parcel/optimizer-htmlnano/node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -3870,61 +3745,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/@parcel/optimizer-svgo/node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/@parcel/optimizer-svgo/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/@parcel/optimizer-svgo/node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/@parcel/optimizer-svgo/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, "node_modules/@parcel/optimizer-svgo/node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -4159,136 +3979,41 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/packager-html/node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "node_modules/@parcel/packager-html/node_modules/posthtml": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", + "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", "dev": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/@parcel/packager-html/node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "engines": { + "node": ">=12.0.0" } }, - "node_modules/@parcel/packager-html/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/@parcel/packager-html/node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "node_modules/@parcel/packager-html/node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", "dev": true, "dependencies": { - "domelementtype": "^2.2.0" + "htmlparser2": "^7.1.1" }, "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "node": ">=12" } }, - "node_modules/@parcel/packager-html/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "node_modules/@parcel/packager-html/node_modules/posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", "dev": true, "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "is-json": "^2.0.1" }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/@parcel/packager-html/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@parcel/packager-html/node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, - "node_modules/@parcel/packager-html/node_modules/posthtml": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", - "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", - "dev": true, - "dependencies": { - "posthtml-parser": "^0.11.0", - "posthtml-render": "^3.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@parcel/packager-html/node_modules/posthtml-parser": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", - "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", - "dev": true, - "dependencies": { - "htmlparser2": "^7.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@parcel/packager-html/node_modules/posthtml-render": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", - "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", - "dev": true, - "dependencies": { - "is-json": "^2.0.1" - }, - "engines": { - "node": ">=12" + "engines": { + "node": ">=12" } }, "node_modules/@parcel/packager-js": { @@ -4384,101 +4109,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/packager-svg/node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/@parcel/packager-svg/node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@parcel/packager-svg/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/@parcel/packager-svg/node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/@parcel/packager-svg/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/@parcel/packager-svg/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@parcel/packager-svg/node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "node_modules/@parcel/packager-svg/node_modules/posthtml": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", @@ -4841,101 +4471,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/transformer-html/node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/@parcel/transformer-html/node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@parcel/transformer-html/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/@parcel/transformer-html/node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/@parcel/transformer-html/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/@parcel/transformer-html/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@parcel/transformer-html/node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "node_modules/@parcel/transformer-html/node_modules/posthtml": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", @@ -5105,139 +4640,44 @@ "clone": "^2.1.1", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.3.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-postcss/node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/@parcel/transformer-posthtml": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.3.2.tgz", - "integrity": "sha512-tMdVExfdM+1G8A9KSHDsjg+S9xEGbhH5mApF2NslPnNZ4ciLKRNuHU2sSV/v8i0a6kacKvDTrwQXYBQJGOodBw==", - "dev": true, - "dependencies": { - "@parcel/plugin": "2.3.2", - "@parcel/utils": "2.3.2", - "nullthrows": "^1.1.1", - "posthtml": "^0.16.5", - "posthtml-parser": "^0.10.1", - "posthtml-render": "^3.0.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 12.0.0", - "parcel": "^2.3.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/transformer-posthtml/node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/@parcel/transformer-posthtml/node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@parcel/transformer-posthtml/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/@parcel/transformer-posthtml/node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/@parcel/transformer-posthtml/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/@parcel/transformer-posthtml/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, + "semver": "^5.7.1" + }, "engines": { - "node": ">=0.12" + "node": ">= 12.0.0", + "parcel": "^2.3.2" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/transformer-posthtml/node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "node_modules/@parcel/transformer-postcss/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/@parcel/transformer-posthtml": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.3.2.tgz", + "integrity": "sha512-tMdVExfdM+1G8A9KSHDsjg+S9xEGbhH5mApF2NslPnNZ4ciLKRNuHU2sSV/v8i0a6kacKvDTrwQXYBQJGOodBw==", "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" + "@parcel/plugin": "2.3.2", + "@parcel/utils": "2.3.2", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.10.1", + "posthtml-render": "^3.0.0", + "semver": "^5.7.1" + }, + "engines": { + "node": ">= 12.0.0", + "parcel": "^2.3.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/@parcel/transformer-posthtml/node_modules/posthtml": { @@ -5349,101 +4789,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/transformer-svg/node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/@parcel/transformer-svg/node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@parcel/transformer-svg/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/@parcel/transformer-svg/node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/@parcel/transformer-svg/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/@parcel/transformer-svg/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@parcel/transformer-svg/node_modules/htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "node_modules/@parcel/transformer-svg/node_modules/posthtml": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", @@ -6794,9 +6139,9 @@ } }, "node_modules/cosmjs-types/node_modules/protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -6819,9 +6164,9 @@ } }, "node_modules/cosmos-wallet": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cosmos-wallet/-/cosmos-wallet-1.1.0.tgz", - "integrity": "sha512-jm5MFRYzvOCA/mhULObpaoE2Na0shBn3lrkv2GDedmRZfuoDvOa63WID4qMoIycteefjRKdxXf+26vDrgd79aQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cosmos-wallet/-/cosmos-wallet-1.2.0.tgz", + "integrity": "sha512-lMEpNhjN6FHU6c8l/lYi1hWU/74bOlTmo3pz0mwVpCHjNSe5u7sZCO7j0dndd3oV0tM8tj/u3eJa4NgZxG9a0Q==", "dependencies": { "@cosmjs/amino": "^0.25.4", "@cosmjs/proto-signing": "^0.25.4" @@ -7267,6 +6612,41 @@ "csstype": "^3.0.2" } }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, "node_modules/domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -7278,6 +6658,35 @@ "webidl-conversions": "^4.0.2" } }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dotenv": { "version": "16.0.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", @@ -7342,10 +6751,13 @@ } }, "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", "dev": true, + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -8126,6 +7538,25 @@ "node": ">=0.10.0" } }, + "node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -9498,9 +8929,9 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/protobufjs": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", - "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.3.tgz", + "integrity": "sha512-yvAslS0hNdBhlSKckI4R1l7wunVilX66uvrjzE4MimiAt7/qw1nLpMhZrn/ObuUTM/c3Xnfl01LYMdcSJe6dwg==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -9698,6 +9129,16 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "node_modules/react-moment": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/react-moment/-/react-moment-1.1.2.tgz", + "integrity": "sha512-lfb+shYXI2tXlQrNUpNr05/1D/kzFj8Isbfp89DQrpZk0fs2JIAnLHWETR0hQS9zvtzwLWlVv0wKLffbue5HoA==", + "peerDependencies": { + "moment": "^2.29.0", + "prop-types": "^15.7.0", + "react": "^16.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-refresh": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", @@ -10703,9 +10144,9 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz", + "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==", "engines": { "node": ">=8.3.0" }, @@ -11093,24 +10534,24 @@ } }, "@cosmjs/amino": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.1.tgz", - "integrity": "sha512-7CihIqU3YOE0dEa1e/dWDMetxmAjYa44P0trmU8cU5TU2JnCBPfjF4hZcTPfoZ4KXNKPQrqAWuLxy0YB6o/5/Q==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.7.tgz", + "integrity": "sha512-NTCUS3+u9bxwW8moC0N1RS4Gk/fs3JPHTKcp7ksH39V4+7uOKM2rGsyFAekiHNxYngnQ+1hU5x2tddS25soK6Q==", "requires": { - "@cosmjs/crypto": "0.28.1", - "@cosmjs/encoding": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/utils": "0.28.1" + "@cosmjs/crypto": "0.28.7", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/utils": "0.28.7" } }, "@cosmjs/crypto": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.1.tgz", - "integrity": "sha512-QLgP+xvd3X4vNU9PPnEGc1PI5qctgg1o6ANivqHgiJdX2bFolsqCqFQDs1rvGf8GWLJ2eGwXZPX1c/QK0bT9+A==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.7.tgz", + "integrity": "sha512-Fuq90nnqXQb6VvUadGtPy1X6arXY+yhqB95VzN8GM/ZdBLeigu6a3XjOvFki4lKO94QdLn2e9huD8dm4tDQDsA==", "requires": { - "@cosmjs/encoding": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/utils": "0.28.1", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/utils": "0.28.7", "@noble/hashes": "^1", "bn.js": "^5.2.0", "elliptic": "^6.5.3", @@ -11118,9 +10559,9 @@ } }, "@cosmjs/encoding": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.1.tgz", - "integrity": "sha512-FqKc+P5rBKq8hW2WHF6L8dmSJhy9mVBDhnJNCLUwyiKBywY9m4BZNTa0mPVQSXISx/c5DPbpJ5SChGL72qNgBw==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.7.tgz", + "integrity": "sha512-m2OuRhxux0YacvtjTocEOHyjnKO/KKGjqXlAY5HXGJpyntr+PIlJFdoS9tHax+1rNRrblZXwYIT+UqOs+kSk5Q==", "requires": { "base64-js": "^1.3.0", "bech32": "^1.1.4", @@ -11128,87 +10569,108 @@ } }, "@cosmjs/json-rpc": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.1.tgz", - "integrity": "sha512-yuOxhrE5ldJDcXhSbtdadAB5GBOGZkcDnZe+v70KcRf09qM81XPYiYfoMsxO9rb1QzThLlAbBx/kRebtzZPwDw==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.7.tgz", + "integrity": "sha512-2qgRL/9ih/ZYU/8LmtDQopaCKJBKqsuoSXfb2XO3yv6EkE28yiA8YAwLW5IrXjl1tfSiolHaBWarEASS/8Q5xA==", "requires": { - "@cosmjs/stream": "0.28.1", + "@cosmjs/stream": "0.28.7", "xstream": "^11.14.0" } }, "@cosmjs/math": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.1.tgz", - "integrity": "sha512-rF5q4BSwNBo0kNBi8asaoHsRx/TchJ/P4IlRjXY8UGCfKCkSRQEID3ffgE8naXf+BDn5x4cSC8da3xy/aCZpAA==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.7.tgz", + "integrity": "sha512-wSXIOOGVzgtRFsGwScpvQ6C6DUx97K7Ez3KyvRALNzspEnIoKPDgTXf438zhZnb+0+ERAwgdkb/6zWaDoVBfUA==", "requires": { "bn.js": "^5.2.0" } }, "@cosmjs/proto-signing": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.1.tgz", - "integrity": "sha512-dmBVV/b3x2mxwiEEdLFBpagr6R/X74R0AJLCxaKDwK4FpI4vSz84pOe5J6c9ffHHXnWp129M+pRBBE2S9tK0Cg==", - "requires": { - "@cosmjs/amino": "0.28.1", - "@cosmjs/crypto": "0.28.1", - "@cosmjs/encoding": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/utils": "0.28.1", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.7.tgz", + "integrity": "sha512-cOwDQVv95KnpHWlkrtxZ+2Q+z2Qp8Co//g+bwNc+YZ403iGHXSK5PRe3YsPa4f3elKJkJpHVi5oyzp4DB8p7mA==", + "requires": { + "@cosmjs/amino": "0.28.7", + "@cosmjs/crypto": "0.28.7", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/utils": "0.28.7", "cosmjs-types": "^0.4.0", - "long": "^4.0.0", - "protobufjs": "~6.10.2" + "long": "^4.0.0" } }, "@cosmjs/socket": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.1.tgz", - "integrity": "sha512-fFClGMOdBF3Jsti929AXoywsu6TtEKALxmwPvRYEsbrurLy9Ko28qoWdPFUvQw5WEy5rkzDq+3DKK5tbzsbQNA==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.7.tgz", + "integrity": "sha512-+sCR5AzjjsKlA0K7m8YdxldMvgJa3Tqnx0AAyQXlNw2VXmW1zu9DkKZWW475XFhwO9UYfrdIsOe1vbhnUjQb5g==", "requires": { - "@cosmjs/stream": "0.28.1", + "@cosmjs/stream": "0.28.7", "isomorphic-ws": "^4.0.1", "ws": "^7", "xstream": "^11.14.0" } }, "@cosmjs/stargate": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.1.tgz", - "integrity": "sha512-6mHHI2pkqb/khRTWRATODNieEv3PFhnUKgxkdiw39111oN35TTuSwxpT1c2le/VheoiaSZtBotDgL60wUP9LGA==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.7.tgz", + "integrity": "sha512-3nY7czFUaY74gacbQAZuNji1qJWWQjv2C1cxYNqe7qAZAvCiz6A9adJVUmCJRL4peeG7tKiOny1J5IFbsgRu0g==", "requires": { "@confio/ics23": "^0.6.8", - "@cosmjs/amino": "0.28.1", - "@cosmjs/encoding": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/proto-signing": "0.28.1", - "@cosmjs/stream": "0.28.1", - "@cosmjs/tendermint-rpc": "0.28.1", - "@cosmjs/utils": "0.28.1", + "@cosmjs/amino": "0.28.7", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/proto-signing": "0.28.7", + "@cosmjs/stream": "0.28.7", + "@cosmjs/tendermint-rpc": "0.28.7", + "@cosmjs/utils": "0.28.7", "cosmjs-types": "^0.4.0", "long": "^4.0.0", - "protobufjs": "~6.10.2", + "protobufjs": "~6.11.3", "xstream": "^11.14.0" + }, + "dependencies": { + "protobufjs": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + } + } } }, "@cosmjs/stream": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.1.tgz", - "integrity": "sha512-lhgPI+vjpnwUOrLJADAA9YReMwbDCohIhuMhXiyXTnJfnUcitTh882LeRueyuynXW7zT1qjzZk1MgdumbDeXiA==", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.7.tgz", + "integrity": "sha512-zTMZadlpmxAMtKUHX/dnYs1STung392+P50WgSUAjllG7CZYl7SAY03AKgc+aoF1ohYQcgH9H7VSgabEgLPhmg==", "requires": { "xstream": "^11.14.0" } }, "@cosmjs/tendermint-rpc": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.1.tgz", - "integrity": "sha512-2Mt3Hd0Lp8BB6jnkiWr0U5oDRwOl3pueVFkCgv6XIUX8OKY48o97KOQrqW1BDAMYSL7QuXF7ULpvmRgfJqIxeA==", - "requires": { - "@cosmjs/crypto": "0.28.1", - "@cosmjs/encoding": "0.28.1", - "@cosmjs/json-rpc": "0.28.1", - "@cosmjs/math": "0.28.1", - "@cosmjs/socket": "0.28.1", - "@cosmjs/stream": "0.28.1", - "@cosmjs/utils": "0.28.1", + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.7.tgz", + "integrity": "sha512-xhIVJL3ol+fZxywP76Ik9pHqCvBdU/BGAw6p48mhla3L3xNcFN2Nf+EnJWcIZPqZl8bHm5QHzPk9VqVFVYCMCw==", + "requires": { + "@cosmjs/crypto": "0.28.7", + "@cosmjs/encoding": "0.28.7", + "@cosmjs/json-rpc": "0.28.7", + "@cosmjs/math": "0.28.7", + "@cosmjs/socket": "0.28.7", + "@cosmjs/stream": "0.28.7", + "@cosmjs/utils": "0.28.7", "axios": "^0.21.2", "readonly-date": "^1.0.0", "xstream": "^11.14.0" @@ -11225,42 +10687,42 @@ } }, "@cosmjs/utils": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.1.tgz", - "integrity": "sha512-PhdsifctdpMUXeWQjbQiHeOCOhWtK/OXdEG3E2PvvYxlmWHNu1faio+u2gZU6PPjL+qgqlAu92sybwsw/TRa+w==" + "version": "0.28.7", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.7.tgz", + "integrity": "sha512-0ya5mRaDY956n87dKjx6wfqgH/uEfrZyMswIhcEPqQPegwp4FDd6cwJtSdv/d5S7fgvoEs2UABvUFNzCT5C0hg==" }, "@desmoslabs/desmjs": { - "version": "2.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs/-/desmjs-2.0.0-beta.4.tgz", - "integrity": "sha512-Ywrb/fQt8fWKm3Xfcx5n9m1Xf/N+IzSIkvZgZ31v/79D5xX/4zN1cQkZO91eariKEsp70mUeB73vKeoXZs+ejw==", - "requires": { - "@cosmjs/amino": "^0.28.1", - "@cosmjs/crypto": "^0.28.1", - "@cosmjs/encoding": "^0.28.1", - "@cosmjs/math": "^0.28.1", - "@cosmjs/proto-signing": "^0.28.1", - "@cosmjs/stargate": "^0.28.1", - "@cosmjs/tendermint-rpc": "^0.28.1", - "@cosmjs/utils": "^0.28.1", - "@desmoslabs/desmjs-types": "2.0.0-beta.4", + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs/-/desmjs-2.0.0-beta.5.tgz", + "integrity": "sha512-K1ltOTVVC3HehcRZJoPcqauhNnBNmVab6WUByjZnM9ufK7jNokkIQjemqo47cqEAaQo4UPvIcd2X7KNXfOqTZg==", + "requires": { + "@cosmjs/amino": "^0.28.4", + "@cosmjs/crypto": "^0.28.4", + "@cosmjs/encoding": "^0.28.4", + "@cosmjs/math": "^0.28.4", + "@cosmjs/proto-signing": "^0.28.4", + "@cosmjs/stargate": "^0.28.4", + "@cosmjs/tendermint-rpc": "^0.28.4", + "@cosmjs/utils": "^0.28.4", + "@desmoslabs/desmjs-types": "2.0.0-beta.5", "cosmjs-types": "^0.4.1", "cosmos-wallet": "^1.1.0", "long": "^4.0.0" } }, "@desmoslabs/desmjs-types": { - "version": "2.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs-types/-/desmjs-types-2.0.0-beta.4.tgz", - "integrity": "sha512-gBdVuUmyqAcirxdKzVXHtmQBS3QfzHglNYTeN0zgT/lH9AFnrpOMrbB8Z0hKiyUzkeJusTZo7pkX92gQ7n0vUA==", + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@desmoslabs/desmjs-types/-/desmjs-types-2.0.0-beta.5.tgz", + "integrity": "sha512-ZsZBDIomOn8bceVjT4Sw0iDGgec4kKHTfLZLfrmhFqSaNQCzHQMbnzlmUpMWRuZ55nQMCTn5DMLzZaDlWW16GA==", "requires": { "long": "^4.0.0", "protobufjs": "^6.11.2" }, "dependencies": { "protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -12263,48 +11725,11 @@ } }, "cssnano-utils": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.2.tgz", - "integrity": "sha512-KhprijuQv2sP4kT92sSQwhlK3SJTbDIsxcfIEySB0O+3m9esFOai7dP9bMx5enHAh2MwarVIcnwiWoOm01RIbQ==", - "dev": true, - "requires": {} - }, - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.2.tgz", + "integrity": "sha512-KhprijuQv2sP4kT92sSQwhlK3SJTbDIsxcfIEySB0O+3m9esFOai7dP9bMx5enHAh2MwarVIcnwiWoOm01RIbQ==", "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } + "requires": {} }, "mdn-data": { "version": "2.0.14", @@ -12754,57 +12179,6 @@ "peer": true, "requires": {} }, - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true - }, "htmlnano": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.0.tgz", @@ -12816,18 +12190,6 @@ "timsort": "^0.3.0" } }, - "htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -13379,43 +12741,6 @@ "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", "dev": true }, - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -13573,69 +12898,6 @@ "posthtml": "^0.16.5" }, "dependencies": { - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true - }, - "htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "posthtml": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", @@ -13723,69 +12985,6 @@ "posthtml": "^0.16.4" }, "dependencies": { - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true - }, - "htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "posthtml": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", @@ -14027,69 +13226,6 @@ "semver": "^5.7.1" }, "dependencies": { - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true - }, - "htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "posthtml": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", @@ -14241,69 +13377,6 @@ "semver": "^5.7.1" }, "dependencies": { - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true - }, - "htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "posthtml": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", @@ -14381,69 +13454,6 @@ "semver": "^5.7.1" }, "dependencies": { - "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "dependencies": { - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true - }, - "htmlparser2": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, "posthtml": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", @@ -15527,9 +14537,9 @@ }, "dependencies": { "protobufjs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz", - "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -15549,9 +14559,9 @@ } }, "cosmos-wallet": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cosmos-wallet/-/cosmos-wallet-1.1.0.tgz", - "integrity": "sha512-jm5MFRYzvOCA/mhULObpaoE2Na0shBn3lrkv2GDedmRZfuoDvOa63WID4qMoIycteefjRKdxXf+26vDrgd79aQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cosmos-wallet/-/cosmos-wallet-1.2.0.tgz", + "integrity": "sha512-lMEpNhjN6FHU6c8l/lYi1hWU/74bOlTmo3pz0mwVpCHjNSe5u7sZCO7j0dndd3oV0tM8tj/u3eJa4NgZxG9a0Q==", "requires": { "@cosmjs/amino": "^0.25.4", "@cosmjs/proto-signing": "^0.25.4" @@ -15936,6 +14946,31 @@ "csstype": "^3.0.2" } }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "dependencies": { + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + } + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -15947,6 +14982,26 @@ "webidl-conversions": "^4.0.2" } }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, "dotenv": { "version": "16.0.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", @@ -16010,9 +15065,9 @@ } }, "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", "dev": true }, "error-ex": { @@ -16603,6 +15658,18 @@ "optional": true, "peer": true }, + "htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -17687,9 +16754,9 @@ } }, "protobufjs": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", - "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.3.tgz", + "integrity": "sha512-yvAslS0hNdBhlSKckI4R1l7wunVilX66uvrjzE4MimiAt7/qw1nLpMhZrn/ObuUTM/c3Xnfl01LYMdcSJe6dwg==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -17863,6 +16930,12 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "react-moment": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/react-moment/-/react-moment-1.1.2.tgz", + "integrity": "sha512-lfb+shYXI2tXlQrNUpNr05/1D/kzFj8Isbfp89DQrpZk0fs2JIAnLHWETR0hQS9zvtzwLWlVv0wKLffbue5HoA==", + "requires": {} + }, "react-refresh": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", @@ -18664,9 +17737,9 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz", + "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==", "requires": {} }, "xml-name-validator": { diff --git a/package.json b/package.json index c6ba9d01c..ac677a86f 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "react-countdown": "^2.3.2", "react-dom": "^17.0.2", "react-github-btn": "^1.2.1", + "react-moment": "^1.1.2", "react-router-dom": "^6.2.1", "react-select": "^5.2.2", "stream": "^0.0.2", diff --git a/src/components/AlertMessage.js b/src/components/AlertMessage.js index 149e8b2c7..fe7a03a71 100644 --- a/src/components/AlertMessage.js +++ b/src/components/AlertMessage.js @@ -16,7 +16,7 @@ function AlertMessage(props) { return ( <> {show && - setShow(false)} dismissible={dismissible}> + setShow(false)} dismissible={dismissible}> {props.message || props.children} } diff --git a/src/components/App.js b/src/components/App.js index 2b467bbb6..c698f7dd8 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -15,7 +15,9 @@ import { Button, Badge, Dropdown, - ButtonGroup + ButtonGroup, + Navbar, + Nav } from 'react-bootstrap'; import { Droplet, @@ -36,12 +38,14 @@ import LogoWhite3x from '../assets/logo-white@3x.png' import PoweredByAkash from '../assets/powered-by-akash.svg' import PoweredByAkashWhite from '../assets/powered-by-akash-white.svg' import TooltipIcon from './TooltipIcon'; +import Governance from './Governance'; class App extends React.Component { constructor(props) { super(props); this.state = {} this.connect = this.connect.bind(this); + this.disconnect = this.disconnect.bind(this); this.showNetworkSelect = this.showNetworkSelect.bind(this); this.getBalance = this.getBalance.bind(this); } @@ -82,7 +86,17 @@ class App extends React.Component { } } - async connect() { + disconnect(){ + localStorage.removeItem('connected') + this.setState({ + address: null, + balance: null, + queryClient: null, + stargateClient: null + }) + } + + async connect(manual) { if (!this.connected()) { return this.setState({ error: 'Could not connect to any available API servers' @@ -90,6 +104,15 @@ class App extends React.Component { } const { network } = this.props const chainId = network.chainId + + if(localStorage.getItem('connected') !== '1'){ + if(manual){ + localStorage.setItem('connected', '1') + }else{ + return + } + } + try { if (window.keplr) { if (network.gasPricePrefer) { @@ -116,6 +139,7 @@ class App extends React.Component { stargateClient.registry.register("/cosmos.authz.v1beta1.MsgRevoke", MsgRevoke) this.setState({ address: address, + accountName: key.name, stargateClient: stargateClient, queryClient: network.queryClient, error: false @@ -131,13 +155,6 @@ class App extends React.Component { } } - async disconnect() { - this.setState({ - address: null, - stargateClient: null - }) - } - suggestChain(network) { if (!window.keplr) return const currency = { @@ -232,49 +249,63 @@ class App extends React.Component {
    - + @@ -284,6 +315,11 @@ class App extends React.Component {

    REStake allows validators to this.setState({ showAbout: true })} className="text-decoration-underline" role="button">auto-compound your {this.props.network.prettyName} staking rewards for you

    + {this.props.network.experimental && ( + + This network was added to REStake automatically and has not been thoroughly tested yet. Raise an issue if you have any problems. + + )} {!this.state.address && ( !this.state.keplr @@ -293,13 +329,20 @@ class App extends React.Component { ) : (
    -
    ) )} - {this.state.address && + {this.props.active === 'governance' && this.state.address && ( + + )} + {this.props.active === 'delegations' && this.state.address && <> {this.props.network?.usingDirectory && ( - Interchain data from cosmos.directory + Interchain APIs from cosmos.directory )}
    diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 1a93da667..205e53ad6 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -114,7 +114,7 @@ class Delegations extends React.Component { } async getDelegations(hideError) { - this.props.queryClient.getDelegations(this.props.address) + return this.props.queryClient.getDelegations(this.props.address) .then( (delegations) => { const orderedAddresses = Object.keys(this.props.validators) @@ -448,11 +448,6 @@ class Delegations extends React.Component { {this.props.network.prettyName} doesn't support Authz just yet. You can stake and compound manually for now and REStake will update automatically when support is added.
    )} - {this.props.network.experimental && ( - - This network was added to REStake automatically and has not been thoroughly tested yet. Raise an issue if you have any problems. - - )} {this.authzSupport() && this.props.operators.length > 0 && this.state.isNanoLedger && ( diff --git a/src/components/Governance.js b/src/components/Governance.js new file mode 100644 index 000000000..ad81b2bfe --- /dev/null +++ b/src/components/Governance.js @@ -0,0 +1,222 @@ +import React, { useState, useEffect, useReducer } from 'react'; +import _ from 'lodash' +import FuzzySearch from 'fuzzy-search' +import { Bech32 } from '@cosmjs/encoding' + +import { add } from 'mathjs' + +import Coins from "./Coins"; +import ClaimRewards from "./ClaimRewards"; +import RevokeRestake from "./RevokeRestake"; +import ValidatorImage from './ValidatorImage' +import TooltipIcon from './TooltipIcon' + +import { + Table, + Button, + Spinner, + Dropdown, + OverlayTrigger, + Tooltip, + Nav +} from 'react-bootstrap' +import { FilterSquare, XCircle } from "react-bootstrap-icons"; +import { useParams, useNavigate } from "react-router-dom"; + +import ValidatorName from "./ValidatorName"; +import ManageRestake from "./ManageRestake"; +import AlertMessage from './AlertMessage'; +import Proposals from './Proposals'; +import { executeSync, mapSync } from '../utils/Helpers.mjs'; +import ProposalModal from './ProposalModal'; +import Proposal from '../utils/Proposal.mjs'; +import Vote from '../utils/Vote.mjs'; + +function Governance(props) { + const { address, network } = props + const [showModal, setShowModal] = useState() + const [proposal, setProposal] = useState() + const [proposals, setProposals] = useState() + const [tallies, setTallies] = useReducer( + (tallies, newTallies) => ({...tallies, ...newTallies}), + {} + ) + const [votes, setVotes] = useReducer( + (votes, newVotes) => ({...votes, ...newVotes}), + {} + ) + const [error, setError] = useState() + const navigate = useNavigate(); + const params = useParams(); + + useEffect(() => { + setProposals() + setTallies({}) + setVotes({}) + + getProposals() + }, [props.queryClient]); + + useEffect(() => { + const interval = setInterval(() => { + getProposals() + }, 120_000); + return () => clearInterval(interval); + }, []); + + useEffect(() => { + if(!proposals || !params.proposalId) return + const prop = proposals.find(el => el.proposal_id === params.proposalId) + if(prop){ + showProposal(prop) + }else if(proposal){ + setProposal(null) + closeProposal() + } + }, [proposals, params.proposalId]) + + useEffect(() => { + if(!proposals) return + + getTallies(proposals) + }, [proposals]); + + useEffect(() => { + if(!proposals || !address) return + + getVotes(proposals) + }, [proposals, address]); + + async function getProposals(hideError) { + if(!props.queryClient) return + + props.queryClient.getProposals().then(async (proposals) => { + proposals = proposals.map(el => Proposal(el)) + setProposals(sortProposals(proposals)) + setTallies(proposals.reduce((sum, proposal) => { + if (!_.every(Object.values(proposal.final_tally_result), el => el === '0')) { + sum[proposal.proposal_id] = proposal.final_tally_result + } + return sum + }, {})) + }, + (error) => { + if(!proposals) setProposals([]) + if (!hideError) { + this.setState({ + error: `Failed to load proposals: ${error.message}`, + }); + } + } + ) + } + + async function getTallies(proposals) { + const calls = proposals.map((proposal) => { + return () => { + const { proposal_id, final_tally_result: result } = proposal + if (tallies[proposal_id]) return + + if (_.every(Object.values(result), el => el === '0')) { + return props.queryClient.getProposalTally(proposal_id).then(result => { + return setTallies({ [proposal_id]: result.tally }) + }).catch(error => { }) + } else { + return setTallies({ [proposal_id]: result }) + } + } + }); + + await executeSync(calls, 2) + }; + + async function getVotes(proposals) { + const calls = proposals.filter(el => el.status === 'PROPOSAL_STATUS_VOTING_PERIOD').map((proposal) => { + return () => { + const { proposal_id } = proposal + if (votes[proposal_id]) return + + return props.queryClient.getProposalVote(proposal_id, address).then(result => { + return setVotes({ [proposal_id]: Vote(result.vote) }) + }).catch(error => { }) + } + }); + + await executeSync(calls, 2) + }; + + function showProposal(proposal){ + setProposal(proposal) + setShowModal(true) + if (proposal.proposal_id !== params.proposalId) { + navigate(`/${network.name}/govern/${proposal.proposal_id}`) + } + } + + function closeProposal(){ + setShowModal(false) + navigate(`/${network.name}/govern`) + } + + function onVote(proposal, vote){ + setError(null) + setVotes({ + [proposal.proposal_id]: vote + }) + closeProposal() + } + + function sortProposals(proposals){ + return _.sortBy(proposals, ({ proposal_id }) => { + return 0 - parseInt(proposal_id) + }); + } + + if (!proposals) { + return ( +
    + + Loading... + +
    + ); + } + + const alerts = ( + <> + + + ); + + return ( + <> + {alerts} +
    + +
    + + + ); +} + +export default Governance; \ No newline at end of file diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 71d7fc4c6..100a7cfa1 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -1,6 +1,6 @@ import _ from 'lodash' import React, { useEffect, useState, useReducer } from 'react'; -import { useParams, useNavigate } from "react-router-dom"; +import { useParams, useNavigate, useMatch } from "react-router-dom"; import Network from '../utils/Network.mjs' import CosmosDirectory from '../utils/CosmosDirectory.mjs' import App from './App'; @@ -18,6 +18,7 @@ const DARK_THEME = 'superhero' function NetworkFinder() { const params = useParams(); const navigate = useNavigate() + const govMatch = useMatch("/:network/govern/*"); const directory = CosmosDirectory() @@ -60,7 +61,21 @@ function NetworkFinder() { operators: network.getOperators() }) - navigate("/" + network.name); + if(govMatch){ + navigate("/" + network.name + '/govern'); + }else{ + navigate("/" + network.name); + } + } + + const setActive = (active) => { + if(!state.network) return + if(active === 'governance'){ + navigate("/" + state.network.name + '/govern'); + }else{ + navigate("/" + state.network.name); + } + setState({active}) } useEffect(() => { @@ -142,7 +157,7 @@ function NetworkFinder() { useEffect(() => { if(state.error) return if(!state.network || !state.network.connected) return - if(state.network && (!Object.keys(state.validators).length)){ + if(state.network && !Object.keys(state.validators).length){ setState({ validators: state.network.getValidators(), operators: state.network.getOperators(), @@ -152,8 +167,15 @@ function NetworkFinder() { }, [state.network]) useEffect(() => { - const validatorAddresses = state.validators && Object.keys(state.validators) - if(validatorAddresses && validatorAddresses.includes(params.validator)){ + if(govMatch){ + setState({active: 'governance'}) + }else{ + setState({active: 'delegations'}) + } + }, [govMatch]) + + useEffect(() => { + if(params.validator && state.validators[params.validator]){ setState({ validator: state.validators[params.validator] }) }else if(state.validator){ setState({ validator: null }) @@ -174,9 +196,9 @@ function NetworkFinder() { ) } - return changeNetwork(network, validators)} + changeNetwork={(network, validators) => changeNetwork(network, validators)} setActive={setActive} theme={theme} themeChoice={themeChoice} themeDefault={themeDefault} setThemeChoice={setThemeChoice} />; } diff --git a/src/components/ProposalDetails.js b/src/components/ProposalDetails.js new file mode 100644 index 000000000..f0bc28f8e --- /dev/null +++ b/src/components/ProposalDetails.js @@ -0,0 +1,120 @@ +import React, { useState, useEffect, useReducer } from 'react'; +import Moment from 'react-moment'; + +import { + Table, +} from 'react-bootstrap' + +import { + XCircle +} from 'react-bootstrap-icons' +import Coins from './Coins'; +import ProposalProgress from './ProposalProgress'; +import VoteForm from './VoteForm'; +import AlertMessage from './AlertMessage'; + +function ProposalDetails(props) { + const { proposal, tally, vote, network } = props + const [error, setError] = useState() + + const { title, description } = proposal.content + + const fixDescription = description.split(/\\n/).join('\n') + + return ( + <> + {error && + + {error} + + } +
    +
    + + + + + + + + + + + + + + + {!proposal.isDeposit && ( + <> + + + + + + + + + + )} + {proposal.isDeposit && ( + <> + + + + + + + + + + )} + +
    ID#{proposal.proposal_id}
    Status{proposal.statusHuman}
    Type{proposal.typeHuman}
    Submit time + + {proposal.submit_time} + +
    Voting end time + + {proposal.voting_end_time} + +
    Deposit end time + + {proposal.deposit_end_time} + +
    Total deposit + {proposal.total_deposit.map(coins => { + return + })} +
    +
    +
    +

    Your Vote

    + +
    +
    +
    + +
    +
    +
    +
    {title}
    +

    + {fixDescription} +

    +
    +
    + + ) +} + +export default ProposalDetails \ No newline at end of file diff --git a/src/components/ProposalModal.js b/src/components/ProposalModal.js new file mode 100644 index 000000000..16386fd6b --- /dev/null +++ b/src/components/ProposalModal.js @@ -0,0 +1,41 @@ +import React, { useEffect } from 'react'; + +import { + Modal, +} from 'react-bootstrap' + +import ProposalDetails from './ProposalDetails'; + +function ProposalModal(props) { + const { show, proposal, tally, vote, network, address } = props + + const handleClose = () => { + props.closeProposal() + } + + if(!proposal) return null + + return ( + <> + + + + {proposal.content.title} + + + + + + + + ); +} + +export default ProposalModal diff --git a/src/components/ProposalProgress.js b/src/components/ProposalProgress.js new file mode 100644 index 000000000..a7c7eca31 --- /dev/null +++ b/src/components/ProposalProgress.js @@ -0,0 +1,54 @@ +import React from 'react'; +import _ from 'lodash' +import { add, divide, format } from 'mathjs' + +import { add, divide, multiply } from 'mathjs' + +import { + OverlayTrigger, + Tooltip, + ProgressBar +} from 'react-bootstrap' + +function ProposalProgress(props) { + const { proposal, tally } = props + const { proposal_id, status } = proposal + + if (!tally) return null + if (status === 'PROPOSAL_STATUS_DEPOSIT_PERIOD') return null + + const variants = { + 'yes': 'success', + 'abstain': 'info', + 'no': 'danger', + 'no_with_veto': 'danger' + } + const total = Object.values(tally).reduce((sum, value) => add(sum, value), 0) + if(total === 0) return null + + const progress = Object.keys(tally).reduce((sum, key) => { + sum[key] = multiply(divide(tally[key], total), 100) + return sum + }, {}) + return ( + + {Object.entries(progress).map(([key, value]) => { + return

    {[_.startCase(key), format(value, { precision: 2, notation: 'fixed' })].join(': ')}%

    + })} + + } + > + + {Object.entries(progress).map(([key, value]) => { + return ( + + ) + })} + +
    + ) +} + +export default ProposalProgress; diff --git a/src/components/Proposals.js b/src/components/Proposals.js new file mode 100644 index 000000000..235306c65 --- /dev/null +++ b/src/components/Proposals.js @@ -0,0 +1,175 @@ +import React, { useState, useEffect } from 'react'; +import Moment from 'react-moment'; +import _ from 'lodash' +import FuzzySearch from 'fuzzy-search' + +import { + Table, + Button, + Nav, +} from 'react-bootstrap' +import { CheckCircle, XCircle } from "react-bootstrap-icons"; + +import ProposalProgress from './ProposalProgress'; +import { PROPOSAL_STATUSES } from '../utils/Proposal.mjs'; + +function Proposals(props) { + const { address, network, proposals, tallies, votes } = props + + const [filter, setFilter] = useState({keywords: '', status: '', group: 'voting'}) + const [results, setResults] = useState([]) + + useEffect(() => { + let filtered = filteredProposals(proposals, filter) + let group = filter.group + while(filtered.length < 1 && group !== 'all'){ + group = 'all' + filtered = filteredProposals(proposals, {...filter, group}) + if(filtered.length > 0){ + return setFilter({ ...filter, group }) + } + } + setResults(filtered) + }, [proposals, filter]); + + function sortProposals(proposals){ + return _.sortBy(proposals, ({ proposal_id }) => { + return 0 - parseInt(proposal_id) + }); + } + + function filterProposals(event){ + setFilter({...filter, keywords: event.target.value}) + } + + function filteredProposals(proposals, filter){ + let searchResults = proposals + const { keywords, status, group } = filter + + if(status){ + searchResults = searchResults.filter(result => { + return result.status === status + }) + } + + searchResults = filterByGroup(searchResults, group) + + if (!keywords || keywords === '') return sortProposals(searchResults) + + const searcher = new FuzzySearch( + searchResults, ['content.title'], + { sort: true } + ) + + return searcher.search(keywords) + } + + function filterByGroup(proposals, group){ + switch (group) { + case 'voting': + proposals = proposals.filter((proposal) => proposal.isVoting) + break; + } + return proposals + } + + function renderProposal(proposal) { + const proposalId = proposal.proposal_id + const vote = votes[proposalId] + return ( + + {proposalId} + + props.showProposal(proposal)}> + {proposal.content.title} + + + + {proposal.statusHuman} + + + + {proposal.isDeposit ? proposal.deposit_end_time : proposal.voting_end_time} + + + + {proposal.isVoting && ( + vote ? vote.optionHuman : + )} + + + + + +
    + +
    + + + ); + } + + return ( + <> +
    +
    +
    + + + + +
    +
    +
    + +
    +
    + + {/* */} +
    +
    + {results.length > 0 && + + + + + + + + + + + + + + {results.map(item => renderProposal(item))} + +
    #ProposalStatusEnd TimeVotedProgress
    + } + {results.length < 1 && +

    No proposals found

    + } + + ) +} + +export default Proposals; diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index 6ebb73858..fcc2ad577 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -27,7 +27,7 @@ function ValidatorModal(props) { useEffect(() => { if (props.show && selectedValidator && validator?.operator_address === selectedValidator.operator_address && params.validator !== selectedValidator.operator_address) { navigate(`/${network.name}/${selectedValidator.operator_address}`) - } else if (params.validator && !props.show) { + } else if (params.validator && props.show === false) { navigate(`/${network.name}`) } }, [props.show, params.validator, selectedValidator]) diff --git a/src/components/Validators.js b/src/components/Validators.js index 840e8bf62..21d32bee6 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -3,7 +3,7 @@ import _ from 'lodash' import FuzzySearch from 'fuzzy-search' import { Bech32 } from '@cosmjs/encoding' -import { add } from 'mathjs' +import { format, add } from 'mathjs' import Coins from "./Coins"; import ClaimRewards from "./ClaimRewards"; @@ -217,6 +217,9 @@ function Validators(props) { )} )} + + {format(validator.commission.commission_rates.rate * 100, 2)}% + - - {denomRewards && ( - - - - )} - + {!props.modal && ( + + {denomRewards && ( + + + + )} + + )}
    {props.manageButton ? ( @@ -367,7 +372,7 @@ function Validators(props) {
    - @@ -419,8 +424,11 @@ function Validators(props) { )} + Fee Delegation - Rewards + {!props.modal && ( + Rewards + )} @@ -430,11 +438,15 @@ function Validators(props) { - - - - - + + + + + {network.apyEnabled !== false && ( + + )} + + - - {props.rewards && ( - - { - const reward = props.rewards[result.operator_address]?.reward?.find(el => el.denom === network.denom) - if(!reward) return sum + {!props.modal && ( + + {props.rewards && ( + + { + const reward = props.rewards[result.operator_address]?.reward?.find(el => el.denom === network.denom) + if (!reward) return sum - return add(sum, reward.amount) - }, 0), - denom: network.denom - }} - decimals={network.decimals} /> - - )} - + return add(sum, reward.amount) + }, 0), + denom: network.denom + }} + decimals={network.decimals} /> + + )} + + )} diff --git a/src/components/VoteForm.js b/src/components/VoteForm.js new file mode 100644 index 000000000..0ef332b31 --- /dev/null +++ b/src/components/VoteForm.js @@ -0,0 +1,150 @@ +import React, { useState, useEffect, useReducer } from 'react'; +import _ from 'lodash' + +import { + Alert, + Form, + Button +} from 'react-bootstrap' +import Vote from '../utils/Vote.mjs'; +import AlertMessage from './AlertMessage.js'; + + +function VoteForm(props) { + const { proposal, vote, address, setError } = props + const { proposal_id } = proposal + + const choices = { + 'VOTE_OPTION_YES': 'Yes', + 'VOTE_OPTION_NO': 'No', + 'VOTE_OPTION_NO_WITH_VETO': 'No with Veto', + 'VOTE_OPTION_ABSTAIN': 'Abstain' + } + + const choiceIndices = { + 'VOTE_OPTION_YES': 1, + 'VOTE_OPTION_NO': 3, + 'VOTE_OPTION_NO_WITH_VETO': 4, + 'VOTE_OPTION_ABSTAIN': 2 + } + + const [choice, setChoice] = useState() + const [loading, setLoading] = useState() + + if (vote && !choice) { + setChoice(vote.option) + } + + function handleVoteChange(event){ + setChoice(event.target.name) + } + + function handleSubmit(event) { + event.preventDefault(); + + if(!choice){ + return setError('Please choose an option') + } + + setLoading(true) + setError() + + const newVote = Vote({ + proposal_id: proposal_id, + voter: address, + option: choice, + options: [ + { + choice, + weight: "1.000000000000000000" + } + ] + }) + + const message = { + typeUrl: "/cosmos.gov.v1beta1.MsgVote", + value: { + proposalId: proposal.proposal_id, + voter: address, + option: newVote.optionValue + } + } + + console.log(message) + + props.stargateClient.signAndBroadcast(address, [message]).then((result) => { + console.log("Successfully broadcasted:", result); + setLoading(false) + setError(null) + props.onVote(proposal, newVote) + }, (error) => { + console.log('Failed to broadcast:', error) + setLoading(false) + setError(error.message) + }) + } + + const voteChanged = vote && vote.option !== choice + + function canVote(){ + if(!proposal.isVoting) return false + + return choice && (!vote || (vote && voteChanged)) + } + + function buttonText(){ + if(!proposal.isVoting) return 'Voting ended' + + return vote ? voteChanged ? 'Change vote' : 'Voted' : 'Vote' + } + + + return ( + <> +
    + +
    + {_.chunk(Object.entries(choices), 2).map((group, index) => { + return ( +
    + {group.map(([key, value]) => { + const voteChoice = vote && key === vote.option + return ( +
    + + + + {voteChoice ? {value} : value} + + +
    + ) + })} +
    + ) + }).flat()} +
    +
    +

    + {!loading + ? ( + + ) : ( + + )} +

    +
    + + ) +} + +export default VoteForm \ No newline at end of file diff --git a/src/index.js b/src/index.js index 929f5177e..735826992 100644 --- a/src/index.js +++ b/src/index.js @@ -20,6 +20,8 @@ const app = ( } /> } /> + } /> + } /> } /> diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 138768818..ed169bd29 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -55,7 +55,7 @@ class Network { this.gasPrice = this.data.gasPrice || this.defaultGasPrice this.gasPriceStep = this.data.gasPriceStep this.gasPricePrefer = this.data.gasPricePrefer - this.gasModifier = this.data.gasModifier || 1.3 + this.gasModifier = this.data.gasModifier || 1.5 this.txTimeout = this.data.txTimeout || 60_000 } diff --git a/src/utils/Proposal.mjs b/src/utils/Proposal.mjs new file mode 100644 index 000000000..56cc09d0c --- /dev/null +++ b/src/utils/Proposal.mjs @@ -0,0 +1,33 @@ +export const PROPOSAL_STATUSES = { + '': 'All', + 'PROPOSAL_STATUS_DEPOSIT_PERIOD': 'Deposit Period', + 'PROPOSAL_STATUS_VOTING_PERIOD': 'Voting Period', + 'PROPOSAL_STATUS_PASSED': 'Passed', + 'PROPOSAL_STATUS_REJECTED': 'Rejected', + 'PROPOSAL_STATUS_FAILED': 'Failed' +} + +const Proposal = (data) => { + + const { title, description } = data.content + const type = data.content['@type'] + + const fixedDescription = description.split(/\\n/).join('\n') + const statusHuman = PROPOSAL_STATUSES[data.status] + const typeHuman = type.split('.').reverse()[0] + + const isDeposit = data.status === 'PROPOSAL_STATUS_DEPOSIT_PERIOD' + const isVoting = data.status === 'PROPOSAL_STATUS_VOTING_PERIOD' + + return { + ...data, + type, + fixedDescription, + statusHuman, + typeHuman, + isDeposit, + isVoting + } +} + +export default Proposal; diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index bd64370f0..8e499ab57 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -105,6 +105,34 @@ const QueryClient = async (chainId, restUrls) => { }); }; + const getProposals = (opts) => { + const { pageSize } = opts || {} + return getAllPages((nextKey) => { + const searchParams = new URLSearchParams(); + searchParams.append("pagination.limit", pageSize || 100); + if (nextKey) searchParams.append("pagination.key", nextKey); + + return axios + .get(restUrl + "/cosmos/gov/v1beta1/proposals?" + + searchParams.toString(), opts) + .then((res) => res.data) + }).then((pages) => { + return pages.map(el => el.proposals).flat(); + }); + }; + + const getProposalTally = (proposal_id, opts) => { + return axios + .get(restUrl + "/cosmos/gov/v1beta1/proposals/" + proposal_id + '/tally', opts) + .then((res) => res.data) + }; + + const getProposalVote = (proposal_id, address, opts) => { + return axios + .get(restUrl + "/cosmos/gov/v1beta1/proposals/" + proposal_id + '/votes/' + address, opts) + .then((res) => res.data) + }; + const getGranteeGrants = (grantee, opts) => { const { pageSize } = opts || {} return getAllPages((nextKey) => { @@ -207,6 +235,9 @@ const QueryClient = async (chainId, restUrls) => { getBalance, getDelegations, getRewards, + getProposals, + getProposalTally, + getProposalVote, getGrants, getGranteeGrants, getGranterGrants, diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index f1010935d..dc724251e 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -169,15 +169,18 @@ async function SigningClient(network, defaultGasPrice, signer, key) { const account = await getAccount(address) const { account_number: accountNumber, sequence } = account const txBodyBytes = makeBodyBytes(messages, memo) - if(getIsNanoLedger()){ - // Convert to amino for ledger devices - const aminoMsgs = messages.map(el => aminoTypes.toAmino(el)) + let aminoMsgs + try { + aminoMsgs = messages.map(el => aminoTypes.toAmino(el)) + } catch { } + if(aminoMsgs){ + // Sign as amino if possible for Ledger and Keplr support const signDoc = makeAminoSignDoc(aminoMsgs, fee, chainId, memo, accountNumber, sequence); const { signature, signed } = await signer.sign(address, signDoc); const authInfoBytes = await makeAuthInfoBytes(account, { amount: signed.fee.amount, gasLimit: signed.fee.gas, - }) + }, SignMode.SIGN_MODE_LEGACY_AMINO_JSON) return { bodyBytes: txBodyBytes, authInfoBytes: authInfoBytes, @@ -188,7 +191,7 @@ async function SigningClient(network, defaultGasPrice, signer, key) { const authInfoBytes = await makeAuthInfoBytes(account, { amount: fee.amount, gasLimit: fee.gas, - }) + }, SignMode.SIGN_MODE_DIRECT) const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber); const { signature, signed } = await signer.signDirect(address, signDoc); return { @@ -239,7 +242,6 @@ async function SigningClient(network, defaultGasPrice, signer, key) { } async function makeAuthInfoBytes(account, fee, mode){ - mode = mode || getIsNanoLedger() ? SignMode.SIGN_MODE_LEGACY_AMINO_JSON : SignMode.SIGN_MODE_DIRECT const { sequence } = account const accountFromSigner = (await signer.getAccounts())[0] if (!accountFromSigner) { diff --git a/src/utils/Vote.mjs b/src/utils/Vote.mjs new file mode 100644 index 000000000..6fb5898a5 --- /dev/null +++ b/src/utils/Vote.mjs @@ -0,0 +1,27 @@ +export const VOTE_CHOICES = { + 'VOTE_OPTION_YES': 'Yes', + 'VOTE_OPTION_NO': 'No', + 'VOTE_OPTION_ABSTAIN': 'Abstain', + 'VOTE_OPTION_NO_WITH_VETO': 'No with Veto' +} + +export const VOTE_VALUES = { + 'VOTE_OPTION_YES': 1, + 'VOTE_OPTION_NO': 3, + 'VOTE_OPTION_NO_WITH_VETO': 4, + 'VOTE_OPTION_ABSTAIN': 2 +} + +const Vote = (data) => { + + const optionHuman = VOTE_CHOICES[data.option] + const optionValue = VOTE_VALUES[data.option] + + return { + ...data, + optionHuman, + optionValue + } +} + +export default Vote; From a1fc612bb5d2702e4f9a62616d3f3f57102c5727 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 19 Jun 2022 21:54:11 +0100 Subject: [PATCH 470/862] Add networks listing, favourites, UI improvements --- src/components/App.css | 14 ++ src/components/App.js | 95 ++++++++---- src/components/Delegations.js | 2 +- src/components/Governance.js | 7 +- src/components/ManageRestake.js | 54 +++---- src/components/NetworkChecks.js | 88 +++++++++++ src/components/NetworkFinder.js | 86 ++++++----- src/components/NetworkSelect.js | 233 ++++++++++++------------------ src/components/Networks.js | 144 ++++++++++++++++++ src/components/ProposalDetails.js | 5 +- src/components/ProposalModal.js | 2 +- src/components/Proposals.js | 24 +-- src/components/ValidatorGrants.js | 105 ++++++++------ src/components/Validators.js | 17 ++- src/components/VoteForm.js | 6 +- src/utils/Network.mjs | 9 +- 16 files changed, 579 insertions(+), 312 deletions(-) create mode 100644 src/components/NetworkChecks.js create mode 100644 src/components/Networks.js diff --git a/src/components/App.css b/src/components/App.css index 7a4f04769..f6a3f4a58 100644 --- a/src/components/App.css +++ b/src/components/App.css @@ -49,4 +49,18 @@ code { code p{ margin-bottom: 0.5rem; +} + +.favourite-on .on, +.favourite-on:hover .off, +.favourite-off .off, +.favourite-off:hover .on{ + display: inline; +} + +.favourite-on .off, +.favourite-on:hover .on, +.favourite-off .on, +.favourite-off:hover .off{ + display: none; } \ No newline at end of file diff --git a/src/components/App.js b/src/components/App.js index c698f7dd8..71ae463a2 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -24,7 +24,9 @@ import { DropletFill, DropletHalf, Coin, - Inboxes + Inboxes, + Hurricane, + Stars } from 'react-bootstrap-icons' import { CopyToClipboard } from 'react-copy-to-clipboard'; import GitHubButton from 'react-github-btn' @@ -39,15 +41,18 @@ import PoweredByAkash from '../assets/powered-by-akash.svg' import PoweredByAkashWhite from '../assets/powered-by-akash-white.svg' import TooltipIcon from './TooltipIcon'; import Governance from './Governance'; +import Networks from './Networks'; class App extends React.Component { constructor(props) { super(props); - this.state = {} + const favouriteJson = localStorage.getItem('favourites') + this.state = {favourites: favouriteJson ? JSON.parse(favouriteJson) : []} this.connect = this.connect.bind(this); this.disconnect = this.disconnect.bind(this); this.showNetworkSelect = this.showNetworkSelect.bind(this); this.getBalance = this.getBalance.bind(this); + this.toggleFavourite = this.toggleFavourite.bind(this); } async componentDidMount() { @@ -57,9 +62,11 @@ class App extends React.Component { } async componentDidUpdate(prevProps, prevState) { + if(!this.props.network) return + if (this.state.keplr != prevState.keplr) { this.connect() - } else if (this.props.network && this.props.network !== prevProps.network) { + } else if (this.props.network !== prevProps.network) { this.setState({ balance: undefined, address: undefined }) this.connect() } @@ -75,7 +82,7 @@ class App extends React.Component { } connected() { - return this.props.network.connected && Object.values(this.props.validators).length > 0 + return this.props.network?.connected && Object.values(this.props.validators).length > 0 } connectKeplr() { @@ -97,7 +104,7 @@ class App extends React.Component { } async connect(manual) { - if (!this.connected()) { + if (this.props.network && !this.connected()) { return this.setState({ error: 'Could not connect to any available API servers' }) @@ -185,6 +192,18 @@ class App extends React.Component { }) } + toggleFavourite(network){ + const { favourites } = this.state + let newFavourites + if(favourites.includes(network.path)){ + newFavourites = favourites.filter(el => el !== network.path) + }else{ + newFavourites = [...favourites, network.path] + } + localStorage.setItem('favourites', JSON.stringify(newFavourites)) + this.setState({ favourites: newFavourites }) + } + async getBalance() { this.state.queryClient.getBalance(this.state.address, this.props.network.denom) .then( @@ -229,9 +248,9 @@ class App extends React.Component { return (
    -
    -
    - this.setState({ showAbout: true })} role="button" className="text-reset text-decoration-none"> +
    +
    + this.props.setActive('networks')} role="button" className="text-reset text-decoration-none"> {this.props.theme === 'light' ? ( REStake @@ -240,7 +259,12 @@ class App extends React.Component { )}
    -
    +
    +

    + REStake allows validators to this.setState({ showAbout: true })} className="text-decoration-underline" role="button">auto-compound your {this.props.network && {this.props.network.prettyName}} staking rewards +

    +
    +
    {this.themeIcon()} { this.setState({ showNetworkSelect: false }) }} networks={this.props.networks} network={this.props.network} @@ -248,27 +272,36 @@ class App extends React.Component { changeNetwork={this.props.changeNetwork} />
    -
    +
    -
    -

    - REStake allows validators to this.setState({ showAbout: true })} className="text-decoration-underline" role="button">auto-compound your {this.props.network.prettyName} staking rewards for you -

    - {this.props.network.experimental && ( +
    + {this.props.network?.experimental && ( This network was added to REStake automatically and has not been thoroughly tested yet. Raise an issue if you have any problems. )} - {!this.state.address && ( + {!this.state.address && this.props.network && ( !this.state.keplr ? ( Please install the Keplr browser extension using desktop Google Chrome.
    WalletConnect and mobile support is coming soon.
    - ) : ( + ) : this.props.active !== 'networks' && (
    ) )} + {this.props.active === 'networks' && ( + + )} {this.props.active === 'governance' && this.state.address && ( {!this.authzSupport() && ( - {this.props.network.prettyName} doesn't support Authz just yet. You can stake and compound manually for now and REStake will update automatically when support is added. + {this.props.network.prettyName} doesn't support Authz just yet. You can stake and compound manually, REStake will update automatically when support is added. )} {this.authzSupport() && diff --git a/src/components/Governance.js b/src/components/Governance.js index ad81b2bfe..ebcc1d79a 100644 --- a/src/components/Governance.js +++ b/src/components/Governance.js @@ -103,9 +103,7 @@ function Governance(props) { (error) => { if(!proposals) setProposals([]) if (!hideError) { - this.setState({ - error: `Failed to load proposals: ${error.message}`, - }); + setError(`Failed to load proposals: ${error.message}`); } } ) @@ -117,7 +115,8 @@ function Governance(props) { const { proposal_id, final_tally_result: result } = proposal if (tallies[proposal_id]) return - if (_.every(Object.values(result), el => el === '0')) { + const talliesInvalid = _.every(Object.values(result), el => el === '0') + if (proposal.isVoting && talliesInvalid) { return props.queryClient.getProposalTally(proposal_id).then(result => { return setTallies({ [proposal_id]: result.tally }) }).catch(error => { }) diff --git a/src/components/ManageRestake.js b/src/components/ManageRestake.js index 4ba56db7a..d087e0054 100644 --- a/src/components/ManageRestake.js +++ b/src/components/ManageRestake.js @@ -16,39 +16,43 @@ function ManageRestake(props) { authzSupport && delegation ? ( grants.grantsValid ? ( - + ) : restakePossible ? ( - - {grants.grantsExist ? 'Update grants to re-enable REStake' : 'Authorize validator to REStake for you'} - - } - > - - + + {grants.grantsExist ? 'Update grants to re-enable REStake' : 'Authorize validator to REStake for you'} + + } + > + + ) : ( + + } + identifier={validator.operator_address} + tooltip="This validator can REStake your rewards" + /> + + ) + ) : ( + } identifier={validator.operator_address} tooltip="This validator can REStake your rewards" /> - ) - ) : ( - } - identifier={validator.operator_address} - tooltip="This validator can REStake your rewards" - /> + ) ) : ( + {state ? ( + + ) : ( + failClass === 'danger' ? : + )}{state ? title : (failTitle || title)} +
    + ) + + return ( +
  8. + +
  9. + ) + } + + const className = ['list-group', props.className].join(' ') + + const operatorCheck = { + title: {network.operatorCount} REStake operators, + failTitle: "No REStake operators", + failDescription: "There are no operators for this network yet. You can stake and compound manually in the meantime." + } + if(!network.authzSupport && network.operatorCount > 0){ + operatorCheck.failTitle = operatorCheck.title, + operatorCheck.failDescription = "Authz is disabled but there are operators ready when support is added." + } + + const testedCheck = { + title: 'Tested with REStake', + failTitle: 'Experimental support', + failDescription: "This network was added to REStake automatically and has not been thoroughly tested yet.", + } + if(!network.authzSupport && network.operatorCount > 0){ + testedCheck.failTitle = testedCheck.title, + testedCheck.failDescription = "Authz is disabled but all other features have been fully tested." + } + + return ( +
      + {([ + renderCheck({ + title: 'API connected', + failTitle: 'API offline', + failDescription: error, + state: (skipConnected ? network.online : network.connected) && !error, + failClass: 'danger', + identifier: 'network' + }), + renderCheck({ + title: 'Authz support', + failTitle: 'Authz unsupported', + failDescription: "This network doesn't support Authz just yet. You can stake and compound manually, REStake will update automatically when support is added.", + state: network.authzSupport, + identifier: 'authz' + }), + renderCheck({ + ...operatorCheck, + state: network.authzSupport && network.operatorCount > 0, + identifier: 'operators' + }), + renderCheck({ + ...testedCheck, + state: network.authzSupport && !network.experimental, + identifier: 'experimental' + }) + ])} +
    + ) +} + +export default NetworkChecks; diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js index 100a7cfa1..c2aeff045 100644 --- a/src/components/NetworkFinder.js +++ b/src/components/NetworkFinder.js @@ -11,6 +11,7 @@ import { } from 'react-bootstrap'; import networksData from '../networks.json'; +import Proposal from '../utils/Proposal.mjs'; const LIGHT_THEME = 'cosmo' const DARK_THEME = 'superhero' @@ -34,13 +35,15 @@ function NetworkFinder() { ) const getNetworks = async () => { - let registryNetworks + let registryNetworks, operatorCounts try { registryNetworks = await directory.getChains() + operatorCounts = await directory.getOperatorCounts() } catch (error) { setState({error: error.message, loading: false}) return {} } + const networks = Object.values(registryNetworks).map(data => { const networkData = networksData.find(el => el.name === data.path) @@ -49,7 +52,7 @@ function NetworkFinder() { if(!networkData) data.experimental = true - return {...data, ...networkData} + return new Network({...data, ...networkData}, operatorCounts[data.path]) }) return _.compact(networks).reduce((a, v) => ({ ...a, [v.path]: v}), {}) } @@ -58,26 +61,36 @@ function NetworkFinder() { setState({ network: network, validators: network.getValidators(), - operators: network.getOperators() + operators: network.getOperators(), + loading: false }) - if(govMatch){ - navigate("/" + network.name + '/govern'); + setActive('governance', network) }else{ - navigate("/" + network.name); + setActive('delegations', network) } } - const setActive = (active) => { - if(!state.network) return - if(active === 'governance'){ - navigate("/" + state.network.name + '/govern'); - }else{ - navigate("/" + state.network.name); + const setActive = (active, network) => { + network = network || state.network + switch (active) { + case 'governance': + navigate("/" + network.path + '/govern'); + break; + case 'delegations': + navigate("/" + network.path); + break; + default: + navigate("/"); + break; } setState({active}) + + const body = document.querySelector('#root'); + body.scrollIntoView({}, 500) } + useEffect(() => { const setThemeEvent = (event) => { setTheme(event.matches ? "dark" : "light") @@ -121,59 +134,44 @@ function NetworkFinder() { } }, [state.networks]) + useEffect(() => { + if(!params.network){ + setState({active: 'networks'}) + } + }, [govMatch, params.network]) + useEffect(() => { if(Object.keys(state.networks).length && !state.network){ - const networks = Object.values(state.networks) - const defaultNetwork = (networks.find(el => el.default === true) || networks[0]) - let networkName = params.network || defaultNetwork.name - let data = state.networks[networkName] - if(params.network && !data){ - networkName = defaultNetwork.name - data = state.networks[networkName] - } - if(!data){ + let networkName = params.network + const network = state.networks[networkName] + if(!network){ setState({loading: false}) return } if(params.network != networkName){ navigate("/" + networkName); } - const network = new Network(data) network.load().then(() => { return network.connect().then(() => { if (network.connected) { - setState({ network: network }) + setState({ + active: govMatch ? 'governance' : 'delegations', + network: network, + validators: network.getValidators(), + operators: network.getOperators(), + loading: false + }) } else { throw false } }) }).catch(error => { console.log(error) - setState({ network: network, loading: false }) + changeNetwork(network) }) } }, [state.networks, state.network, params.network, navigate]) - useEffect(() => { - if(state.error) return - if(!state.network || !state.network.connected) return - if(state.network && !Object.keys(state.validators).length){ - setState({ - validators: state.network.getValidators(), - operators: state.network.getOperators(), - loading: false - }) - } - }, [state.network]) - - useEffect(() => { - if(govMatch){ - setState({active: 'governance'}) - }else{ - setState({active: 'delegations'}) - } - }, [govMatch]) - useEffect(() => { if(params.validator && state.validators[params.validator]){ setState({ validator: state.validators[params.validator] }) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index e3d6f7e60..dbdb80ff0 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -2,7 +2,6 @@ import React, { useState, useReducer, useEffect } from 'react'; import CosmosDirectory from '../utils/CosmosDirectory.mjs'; import Network from '../utils/Network.mjs' -import TooltipIcon from './TooltipIcon.js'; import { Button, @@ -11,16 +10,14 @@ import { Badge } from 'react-bootstrap' -import { CheckCircle, XCircle } from "react-bootstrap-icons"; - import Select from 'react-select'; +import NetworkChecks from './NetworkChecks.js'; function NetworkSelect(props) { const [show, setShow] = useState(props.show); const [loading, setLoading] = useState(false); const [error, setError] = useState(false); const [selectedNetwork, setSelectedNetwork] = useState(); - const [validators, setValidators] = useState([]); const [operatorCounts, setOperatorCounts] = useState({}); const [options, setOptions] = useReducer( (state, newState) => ({ ...state, ...newState }), @@ -29,7 +26,6 @@ function NetworkSelect(props) { function handleOpen() { setSelectedNetwork(props.network); - setValidators(props.validators); CosmosDirectory().getOperatorCounts().then(counts => { setOperatorCounts(counts); }); @@ -37,6 +33,7 @@ function NetworkSelect(props) { } function handleClose() { + setSelectedNetwork(); setShow(false); props.onHide(); } @@ -44,16 +41,15 @@ function NetworkSelect(props) { function handleSubmit(event) { event.preventDefault(); - props.changeNetwork(selectedNetwork, validators); + props.changeNetwork(selectedNetwork); handleClose(); } function selectNetwork(newValue) { - const data = props.networks[newValue.value]; - if (data) { + const network = props.networks[newValue.value]; + if (network) { setLoading(true); setError(false); - const network = new Network(data); network.load().then(() => { setSelectedNetwork(network); if (network.usingDirectory && !network.connectedDirectory()) { @@ -61,7 +57,6 @@ function NetworkSelect(props) { } return network.connect().then(() => { if (network.connected) { - setValidators(network.getValidators()); setLoading(false); } else { throw false; @@ -75,29 +70,10 @@ function NetworkSelect(props) { } } - function renderCheck({ title, failTitle, description, failDescription, state, successClass, failClass, identifier }){ - const className = state ? (successClass || 'success') : (failClass || 'warning') + useEffect(() => { + + }, [selectedNetwork]); - const content = ( -
    - {state ? ( - - ) : ( - - )}{state ? title : (failTitle || title)} -
    - ) - - return ( -
  10. - -
  11. - ) - b} useEffect(() => { if (props.show && !show) { @@ -110,15 +86,14 @@ function NetworkSelect(props) { useEffect(() => { const networks = Object.values(props.networks).sort((a, b) => a.name > b.name ? 1 : -1) setOptions({ - networks: networks.map(el => { - const network = new Network(el) + networks: networks.map(network => { return { - value: el.name, - label: el.pretty_name, - image: el.image, - operatorCount: el.operators?.length || operatorCounts[el.name], - authz: el.params?.authz, - online: !network.usingDirectory || network.connectedDirectory(), + value: network.path, + label: network.prettyName, + image: network.image, + operatorCount: network.operatorCount, + authz: network.authzSupport, + online: network.online, experimental: network.experimental } }), @@ -129,18 +104,28 @@ function NetworkSelect(props) { return ( <> - : ( - - )}
    - - } + } + {selectedNetwork && ( + <> + +
    + {!loading + ? !error && + : ( + + )} +
    + + )} + diff --git a/src/components/Networks.js b/src/components/Networks.js new file mode 100644 index 000000000..9a93946bc --- /dev/null +++ b/src/components/Networks.js @@ -0,0 +1,144 @@ +import React, { useState, useEffect } from 'react'; +import _ from 'lodash' +import FuzzySearch from 'fuzzy-search' + +import { + Row, + Col, + Card, + Nav +} from 'react-bootstrap' +import { XCircle, BookmarkStar, BookmarkStarFill } from "react-bootstrap-icons"; +import NetworkChecks from './NetworkChecks'; + +function Networks(props) { + const { networks, favourites } = props + + const [filter, setFilter] = useState({keywords: '', group: 'favourites'}) + const [results, setResults] = useState([]) + + useEffect(() => { + let filtered = filteredNetworks(networks, filter) + let group = filter.group + while(filtered.length < 1 && group !== 'all'){ + group = 'all' + filtered = filteredNetworks(networks, {...filter, group}) + if(filtered.length > 0 || group === 'all'){ + return setFilter({ ...filter, group }) + } + } + setResults(filtered) + }, [networks, filter]); + + function filterNetworks(event){ + setFilter({...filter, keywords: event.target.value}) + } + + function filteredNetworks(networks, filter){ + let searchResults = networks + const { keywords, group } = filter + + switch (group) { + case 'favourites': + searchResults = searchResults.filter((network) => favourites.includes(network.path)) + break; + } + + if (!keywords || keywords === '') return searchResults + + const searcher = new FuzzySearch( + searchResults, ['prettyName'], + { sort: true } + ) + + return searcher.search(keywords) + } + + async function changeNetwork(network){ + await network.load() + await network.connect() + props.changeNetwork(network) + } + + function toggleFavourite(network){ + props.toggleFavourite(network) + } + + function renderFavourite(network){ + const favourited = favourites.includes(network.path) + const className="text-success favourite-" + (favourited ? 'on' : 'off') + return ( +
    + + +
    + ) + } + + function renderNetworks(networks, opts){ + return ( + + {networks.map((network) => ( + + + toggleFavourite(network)} role="button" className="text-right position-absolute top-0 end-0 py-1 px-2" style={{ zIndex: 2 }}> + {renderFavourite(network)} + + changeNetwork(network)}> + + + + + + + {network.prettyName} + + + + + + + + ))} + + ) + } + + return ( + <> +
    +
    +
    + + + + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + {renderNetworks(results)} + + ); +} + +export default Networks; \ No newline at end of file diff --git a/src/components/ProposalDetails.js b/src/components/ProposalDetails.js index f0bc28f8e..2717a487c 100644 --- a/src/components/ProposalDetails.js +++ b/src/components/ProposalDetails.js @@ -1,13 +1,10 @@ -import React, { useState, useEffect, useReducer } from 'react'; +import React, { useState } from 'react'; import Moment from 'react-moment'; import { Table, } from 'react-bootstrap' -import { - XCircle -} from 'react-bootstrap-icons' import Coins from './Coins'; import ProposalProgress from './ProposalProgress'; import VoteForm from './VoteForm'; diff --git a/src/components/ProposalModal.js b/src/components/ProposalModal.js index 16386fd6b..cfda4ee5e 100644 --- a/src/components/ProposalModal.js +++ b/src/components/ProposalModal.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { Modal, diff --git a/src/components/Proposals.js b/src/components/Proposals.js index 235306c65..c745c1322 100644 --- a/src/components/Proposals.js +++ b/src/components/Proposals.js @@ -8,13 +8,13 @@ import { Button, Nav, } from 'react-bootstrap' -import { CheckCircle, XCircle } from "react-bootstrap-icons"; +import { XCircle } from "react-bootstrap-icons"; import ProposalProgress from './ProposalProgress'; import { PROPOSAL_STATUSES } from '../utils/Proposal.mjs'; function Proposals(props) { - const { address, network, proposals, tallies, votes } = props + const { proposals, tallies, votes } = props const [filter, setFilter] = useState({keywords: '', status: '', group: 'voting'}) const [results, setResults] = useState([]) @@ -25,7 +25,7 @@ function Proposals(props) { while(filtered.length < 1 && group !== 'all'){ group = 'all' filtered = filteredProposals(proposals, {...filter, group}) - if(filtered.length > 0){ + if(filtered.length > 0 || group === 'all'){ return setFilter({ ...filter, group }) } } @@ -84,7 +84,7 @@ function Proposals(props) { {proposal.content.title} - + {proposal.statusHuman} @@ -97,7 +97,7 @@ function Proposals(props) { vote ? vote.optionHuman : )} - + @@ -126,8 +126,8 @@ function Proposals(props) {
    -
    -
    -
    {results.length > 0 && diff --git a/src/components/Validators.js b/src/components/Validators.js index 362ee3ba9..744d92ec7 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -1,7 +1,6 @@ import React, { useState, useEffect } from 'react'; import _ from 'lodash' import FuzzySearch from 'fuzzy-search' -import { Bech32 } from '@cosmjs/encoding' import { format, add } from 'mathjs' @@ -20,7 +19,7 @@ import { Tooltip, Nav } from 'react-bootstrap' -import { FilterSquare, XCircle } from "react-bootstrap-icons"; +import { XCircle } from "react-bootstrap-icons"; import ValidatorName from "./ValidatorName"; import ManageRestake from "./ManageRestake"; @@ -31,6 +30,8 @@ function Validators(props) { const [filter, setFilter] = useState({keywords: '', status: 'active', group: 'delegated'}) const [results, setResults] = useState([]) + const showCommission = results && Object.values(results).find(el => el.isValidatorOperator(address)) + useEffect(() => { if(delegations && filter.group !== 'delegated'){ return setFilter({ ...filter, group: 'delegated' }) @@ -122,14 +123,6 @@ function Validators(props) { return validators } - function isValidatorOperator(validator) { - if (!address || !validator || !window.atob) return false; - - const prefix = network.prefix - const validatorOperator = Bech32.encode(prefix, Bech32.decode(validator.operator_address).data) - return validatorOperator === address - } - function operatorForValidator(validatorAddress) { return operators.find((el) => el.address === validatorAddress); } @@ -137,12 +130,16 @@ function Validators(props) { function renderValidator(validator) { const validatorAddress = validator.operator_address const delegation = delegations[validatorAddress]; - const validatorOperator = isValidatorOperator(validator) + const validatorOperator = validator.isValidatorOperator(address) const rewards = props.rewards && props.rewards[validatorAddress]; const denomRewards = rewards && rewards.reward.find( (reward) => reward.denom === network.denom ); + const commission = props.commission && props.commission[validatorAddress] + const denomCommission = commission && commission.commission.find( + (commission) => commission.denom === network.denom + ); const operator = operatorForValidator(validatorAddress); const grants = operator && operatorGrants[operator.botAddress] @@ -246,6 +243,19 @@ function Validators(props) { )} )} + {!props.modal && showCommission && ( + + {denomCommission && ( + + + + )} + + )}
    {props.manageButton ? ( @@ -424,7 +434,6 @@ function Validators(props) { - {/* */}
    {results.length > 0 && @@ -455,6 +464,9 @@ function Validators(props) { {!props.modal && ( Rewards )} + {!props.modal && showCommission && ( + Commission + )} @@ -505,6 +517,25 @@ function Validators(props) { )} )} + {!props.modal && showCommission && ( + + {props.commission && ( + + { + const commission = props.commission[result.operator_address]?.commission?.find(el => el.denom === network.denom) + if (!commission) return sum + + return add(sum, commission.amount) + }, 0), + denom: network.denom + }} + decimals={network.decimals} /> + + )} + + )} diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index e14798700..2592d4d92 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -2,6 +2,7 @@ import _ from 'lodash' import { multiply, pow, format, bignumber } from 'mathjs' import QueryClient from './QueryClient.mjs' import SigningClient from './SigningClient.mjs' +import Validator from './Validator.mjs' import Operator from './Operator.mjs' import Chain from './Chain.mjs' import CosmosDirectory from './CosmosDirectory.mjs' @@ -58,9 +59,11 @@ class Network { async load() { this.chain = await Chain(this.data, this.directory) - this.validators = await this.directory.getValidators(this.name) - this.operators = (this.data.operators || this.validators.filter(el => el.restake && this.allowOperator(el.operator_address))).map(el => { - return Operator(el) + this.validators = (await this.directory.getValidators(this.name)).map(data => { + return Validator(this, data) + }) + this.operators = (this.data.operators || this.validators.filter(el => el.restake && this.allowOperator(el.operator_address))).map(data => { + return Operator(this, data) }) this.operatorCount = this.operators.length this.prettyName = this.chain.prettyName diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index d30a4acfe..fee21b260 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -1,7 +1,7 @@ import moment from 'moment' import parse from 'parse-duration' -const Operator = (data) => { +const Operator = (network, data) => { const { address } = data const botAddress = data.restake.address const runTime = data.restake.run_time diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 8e499ab57..53572eff0 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -89,12 +89,7 @@ const QueryClient = async (chainId, restUrls) => { const getRewards = (address, opts) => { return axios - .get( - restUrl + - "/cosmos/distribution/v1beta1/delegators/" + - address + - "/rewards", opts - ) + .get(`${restUrl}/cosmos/distribution/v1beta1/delegators/${address}/rewards`, opts) .then((res) => res.data) .then((result) => { const rewards = result.rewards.reduce( @@ -105,6 +100,15 @@ const QueryClient = async (chainId, restUrls) => { }); }; + const getCommission = (validatorAddress, opts) => { + return axios + .get(`${restUrl}/cosmos/distribution/v1beta1/validators/${validatorAddress}/commission`, opts) + .then((res) => res.data) + .then((result) => { + return result.commission + }); + }; + const getProposals = (opts) => { const { pageSize } = opts || {} return getAllPages((nextKey) => { @@ -235,6 +239,7 @@ const QueryClient = async (chainId, restUrls) => { getBalance, getDelegations, getRewards, + getCommission, getProposals, getProposalTally, getProposalVote, diff --git a/src/utils/Validator.mjs b/src/utils/Validator.mjs new file mode 100644 index 000000000..6154f33fb --- /dev/null +++ b/src/utils/Validator.mjs @@ -0,0 +1,21 @@ +import { Bech32 } from '@cosmjs/encoding' + +const Validator = (network, data) => { + const address = data.operator_address + + function isValidatorOperator(address) { + if (!address || !window.atob) return false; + + const prefix = network.prefix + const validatorOperator = Bech32.encode(prefix, Bech32.decode(data.operator_address).data) + return validatorOperator === address + } + + return { + ...data, + address, + isValidatorOperator + } +} + +export default Validator; From ea5b6a44cd503801bc125d04363774369ca68a77 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 8 Jul 2022 22:09:35 +0100 Subject: [PATCH 523/862] Prevent incorrect network delegation queries --- src/components/Delegations.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 902dd58c3..49c531f45 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -101,8 +101,9 @@ class Delegations extends React.Component { async getDelegations(hideError) { if(!this.props.address) return + const address = this.props.address - return this.props.queryClient.getDelegations(this.props.address) + return this.props.queryClient.getDelegations(address) .then( (delegations) => { const orderedAddresses = Object.keys(this.props.validators) @@ -112,11 +113,15 @@ class Delegations extends React.Component { } return sum }, {}) - this.setState({ - delegations: delegations, - }); + if(address === this.props.address){ + this.setState({ + delegations: delegations, + }); + } }, (error) => { + if(address !== this.props.address) return + if([404, 500].includes(error.response && error.response.status)){ this.setState({ delegations: {}, From c1ae61d0854a00faab82a653093c6687a71478b2 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Jul 2022 11:30:50 +0100 Subject: [PATCH 524/862] Tidy and handle missing validators --- src/components/Grants.js | 3 --- src/utils/Network.mjs | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/Grants.js b/src/components/Grants.js index de4045d17..bd0901660 100644 --- a/src/components/Grants.js +++ b/src/components/Grants.js @@ -30,9 +30,6 @@ function Grants(props) { const isNanoLedger = props.stargateClient?.getIsNanoLedger() - // TODO - // Vote exec - useEffect(() => { if(!grants) return diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 2592d4d92..5e9efda02 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -144,7 +144,7 @@ class Network { getValidators(opts) { opts = opts || {} - return this.validators.filter(validator => { + return (this.validators || []).filter(validator => { if (opts.status) return validator.status === opts.status return true From 72b0cf0a7e95479f922ae86d39fdf3d817143a87 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Jul 2022 11:58:04 +0100 Subject: [PATCH 525/862] Improve Keplr default gas price step --- src/networks.json | 1 - src/utils/Network.mjs | 10 +++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 9c3fbdebb..6c00c85ec 100644 --- a/src/networks.json +++ b/src/networks.json @@ -197,7 +197,6 @@ }, { "name": "genesisl1", - "gasPrice": "200000el1", "txTimeout": 120000 } ] diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 5e9efda02..320873f70 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -1,5 +1,8 @@ import _ from 'lodash' import { multiply, pow, format, bignumber } from 'mathjs' +import { + GasPrice, +} from "@cosmjs/stargate"; import QueryClient from './QueryClient.mjs' import SigningClient from './SigningClient.mjs' import Validator from './Validator.mjs' @@ -80,7 +83,12 @@ class Network { this.authzSupport = this.chain.authzSupport this.defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom this.gasPrice = this.data.gasPrice || this.defaultGasPrice - this.gasPriceStep = this.data.gasPriceStep + this.gasPriceAmount = GasPrice.fromString(this.gasPrice).amount.toString() + this.gasPriceStep = this.data.gasPriceStep || { + "low": multiply(this.gasPriceAmount, 0.5), + "average": multiply(this.gasPriceAmount, 1), + "high": multiply(this.gasPriceAmount, 2) + } this.gasPricePrefer = this.data.gasPricePrefer this.gasModifier = this.data.gasModifier || 1.5 this.txTimeout = this.data.txTimeout || 60_000 From e0e7f854a078f94103fdbac0c37f30ae74ddb3ff Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 9 Jul 2022 00:18:33 +0100 Subject: [PATCH 526/862] Full UI Authz functionality --- scripts/base.mjs | 32 ++- src/components/AddressModal.js | 105 ++++++++++ src/components/App.css | 4 +- src/components/App.js | 291 +++++++++++++++++++--------- src/components/ClaimRewards.js | 73 +++---- src/components/DelegateForm.js | 66 ++++--- src/components/Delegations.js | 13 +- src/components/Favourite.js | 20 ++ src/components/Governance.js | 38 ++-- src/components/GrantModal.js | 145 ++++++++------ src/components/Grants.js | 22 ++- src/components/ProposalDetails.js | 12 +- src/components/ProposalModal.js | 2 + src/components/RevokeGrant.js | 41 ++-- src/components/ValidatorDelegate.js | 9 + src/components/ValidatorGrants.js | 55 +++--- src/components/ValidatorModal.js | 12 ++ src/components/Validators.js | 17 +- src/components/VoteForm.js | 41 ++-- src/utils/EthSigner.mjs | 12 +- src/utils/Helpers.mjs | 24 +++ src/utils/Network.mjs | 8 - src/utils/QueryClient.mjs | 6 +- src/utils/SigningClient.mjs | 17 +- src/utils/Wallet.mjs | 68 +++++++ 25 files changed, 776 insertions(+), 357 deletions(-) create mode 100644 src/components/AddressModal.js create mode 100644 src/components/Favourite.js create mode 100644 src/utils/Wallet.mjs diff --git a/scripts/base.mjs b/scripts/base.mjs index 20e8afeb0..73bc0cad9 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -6,14 +6,13 @@ import { add, bignumber, floor, smaller, smallerEq } from 'mathjs' import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import { Slip10RawIndex, pathToString } from "@cosmjs/crypto"; -import { Wallet } from "@ethersproject/wallet"; -import { ETH } from "@tharsis/address-converter"; -import Bech32 from "bech32"; +import { Wallet as EthWallet } from "@ethersproject/wallet"; import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx.js"; import { MsgExec } from "cosmjs-types/cosmos/authz/v1beta1/tx.js"; import Network from '../src/utils/Network.mjs' +import Wallet from '../src/utils/Wallet.mjs'; import AutostakeHealth from "../src/utils/AutostakeHealth.mjs"; import {coin, timeStamp, mapSync, executeSync, overrideNetworks, parseGrants} from '../src/utils/Helpers.mjs' import EthSigner from '../src/utils/EthSigner.mjs'; @@ -106,7 +105,9 @@ export class Autostake { timeStamp('Starting', network.prettyName) - const { wallet, botAddress, slip44 } = await this.getWallet(network) + const { signer, slip44 } = await this.getSigner(network) + const wallet = new Wallet(network, signer) + const botAddress = await wallet.getAddress() timeStamp('Bot address is', botAddress) @@ -123,7 +124,7 @@ export class Autostake { await network.connect() if (!network.restUrl) throw new Error('Could not connect to REST API') - const client = await network.signingClient(wallet) + const client = wallet.signingClient client.registry.register("/cosmos.authz.v1beta1.MsgExec", MsgExec) return { @@ -135,7 +136,7 @@ export class Autostake { } } - async getWallet(network){ + async getSigner(network){ let slip44 if(network.data.autostake?.correctSlip44 || network.slip44 === 60){ if(network.slip44 === 60) timeStamp('Found ETH coin type') @@ -152,28 +153,17 @@ export class Autostake { ]; slip44 != 118 && timeStamp('Using HD Path', pathToString(hdPath)) - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.mnemonic, { + let signer = await DirectSecp256k1HdWallet.fromMnemonic(this.mnemonic, { prefix: network.prefix, hdPaths: [hdPath] }); if(network.slip44 === 60){ - return await this.getEthWallet(network, wallet) + const ethSigner = EthWallet.fromMnemonic(this.mnemonic); + signer = EthSigner(signer, ethSigner, network.prefix) } - const accounts = await wallet.getAccounts() - const botAddress = accounts[0].address - - return { wallet, botAddress, slip44 } - } - - async getEthWallet(network, signer){ - const wallet = Wallet.fromMnemonic(this.mnemonic); - const ethereumAddress = await wallet.getAddress(); - const data = ETH.decoder(ethereumAddress); - const botAddress = Bech32.encode(network.prefix, Bech32.toWords(data)) - - return { wallet: EthSigner(signer, wallet), botAddress, slip44: 60 } + return { signer, slip44 } } checkBalance(client) { diff --git a/src/components/AddressModal.js b/src/components/AddressModal.js new file mode 100644 index 000000000..35c0b4031 --- /dev/null +++ b/src/components/AddressModal.js @@ -0,0 +1,105 @@ +import React, { useState, useEffect } from 'react'; + +import { + Modal, + Table, + Form, + Button +} from 'react-bootstrap' + +import { + XCircle +} from 'react-bootstrap-icons' +import CopyToClipboard from 'react-copy-to-clipboard'; + +function AddressModal(props) { + const { show, network, networks, favouriteAddresses, updateFavouriteAddresses } = props + const [newAddress, setNewAddress] = useState('') + const [newLabel, setNewLabel] = useState('') + const [selectedNetwork, setSelectedNetwork] = useState(network) + + const networkAddresses = (selectedNetwork && favouriteAddresses[selectedNetwork.path]) || [] + + useEffect(() => { + setSelectedNetwork(network) + }, [network]) + + function onHide(){ + props.onHide() + } + + function updateAddress(address, label){ + updateFavouriteAddresses({...favouriteAddresses, [network.path]: networkAddresses.map(data => { + if(data.address === address){ + return {...data, label} + } + return data + })}) + } + + function removeAddress(address){ + updateFavouriteAddresses({...favouriteAddresses, [network.path]: networkAddresses.filter(data => data.address !== address)}) + } + + function addAddress(){ + if(!validNewAddress()) return + + updateFavouriteAddresses({...favouriteAddresses, [network.path]: [...networkAddresses, {address: newAddress, label: newLabel}]}) + setNewAddress('') + setNewLabel('') + } + + function validNewAddress(){ + return newAddress !== '' && !networkAddresses.some(el => el.address === newAddress) + } + + return ( + <> + onHide()}> + + Saved Addresses + + + + {selectedNetwork && ( + + + + + + + + + + {networkAddresses.map(({ label, address }) => { + return ( + + + + + + ) + })} + + + + + + +
    AddressLabel
    + + {address} + + updateAddress(address, e.target.value)} /> removeAddress(address)} />
    setNewAddress(e.target.value)} /> setNewLabel(e.target.value)} />
    + )} +
    +
    + + ); +} + +export default AddressModal diff --git a/src/components/App.css b/src/components/App.css index 0e98560fe..a5e3a4216 100644 --- a/src/components/App.css +++ b/src/components/App.css @@ -53,7 +53,7 @@ code p{ .favourite-on .on, .favourite-off .off { - display: inline; + display: block; } .favourite-on .off, @@ -64,7 +64,7 @@ code p{ @media (hover: hover) { .favourite-on:hover .off, .favourite-off:hover .on { - display: inline; + display: block; } .favourite-on:hover .on, diff --git a/src/components/App.js b/src/components/App.js index 3c8611211..536dc9821 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -30,7 +30,7 @@ import { WrenchAdjustableCircleFill, Magic, Clipboard, - ClipboardCheck + ClipboardCheck, } from 'react-bootstrap-icons' import { CopyToClipboard } from 'react-copy-to-clipboard'; import GitHubButton from 'react-github-btn' @@ -47,12 +47,19 @@ import TooltipIcon from './TooltipIcon'; import Governance from './Governance'; import Networks from './Networks'; import Grants from './Grants'; +import Favourite from './Favourite'; +import AddressModal from './AddressModal'; +import Wallet from '../utils/Wallet.mjs'; class App extends React.Component { constructor(props) { super(props); const favouriteJson = localStorage.getItem('favourites') - this.state = {favourites: favouriteJson ? JSON.parse(favouriteJson) : []} + const favouriteAddressJson = localStorage.getItem('favourite-addresses') + this.state = { + favourites: favouriteJson ? JSON.parse(favouriteJson) : [], + favouriteAddresses: favouriteAddressJson ? JSON.parse(favouriteAddressJson) : {} + } this.connect = this.connect.bind(this); this.disconnect = this.disconnect.bind(this); this.connectKeplr = this.connectKeplr.bind(this); @@ -61,6 +68,8 @@ class App extends React.Component { this.onGrant = this.onGrant.bind(this); this.onRevoke = this.onRevoke.bind(this); this.toggleFavourite = this.toggleFavourite.bind(this); + this.toggleFavouriteAddress = this.toggleFavouriteAddress.bind(this); + this.updateFavouriteAddresses = this.updateFavouriteAddresses.bind(this); } async componentDidMount() { @@ -70,13 +79,22 @@ class App extends React.Component { } async componentDidUpdate(prevProps, prevState) { - if(!this.props.network) return + if (!this.props.network) return if (this.state.keplr != prevState.keplr) { this.connect() } else if (this.props.network !== prevProps.network) { this.setState({ balance: undefined, address: undefined, grants: undefined }) this.connect() + }else if(this.state.address !== prevState.address){ + this.setState({ balance: undefined, grants: undefined }) + this.getBalance() + this.getGrants() + } + if(this.state.grants?.grantee !== prevState.grants?.grantee){ + if(this.state.wallet && this.state.wallet.address === this.state.address){ + this.state.wallet.grants = this.state.grants?.grantee || [] + } } } @@ -102,11 +120,12 @@ class App extends React.Component { } } - disconnect(){ + disconnect() { localStorage.removeItem('connected') this.setState({ address: null, balance: null, + wallet: null, stargateClient: null }) } @@ -119,22 +138,22 @@ class App extends React.Component { }) } - if(manual && !this.state.keplr){ + if (manual && !this.state.keplr) { return this.setState({ keplrError: true }) } - if(localStorage.getItem('connected') !== '1'){ - if(manual){ + if (localStorage.getItem('connected') !== '1') { + if (manual) { localStorage.setItem('connected', '1') - }else{ + } else { return } } const { network } = this.props - if(!network) return + if (!network) return const chainId = network.chainId @@ -155,16 +174,16 @@ class App extends React.Component { try { const offlineSigner = await window.getOfflineSignerAuto(chainId) const key = await window.keplr.getKey(chainId); - const stargateClient = await network.signingClient(offlineSigner, key, network.gasPricePrefer) - - const accounts = await offlineSigner.getAccounts(); - const address = accounts[0].address; - + const wallet = new Wallet(network, offlineSigner, key) + const stargateClient = wallet.signingClient stargateClient.registry.register("/cosmos.authz.v1beta1.MsgGrant", MsgGrant) stargateClient.registry.register("/cosmos.authz.v1beta1.MsgRevoke", MsgRevoke) + + const address = await wallet.getAddress(); + this.setState({ address: address, - accountName: key.name, + wallet: wallet, stargateClient: stargateClient, error: false }) @@ -217,24 +236,44 @@ class App extends React.Component { this.setState({ grantInterval }); } - clearRefreshInterval(){ + clearRefreshInterval() { clearInterval(this.state.grantInterval); } - toggleFavourite(network){ + toggleFavourite(network) { const { favourites } = this.state let newFavourites - if(favourites.includes(network.path)){ + if (favourites.includes(network.path)) { newFavourites = favourites.filter(el => el !== network.path) - }else{ + } else { newFavourites = [...favourites, network.path] } localStorage.setItem('favourites', JSON.stringify(newFavourites)) this.setState({ favourites: newFavourites }) } + toggleFavouriteAddress(address, label) { + const favourites = this.state.favouriteAddresses[this.props.network.path] || [] + let newFavourites + if (favourites.some(el => el.address === address)) { + newFavourites = favourites.filter(el => el.address !== address) + } else { + newFavourites = [...favourites, { address, label }] + } + this.updateFavouriteAddresses({ ...this.state.favouriteAddresses, [this.props.network.path]: newFavourites }) + } + + updateFavouriteAddresses(newFavourites) { + localStorage.setItem('favourite-addresses', JSON.stringify(newFavourites)) + this.setState({ favouriteAddresses: newFavourites }) + } + + otherFavouriteAddresses() { + return (this.state.favouriteAddresses[this.props.network.path] || []).filter(el => el.address !== this.state.wallet.address) + } + async getBalance() { - if(!this.state.address) return + if (!this.state.address) return this.props.queryClient.getBalance(this.state.address, this.props.network.denom) .then( @@ -253,34 +292,59 @@ class App extends React.Component { try { granterGrants = await this.props.queryClient.getGranterGrants(address) - if(address !== this.state.address) return + if (address !== this.state.address) return granteeGrants = await this.props.queryClient.getGranteeGrants(address) grantQuerySupport = true - } catch (error) { - console.log('Failed to get all grants in batch', error.message) + } catch (error) { + console.log('Failed to get all grants in batch', error.message) grantQuerySupport = error.response?.status !== 501 } - if(granterGrants){ + if (granterGrants) { this.setState((state) => { if (address !== state.address) return {} - return { grantQuerySupport, grants: { granter: granterGrants, grantee: (granteeGrants || state.grants?.grantee) } } + return { + grantQuerySupport, + grants: { + granter: granterGrants, + grantee: (granteeGrants || state.grants?.grantee) + } + } }) return } - const calls = this.props.operators.map((operator) => { + let addresses = this.props.operators.map(el => el.botAddress) + const favourites = this.state.favouriteAddresses[this.props.network.path] || [] + addresses = addresses.concat(favourites.filter(el => !addresses.includes(el.address)).map(el => el.address)) + + granterGrants = await this.getGrantsIndividually(addresses.map(el => { + return { grantee: el, granter: address } + })) + this.setState((state) => { + return { grantQuerySupport, grants: { ...state.grants, granter: granterGrants } } + }) + granteeGrants = await this.getGrantsIndividually(favourites.map(el => { + return { granter: el.address, grantee: address } + })) + this.setState((state) => { + return { grantQuerySupport, grants: { ...state.grants, grantee: granteeGrants } } + }) + } + + async getGrantsIndividually(grants){ + const address = this.state.address + const calls = grants.map(({granter, grantee}) => { return () => { - const { botAddress } = operator; - if (!this.state.address || !this.props.operators.includes(operator)) return; + if (address !== this.state.address) return - return this.props.queryClient.getGrants(botAddress, this.state.address).then( + return this.props.queryClient.getGrants(grantee, granter).then( (result) => { return result.map(grant => { return { ...grant, - grantee: botAddress, - granter: this.state.address + grantee, + granter } }) }); @@ -289,42 +353,51 @@ class App extends React.Component { const batchCalls = _.chunk(calls, 5); - granterGrants = [] + let allGrants = [] for (const batchCall of batchCalls) { + if (address !== this.state.address) return const grants = (await Promise.allSettled(batchCall.map(call => call()))).map(el => el.status === 'fulfilled' && el.value) - granterGrants = granterGrants.concat(_.compact(grants.flat())) + allGrants = allGrants.concat(_.compact(grants.flat())) } - this.setState((state, props) => { - return { grantQuerySupport, grants: { ...state.grants, granter: granterGrants } } - }) + return allGrants } - onGrant(grantee, grant){ + onGrant(grantee, grant) { + const filterGrant = (el) => { + if (el.grantee !== grantee) return true + if (el.authorization['@type'] === grant.authorization['@type'] && el.authorization.msg === grant.authorization.msg) { + return false + } + return true + } this.setState((state, props) => { - const granterGrants = state.grants ? state.grants.granter.filter(el => { - if(el.grantee !== grantee) return true - if(el.authorization['@type'] === grant.authorization['@type'] && el.authorization.msg === grant.authorization.msg){ - return false - } - return true - }) : [] + const granterGrants = state.grants ? state.grants.granter.filter(filterGrant) : [] granterGrants.push(grant) return { grants: { ...state.grants, granter: granterGrants } } }) + if(grantee === this.state.wallet.address){ + const grants = this.state.wallet.grants.filter(filterGrant) + grants.push(grant) + this.state.wallet.grants = grants + } } - onRevoke(grantee, msgTypes){ + onRevoke(grantee, msgTypes) { + const filterGrant = (el) => { + if (el.grantee !== grantee) return true + if (msgTypes.includes('/cosmos.staking.v1beta1.MsgDelegate')) { + if (el.authorization['@type'] === '/cosmos.staking.v1beta1.StakeAuthorization') return false + } + if (el.authorization['@type'] === '/cosmos.authz.v1beta1.GenericAuthorization' && msgTypes.includes(el.authorization.msg)) return false + return true; + } this.setState((state, props) => { - const granterGrants = state.grants.granter.filter(el => { - if(el.grantee !== grantee) return true - if(msgTypes.includes('/cosmos.staking.v1beta1.MsgDelegate')){ - if(el.authorization['@type'] === '/cosmos.staking.v1beta1.StakeAuthorization') return false - } - if (el.authorization['@type'] === '/cosmos.authz.v1beta1.GenericAuthorization' && msgTypes.includes(el.authorization.msg)) return false - return true; - }) + const granterGrants = state.grants.granter.filter(filterGrant) return { grants: { ...state.grants, granter: granterGrants } } }) + if(grantee === this.state.wallet.address){ + this.state.wallet.grants = this.state.wallet.grants.filter(filterGrant) + } } setCopied() { @@ -383,6 +456,18 @@ class App extends React.Component { ) } + introText(){ + switch (this.props.active) { + case 'networks': + return REStake automatically imports Cosmos chains from the Chain Registry + case 'governance': + return REStake let's you vote on behalf of your other {this.props.network && {this.props.network.prettyName}} wallets using Authz + case 'grants': + return REStake manages all your {this.props.network && {this.props.network.prettyName}} Authz grants in one place + } + return REStake allows validators to this.setState({ showAbout: true })} className="text-decoration-underline" role="button">auto-compound your {this.props.network && {this.props.network.prettyName}} staking rewards + } + render() { return ( @@ -403,7 +488,7 @@ class App extends React.Component {

    - REStake allows validators to this.setState({ showAbout: true })} className="text-decoration-underline" role="button">auto-compound your {this.props.network && {this.props.network.prettyName}} staking rewards + {this.introText()}

    @@ -419,11 +504,11 @@ class App extends React.Component {
    {this.props.availableBalance &&
    this.setAvailableAmount()}> - Available: + Available:
    }
    diff --git a/src/components/Grants.js b/src/components/Grants.js index d76f8d88e..2ba82f32c 100644 --- a/src/components/Grants.js +++ b/src/components/Grants.js @@ -97,7 +97,7 @@ function Grants(props) { case "/cosmos.staking.v1beta1.StakeAuthorization": return ( - Maximum: {maxTokens ? : 'unlimited'}
    + Maximum: {maxTokens ? : 'unlimited'}
    Validators: {grant.authorization.allow_list.address.join(', ')}
    ) diff --git a/src/components/ProposalDetails.js b/src/components/ProposalDetails.js index 6f4026992..e509a6b60 100644 --- a/src/components/ProposalDetails.js +++ b/src/components/ProposalDetails.js @@ -105,7 +105,7 @@ function ProposalDetails(props) { Total deposit {proposal.total_deposit.map(coins => { - return + return })} diff --git a/src/components/SendModal.js b/src/components/SendModal.js index 4a16aaddb..f42d64c27 100644 --- a/src/components/SendModal.js +++ b/src/components/SendModal.js @@ -20,7 +20,7 @@ function SendModal(props) { const [error, setError] = useState() const [state, setState] = useState({recipientValue: '', customRecipientValue: '', memoValue: ''}); - const denom = network && network.symbol.toUpperCase() + const denom = network && network.symbol const step = 1 / pow(10, network?.decimals || 6) useEffect(() => { @@ -188,7 +188,7 @@ function SendModal(props) {
    {props.balance &&
    setAvailableAmount()}> - Available: + Available:
    }
    @@ -200,7 +200,7 @@ function SendModal(props) {

    {!loading ? ( - + ) : + + ) : ( + + ) + ) : ( + - - ) : ( - - ) + ) ) : (

    diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index 1345ea0f8..a53857c3c 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -153,7 +153,7 @@ class DelegateForm extends React.Component {
    {this.props.availableBalance &&
    this.setAvailableAmount()}> - Available: + Available:
    }
    diff --git a/src/components/GrantModal.js b/src/components/GrantModal.js index a7afef06f..9de7ea058 100644 --- a/src/components/GrantModal.js +++ b/src/components/GrantModal.js @@ -19,7 +19,7 @@ import { buildExecMessage } from '../utils/Helpers.mjs'; function GrantModal(props) { const { show, network, address, wallet } = props - const isNanoLedger = props.wallet?.getIsNanoLedger() + const isNanoLedger = wallet?.getIsNanoLedger() const defaultExpiry = moment().add(1, 'year') const [loading, setLoading] = useState(false); const [error, setError] = useState() @@ -37,7 +37,7 @@ function GrantModal(props) { messageTypeValue: messageTypes[0], customMessageTypeValue: '', }) - setShowLedger(isNanoLedger) + setShowLedger(wallet?.getIsNanoLedger()) setError(null) }, [address]) diff --git a/src/components/Grants.js b/src/components/Grants.js index 2ba82f32c..93cbc246a 100644 --- a/src/components/Grants.js +++ b/src/components/Grants.js @@ -97,7 +97,7 @@ function Grants(props) { case "/cosmos.staking.v1beta1.StakeAuthorization": return ( - Maximum: {maxTokens ? : 'unlimited'}
    + Maximum: {maxTokens ? : 'unlimited'}
    Validators: {grant.authorization.allow_list.address.join(', ')}
    ) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index a11b66537..f19f062ae 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -115,6 +115,8 @@ function NetworkSelect(props) { }) }, [props.networks, selectedNetwork]) + const price = props.network?.baseAsset?.prices?.coingecko + return ( <> + ) :
    diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index bad9d4c89..d7a2e43ce 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -2,12 +2,9 @@ import ChainAsset from "./ChainAsset.mjs"; const Chain = async (data, directory) => { const chainData = await directory.getChainData(data.name); - const tokenData = await directory.getTokenData(data.name); - const assets = tokenData.assets.map(el => ChainAsset(el)) + const assets = chainData.assets.map(el => ChainAsset(el)) const baseAsset = assets[0] - const base = baseAsset.base - const display = baseAsset.display return { prettyName: data.prettyName || chainData.pretty_name, @@ -26,8 +23,7 @@ const Chain = async (data, directory) => { explorers: chainData.explorers, assets, baseAsset, - chainData, - tokenData + chainData } } diff --git a/src/utils/ChainAsset.mjs b/src/utils/ChainAsset.mjs index 608b718f0..f62b417f9 100644 --- a/src/utils/ChainAsset.mjs +++ b/src/utils/ChainAsset.mjs @@ -1,10 +1,6 @@ function ChainAsset(data) { - const { symbol } = data - const base = data.denom_units.find(el => el.denom === data.base) - const display = data.denom_units.find(el => el.denom === data.display) + const { symbol, base, display, image } = data const decimals = display?.exponent ?? 6 - const logo_URIs = data.logo_URIs - const image = logo_URIs && (logo_URIs.svg || logo_URIs.png) return { ...data, From f030b8001ca0d6db0b1e94cba4952be60b7125dd Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 4 Aug 2022 23:11:34 +0100 Subject: [PATCH 582/862] Show prices throughout UI and refactor --- src/assets/coingecko.png | Bin 0 -> 256511 bytes src/components/AboutLedger.js | 2 +- src/components/App.js | 21 +++++++++++++------ src/components/Coins.js | 4 ++-- src/components/Delegations.js | 2 +- src/components/GrantModal.js | 2 +- src/components/NetworkChecks.js | 23 ++++++++++++++------ src/components/NetworkSelect.js | 2 +- src/components/Validators.js | 36 ++++++++++++++++++++++---------- src/utils/Chain.mjs | 24 ++++++++++----------- src/utils/Network.mjs | 17 ++++++++------- 11 files changed, 84 insertions(+), 49 deletions(-) create mode 100644 src/assets/coingecko.png diff --git a/src/assets/coingecko.png b/src/assets/coingecko.png new file mode 100644 index 0000000000000000000000000000000000000000..76d2cc5489354c532031343644b3613a10782522 GIT binary patch literal 256511 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 literal 0 HcmV?d00001 diff --git a/src/components/AboutLedger.js b/src/components/AboutLedger.js index 484c14270..e07eef18c 100644 --- a/src/components/AboutLedger.js +++ b/src/components/AboutLedger.js @@ -10,7 +10,7 @@ function AboutLedger(props) { const aboutParam = searchParams.get('about') const { network, validator } = props - const { daemon_name, chain_id, node_home, codebase } = network.chain.chainData + const { daemon_name, chain_id, node_home, codebase } = network.chain.data const { git_repo, binaries } = codebase || {} const address = validator?.restake.address || GrantAddress diff --git a/src/components/App.js b/src/components/App.js index bd6e18c7b..d5833f21b 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -16,7 +16,8 @@ import { Dropdown, ButtonGroup, Navbar, - Nav + Nav, + Spinner } from 'react-bootstrap'; import { Droplet, @@ -627,13 +628,21 @@ class App extends React.Component { )} )} + {this.state.address && (
  12. - + {this.state.balance ? ( + + ) : ( + + Loading... + + )}
  13. + )}
  14. diff --git a/src/components/Coins.js b/src/components/Coins.js index cbcff202c..07affcd14 100644 --- a/src/components/Coins.js +++ b/src/components/Coins.js @@ -3,7 +3,7 @@ import _ from 'lodash' function Coins(props) { const { asset, coins, fullPrecision, inBaseDenom, hideValue, className } = props const { decimals, symbol, prices } = asset - const { coingecko } = prices + const { coingecko } = prices || {} function amount(coins){ if(inBaseDenom) return coins.amount @@ -13,7 +13,7 @@ function Coins(props) { } function value(coins){ - return (coins.amount / Math.pow(10, decimals) * coingecko.usd).toLocaleString(undefined, { maximumFractionDigits: 2 }) + return (coins.amount / Math.pow(10, decimals) * coingecko.usd).toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 }) } if(!coins || !coins.denom){ diff --git a/src/components/Delegations.js b/src/components/Delegations.js index cb24e568c..74a42ade1 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -443,7 +443,7 @@ class Delegations extends React.Component { validators={this.props.validators} operators={this.props.operators} validatorApy={this.state.validatorApy} - delegations={this.state.delegations || {}} + delegations={this.state.delegations} rewards={this.state.rewards} commission={this.state.commission} stargateClient={this.props.stargateClient} diff --git a/src/components/GrantModal.js b/src/components/GrantModal.js index 9de7ea058..66acdf8bc 100644 --- a/src/components/GrantModal.js +++ b/src/components/GrantModal.js @@ -26,7 +26,7 @@ function GrantModal(props) { const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry.format('YYYY-MM-DD') }); const [showLedger, setShowLedger] = useState(isNanoLedger) - const { daemon_name, chain_id } = network.chain.chainData + const { daemon_name, chain_id } = network.chain.data useEffect(() => { setState({ diff --git a/src/components/NetworkChecks.js b/src/components/NetworkChecks.js index 4b1a2e769..9c4a5b6b9 100644 --- a/src/components/NetworkChecks.js +++ b/src/components/NetworkChecks.js @@ -2,16 +2,19 @@ import React from 'react'; import _ from 'lodash' import { CheckCircle, XCircle, InfoCircle } from "react-bootstrap-icons"; import TooltipIcon from './TooltipIcon.js'; +import Coingecko from '../assets/coingecko.png' function NetworkChecks(props) { const { network, error, skipConnected } = props + const baseAsset = network?.baseAsset + const price = baseAsset?.prices?.coingecko - function renderCheck({ title, failTitle, description, failDescription, state, successClass, failClass, identifier }) { + function renderCheck({ title, failTitle, description, failDescription, state, successClass, failClass, identifier, icon }) { const className = state ? (successClass || 'success') : (failClass || 'warning') const content = (
    - {state ? ( + {icon ? icon : state ? ( ) : ( failClass === 'danger' ? : @@ -55,17 +58,25 @@ function NetworkChecks(props) { return (
      {([ + renderCheck({ + title: {`$${price && price.usd.toLocaleString(undefined, { maximumFractionDigits: 8, minimumFractionDigits: 2 })}`}, + failTitle: 'Unknown Price', + state: price?.usd, + identifier: 'price', + icon: , + failClass: 'success', + }), renderCheck({ title: {`${Math.round(network.estimatedApr * 100).toLocaleString()}% APR`}, - failTitle: 'APR Unknown', + failTitle: 'Unknown APR', state: network.estimatedApr, - identifier: 'apr' + identifier: 'apr', }), - renderCheck({ + !skipConnected && renderCheck({ title: 'API connected', failTitle: 'API offline', failDescription: error, - state: (skipConnected ? network.online : network.connected) && !error, + state: network.connected && !error, failClass: 'danger', identifier: 'network' }), diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index f19f062ae..edc4a7391 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -128,7 +128,7 @@ function NetworkSelect(props) {
      {props.network.prettyName} {!!price?.usd && ( -   ${price.usd.toLocaleString(undefined, { maximumFractionDigits: 2 })} +   ${price.usd.toLocaleString(undefined, { maximumFractionDigits: 4, minimumFractionDigits: 2 })} )}
      diff --git a/src/components/Validators.js b/src/components/Validators.js index 56b3b0346..f7c71f8e8 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -38,7 +38,7 @@ function Validators(props) { if(delegations && filter.group !== 'delegated'){ return setFilter({ ...filter, group: 'delegated' }) } - }, [Object.keys(delegations).length]); + }, [Object.keys(delegations || {}).length]); useEffect(() => { let filtered = filteredValidators(validators, filter) @@ -131,7 +131,7 @@ function Validators(props) { function renderValidator(validator) { const validatorAddress = validator.operator_address - const delegation = delegations[validatorAddress]; + const delegation = delegations && delegations[validatorAddress]; const validatorOperator = validator.isValidatorOperator(address) const rewards = props.rewards && props.rewards[validatorAddress]; @@ -234,16 +234,22 @@ function Validators(props) { {format(validator.commission.commission_rates.rate * 100, 2)}% - - - + {delegations ? ( + + + + ) : ( + + Loading... + + )} {!props.modal && ( - {denomRewards && ( + {props.rewards ? denomRewards && ( + ) : ( + + Loading... + )} )} {!props.modal && showCommission && ( - {denomCommission && ( + {denomCommission ? ( + ) : validatorOperator && ( + + Loading... + )} )} @@ -513,7 +527,7 @@ function Validators(props) { { - const delegation = delegations[result.operator_address] + const delegation = delegations && delegations[result.operator_address] if(!delegation) return sum return add(sum, delegation.balance.amount) diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index d7a2e43ce..a82ed86e5 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -1,29 +1,27 @@ import ChainAsset from "./ChainAsset.mjs"; -const Chain = async (data, directory) => { - const chainData = await directory.getChainData(data.name); - - const assets = chainData.assets.map(el => ChainAsset(el)) +const Chain = (data) => { + const assets = data.assets.map(el => ChainAsset(el)) const baseAsset = assets[0] return { - prettyName: data.prettyName || chainData.pretty_name, - chainId: data.chainId || chainData.chain_id, - prefix: data.prefix || chainData.bech32_prefix, - slip44: data.slip44 || chainData.slip44 || 118, - estimatedApr: chainData.params?.calculated_apr, - authzSupport: data.authzSupport ?? chainData.params?.authz, + prettyName: data.prettyName || data.pretty_name, + chainId: data.chainId || data.chain_id, + prefix: data.prefix || data.bech32_prefix, + slip44: data.slip44 || data.slip44 || 118, + estimatedApr: data.params?.calculated_apr, + authzSupport: data.authzSupport ?? data.params?.authz, denom: data.denom || baseAsset.base?.denom, display: data.display || baseAsset.display?.denom, symbol: data.symbol || baseAsset.symbol, decimals: data.decimals || baseAsset.decimals, image: data.image || baseAsset.image, coinGeckoId: baseAsset.coingecko_id, - services: chainData.services, - explorers: chainData.explorers, + services: data.services, + explorers: data.explorers, assets, baseAsset, - chainData + data } } diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 0ef8eebe4..4e354ddf8 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -14,19 +14,17 @@ class Network { this.data = data this.enabled = data.enabled this.experimental = data.experimental - this.authzSupport = data.params?.authz - this.estimatedApr = data.params?.calculated_apr this.operatorAddresses = operatorAddresses || {} this.operatorCount = data.operators?.length || this.estimateOperatorCount() - this.apyEnabled = data.apyEnabled !== false && (!this.estimatedApr || this.estimatedApr > 0) this.name = data.path || data.name this.path = data.path || data.name this.image = data.image this.prettyName = data.prettyName || data.pretty_name this.default = data.default this.testnet = data.testnet || data.network_type === 'testnet' - this.directory = CosmosDirectory(this.testnet) + this.setChain(this.data) + this.directory = CosmosDirectory(this.testnet) this.rpcUrl = this.directory.rpcUrl(this.name) // only used for Keplr suggestChain this.restUrl = data.restUrl || this.directory.restUrl(this.name) @@ -42,7 +40,7 @@ class Network { } connectedDirectory() { - const apis = this.chain ? this.chain.chainData['best_apis'] : this.data['best_apis'] + const apis = this.chain ? this.chain.data['best_apis'] : this.data['best_apis'] return apis && ['rest'].every(type => apis[type].length > 0) } @@ -60,7 +58,8 @@ class Network { } async load() { - this.chain = await Chain(this.data, this.directory) + const chainData = await this.directory.getChainData(this.data.name); + this.setChain({...this.data, ...chainData}) this.validators = (await this.directory.getValidators(this.name)).map(data => { return Validator(this, data) }) @@ -68,6 +67,10 @@ class Network { return Operator(this, data) }) this.operatorCount = this.operators.length + } + + async setChain(data){ + this.chain = Chain(data) this.prettyName = this.chain.prettyName this.chainId = this.chain.chainId this.prefix = this.chain.prefix @@ -81,7 +84,7 @@ class Network { this.image = this.chain.image this.coinGeckoId = this.chain.coinGeckoId this.estimatedApr = this.chain.estimatedApr - this.apyEnabled = this.apyEnabled && !!this.estimatedApr && this.estimatedApr > 0 + this.apyEnabled = data.apyEnabled !== false && !!this.estimatedApr && this.estimatedApr > 0 this.authzSupport = this.chain.authzSupport this.defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom this.gasPrice = this.data.gasPrice || this.defaultGasPrice From 7f95087597c22f92dadb809ffe1d8c5e085b5a6b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 5 Aug 2022 11:08:54 +0100 Subject: [PATCH 583/862] UI tweaks --- src/components/App.js | 13 +++++++------ src/components/ClaimRewards.js | 2 +- src/components/NetworkChecks.js | 4 ++-- src/components/RevokeGrant.js | 2 +- src/components/ValidatorDelegate.js | 2 +- src/components/Validators.js | 8 ++++---- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index d5833f21b..1bfb49180 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -629,12 +629,12 @@ class App extends React.Component { )} {this.state.address && ( -
    • +
    • {this.state.balance ? ( ) : ( @@ -656,7 +656,7 @@ class App extends React.Component { {this.state.address && (
      {this.addressName()} - + this.setCopied()}> this.setState({ showSendModal: true })} > @@ -680,16 +681,16 @@ class App extends React.Component { ) : ( <> - this.connect(true)} disabled={!window.keplr}>Connect Keplr Extension + this.connect(true)} disabled={!window.keplr}>Connect Keplr Extension {/* this.connect(true)} disabled={!window.falcon}>Connect Falcon Wallet */} )} - this.setState({ showAddressModal: true })}>Saved Addresses + this.setState({ showAddressModal: true })}>Saved Addresses {this.state.address && ( <> - Disconnect + Disconnect )} diff --git a/src/components/ClaimRewards.js b/src/components/ClaimRewards.js index 8bc940907..fbfd1f13c 100644 --- a/src/components/ClaimRewards.js +++ b/src/components/ClaimRewards.js @@ -117,7 +117,7 @@ function ClaimRewards(props) { return ( <> - claim()}> + claim()}> {buttonText()} diff --git a/src/components/NetworkChecks.js b/src/components/NetworkChecks.js index 9c4a5b6b9..96e45bed3 100644 --- a/src/components/NetworkChecks.js +++ b/src/components/NetworkChecks.js @@ -60,7 +60,7 @@ function NetworkChecks(props) { {([ renderCheck({ title: {`$${price && price.usd.toLocaleString(undefined, { maximumFractionDigits: 8, minimumFractionDigits: 2 })}`}, - failTitle: 'Unknown Price', + failTitle: 'Price Unknown', state: price?.usd, identifier: 'price', icon: , @@ -68,7 +68,7 @@ function NetworkChecks(props) { }), renderCheck({ title: {`${Math.round(network.estimatedApr * 100).toLocaleString()}% APR`}, - failTitle: 'Unknown APR', + failTitle: 'APR Unknown', state: network.estimatedApr, identifier: 'apr', }), diff --git a/src/components/RevokeGrant.js b/src/components/RevokeGrant.js index 4d620e43c..29d4a6bad 100644 --- a/src/components/RevokeGrant.js +++ b/src/components/RevokeGrant.js @@ -63,7 +63,7 @@ function RevokeGrant(props) { } return ( - revoke()} > + revoke()} > {buttonText} ) diff --git a/src/components/ValidatorDelegate.js b/src/components/ValidatorDelegate.js index 23b7faf70..6443f20d9 100644 --- a/src/components/ValidatorDelegate.js +++ b/src/components/ValidatorDelegate.js @@ -56,7 +56,7 @@ function ValidatorDelegate(props) { Current Rewards - + {!!props.commission && ( diff --git a/src/components/Validators.js b/src/components/Validators.js index f7c71f8e8..d05192a66 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -304,7 +304,7 @@ function Validators(props) { {operator && props.restakePossible && ( <> - props.showValidator(validator, { activeTab: 'restake' })}> + props.showValidator(validator, { activeTab: 'restake' })}> {grants.grantsValid ? 'Manage REStake' : 'Enable REStake'} {grants.grantsExist && ( @@ -372,13 +372,13 @@ function Validators(props) { )}
      - props.showValidator(validator, { activeTab: 'delegate' })}> + props.showValidator(validator, { activeTab: 'delegate' })}> Delegate - props.showValidator(validator, { redelegate: true })}> + props.showValidator(validator, { redelegate: true })}> Redelegate - props.showValidator(validator, { undelegate: true })}> + props.showValidator(validator, { undelegate: true })}> Undelegate From bbdd8cbb7962e806fa0d636afac628e6476a986e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 8 Aug 2022 10:29:20 +0100 Subject: [PATCH 584/862] Handle partial chain --- src/utils/Chain.mjs | 14 +++++++------- src/utils/Network.mjs | 14 ++++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index a82ed86e5..ff6c69be4 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -1,7 +1,7 @@ import ChainAsset from "./ChainAsset.mjs"; const Chain = (data) => { - const assets = data.assets.map(el => ChainAsset(el)) + const assets = data.assets?.map(el => ChainAsset(el)) || [] const baseAsset = assets[0] return { @@ -11,12 +11,12 @@ const Chain = (data) => { slip44: data.slip44 || data.slip44 || 118, estimatedApr: data.params?.calculated_apr, authzSupport: data.authzSupport ?? data.params?.authz, - denom: data.denom || baseAsset.base?.denom, - display: data.display || baseAsset.display?.denom, - symbol: data.symbol || baseAsset.symbol, - decimals: data.decimals || baseAsset.decimals, - image: data.image || baseAsset.image, - coinGeckoId: baseAsset.coingecko_id, + denom: data.denom || baseAsset?.base?.denom, + display: data.display || baseAsset?.display?.denom, + symbol: data.symbol || baseAsset?.symbol, + decimals: data.decimals || baseAsset?.decimals, + image: data.image || baseAsset?.image, + coinGeckoId: baseAsset?.coingecko_id, services: data.services, explorers: data.explorers, assets, diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 4e354ddf8..af3d5c419 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -86,13 +86,15 @@ class Network { this.estimatedApr = this.chain.estimatedApr this.apyEnabled = data.apyEnabled !== false && !!this.estimatedApr && this.estimatedApr > 0 this.authzSupport = this.chain.authzSupport - this.defaultGasPrice = format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom + this.defaultGasPrice = this.decimals && format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom this.gasPrice = this.data.gasPrice || this.defaultGasPrice - this.gasPriceAmount = GasPrice.fromString(this.gasPrice).amount.toString() - this.gasPriceStep = this.data.gasPriceStep || { - "low": multiply(this.gasPriceAmount, 0.5), - "average": multiply(this.gasPriceAmount, 1), - "high": multiply(this.gasPriceAmount, 2) + if(this.gasPrice){ + this.gasPriceAmount = GasPrice.fromString(this.gasPrice).amount.toString() + this.gasPriceStep = this.data.gasPriceStep || { + "low": multiply(this.gasPriceAmount, 0.5), + "average": multiply(this.gasPriceAmount, 1), + "high": multiply(this.gasPriceAmount, 2) + } } this.gasPricePrefer = this.data.gasPricePrefer this.gasModifier = this.data.gasModifier || 1.5 From b5c853e6ad3c1e06fdd62458ff4ed769477187a9 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 10 Aug 2022 17:06:20 +0100 Subject: [PATCH 585/862] Don't show loader unless address is set --- src/components/Validators.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Validators.js b/src/components/Validators.js index d05192a66..00bbd1f90 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -241,7 +241,7 @@ function Validators(props) { asset={network.baseAsset} /> - ) : ( + ) : address && ( Loading... @@ -257,7 +257,7 @@ function Validators(props) { asset={network.baseAsset} /> - ) : ( + ) : address && ( Loading... From 306023d3b3c383823c4b64c73653d444774b065d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 11 Aug 2022 20:16:11 +0100 Subject: [PATCH 586/862] Fix signingClient naming --- src/components/App.js | 18 +++++++++--------- src/components/ClaimRewards.js | 10 +++++----- src/components/DelegateForm.js | 6 +++--- src/components/Delegations.js | 8 ++++---- src/components/Governance.js | 4 ++-- src/components/GrantModal.js | 2 +- src/components/Grants.js | 4 ++-- src/components/ProposalDetails.js | 2 +- src/components/ProposalModal.js | 2 +- src/components/RevokeGrant.js | 6 +++--- src/components/SendModal.js | 6 +++--- src/components/ValidatorDelegate.js | 2 +- src/components/ValidatorGrants.js | 4 ++-- src/components/ValidatorModal.js | 4 ++-- src/components/Validators.js | 8 ++++---- src/components/VoteForm.js | 2 +- 16 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 1bfb49180..2a00a4e3f 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -134,7 +134,7 @@ class App extends React.Component { address: null, balance: null, wallet: null, - stargateClient: null + signingClient: null }) } @@ -182,16 +182,16 @@ class App extends React.Component { const offlineSigner = await window.getOfflineSignerAuto(chainId) const key = await window.keplr.getKey(chainId); const wallet = new Wallet(network, offlineSigner, key) - const stargateClient = wallet.signingClient - stargateClient.registry.register("/cosmos.authz.v1beta1.MsgGrant", MsgGrant) - stargateClient.registry.register("/cosmos.authz.v1beta1.MsgRevoke", MsgRevoke) + const signingClient = wallet.signingClient + signingClient.registry.register("/cosmos.authz.v1beta1.MsgGrant", MsgGrant) + signingClient.registry.register("/cosmos.authz.v1beta1.MsgRevoke", MsgRevoke) const address = await wallet.getAddress(); this.setState({ address: address, wallet: wallet, - stargateClient: stargateClient, + signingClient: signingClient, error: false }) } catch (e) { @@ -739,7 +739,7 @@ class App extends React.Component { onGrant={this.onGrant} onRevoke={this.onRevoke} queryClient={this.props.queryClient} - stargateClient={this.state.stargateClient} /> + signingClient={this.state.signingClient} /> } {this.props.active === 'governance' && ( @@ -749,7 +749,7 @@ class App extends React.Component { wallet={this.state.wallet} favouriteAddresses={this.favouriteAddresses()} queryClient={this.props.queryClient} - stargateClient={this.state.stargateClient} /> + signingClient={this.state.signingClient} /> )} {this.props.active === 'grants' && this.state.address && this.props.network.authzSupport && ( + signingClient={this.state.signingClient} /> )}

      @@ -827,7 +827,7 @@ class App extends React.Component { wallet={this.state.wallet} balance={this.state.balance} favouriteAddresses={this.favouriteAddresses()} - stargateClient={this.state.stargateClient} + signingClient={this.state.signingClient} onHide={() => this.setState({ showSendModal: false })} onSend={this.onSend} /> diff --git a/src/components/ClaimRewards.js b/src/components/ClaimRewards.js index fbfd1f13c..bdb8d40f4 100644 --- a/src/components/ClaimRewards.js +++ b/src/components/ClaimRewards.js @@ -9,7 +9,7 @@ import { import { add, subtract, multiply, divide, bignumber, floor } from 'mathjs' function ClaimRewards(props) { - const { address, wallet, stargateClient, validatorRewards } = props + const { address, wallet, signingClient, validatorRewards } = props async function claim(){ props.setLoading(true) @@ -18,14 +18,14 @@ function ClaimRewards(props) { let gas try { - gas = await stargateClient.simulate(wallet.address, gasSimMessages) + gas = await signingClient.simulate(wallet.address, gasSimMessages) } catch (error) { props.setLoading(false) props.setError('Failed to broadcast: ' + error.message) return } - const fee = stargateClient.getFee(gas) + const fee = signingClient.getFee(gas) const feeAmount = fee.amount[0].amount const totalReward = validatorRewards.reduce((sum, validatorReward) => add(sum, bignumber(validatorReward.reward)), 0); @@ -45,7 +45,7 @@ function ClaimRewards(props) { let messages = buildMessages(adjustedValidatorRewards) try { - gas = gas || await stargateClient.simulate(wallet.address, messages) + gas = gas || await signingClient.simulate(wallet.address, messages) } catch (error) { props.setLoading(false) props.setError('Failed to broadcast: ' + error.message) @@ -53,7 +53,7 @@ function ClaimRewards(props) { } console.log(messages, gas) - const signAndBroadcast = stargateClient.signAndBroadcastWithoutBalanceCheck + const signAndBroadcast = signingClient.signAndBroadcastWithoutBalanceCheck signAndBroadcast(wallet.address, messages, gas).then((result) => { console.log("Successfully broadcasted:", result); props.setLoading(false) diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index a53857c3c..2bc9b5969 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -38,7 +38,7 @@ class DelegateForm extends React.Component { const wallet = this.props.wallet const amount = this.state.amount const memo = this.state.memo - const client = this.props.stargateClient + const client = this.props.signingClient const decimals = pow(10, this.props.network.decimals) const denomAmount = bignumber(multiply(amount, decimals)) @@ -108,9 +108,9 @@ class DelegateForm extends React.Component { this.setState({error: undefined}) const messages = this.buildMessages(multiply(this.props.availableBalance.amount, 0.95)) - this.props.stargateClient.simulate(this.props.wallet.address, messages).then(gas => { + this.props.signingClient.simulate(this.props.wallet.address, messages).then(gas => { const saveTxFeeNum = (this.props.redelegate || this.props.undelegate) ? 0 : 10 - const gasPrice = this.props.stargateClient.getFee(gas).amount[0].amount + const gasPrice = this.props.signingClient.getFee(gas).amount[0].amount const decimals = pow(10, this.props.network.decimals) const amount = divide(subtract(this.props.availableBalance.amount, multiply(gasPrice, saveTxFeeNum)), decimals) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 74a42ade1..15741e32f 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -383,7 +383,7 @@ class Delegations extends React.Component { grants={this.operatorGrants()} authzSupport={this.authzSupport()} restakePossible={this.restakePossible()} - stargateClient={this.props.stargateClient} + signingClient={this.props.signingClient} hideModal={this.hideValidatorModal} onDelegate={this.onClaimRewards} onGrant={this.onGrant} @@ -446,7 +446,7 @@ class Delegations extends React.Component { delegations={this.state.delegations} rewards={this.state.rewards} commission={this.state.commission} - stargateClient={this.props.stargateClient} + signingClient={this.props.signingClient} validatorLoading={this.state.validatorLoading} isLoading={this.props.wallet && (!this.state.delegations || (this.props.network?.authzSupport && !this.props.grants?.granter))} operatorGrants={this.operatorGrants()} @@ -486,7 +486,7 @@ class Delegations extends React.Component { address={this.props.address} wallet={this.props.wallet} validatorRewards={this.validatorRewards()} - stargateClient={this.props.stargateClient} + signingClient={this.props.signingClient} onClaimRewards={this.onClaimRewards} setLoading={this.setClaimLoading} setError={this.setError} @@ -497,7 +497,7 @@ class Delegations extends React.Component { address={this.props.address} wallet={this.props.wallet} validatorRewards={this.validatorRewards()} - stargateClient={this.props.stargateClient} + signingClient={this.props.signingClient} onClaimRewards={this.onClaimRewards} setLoading={this.setClaimLoading} setError={this.setError} diff --git a/src/components/Governance.js b/src/components/Governance.js index 032532c94..f363baaea 100644 --- a/src/components/Governance.js +++ b/src/components/Governance.js @@ -185,7 +185,7 @@ function Governance(props) { proposals={proposals} tallies={tallies} votes={votes} - stargateClient={props.stargateClient} + signingClient={props.signingClient} showProposal={showProposal} setError={setError} onVote={onVote} /> @@ -201,7 +201,7 @@ function Governance(props) { granters={voteGrants.map(el => el.granter)} favouriteAddresses={props.favouriteAddresses} queryClient={props.queryClient} - stargateClient={props.stargateClient} + signingClient={props.signingClient} closeProposal={closeProposal} onVote={onVote} setError={setError} diff --git a/src/components/GrantModal.js b/src/components/GrantModal.js index 66acdf8bc..331a9c5af 100644 --- a/src/components/GrantModal.js +++ b/src/components/GrantModal.js @@ -71,7 +71,7 @@ function GrantModal(props) { ] console.log(messages) - props.stargateClient.signAndBroadcast(wallet.address, messages).then((result) => { + props.signingClient.signAndBroadcast(wallet.address, messages).then((result) => { console.log("Successfully broadcasted:", result); showLoading(false) props.onGrant(grantee(), { diff --git a/src/components/Grants.js b/src/components/Grants.js index 93cbc246a..81ea5d0b5 100644 --- a/src/components/Grants.js +++ b/src/components/Grants.js @@ -165,7 +165,7 @@ function Grants(props) { button={true} size="sm" grants={[grant]} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onRevoke={onRevoke} setLoading={(loading) => setGrantLoading({ [grantId]: loading }) @@ -292,7 +292,7 @@ function Grants(props) { address={props.address} wallet={props.wallet} favouriteAddresses={props.favouriteAddresses} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onHide={closeModal} onGrant={onGrant} /> diff --git a/src/components/ProposalDetails.js b/src/components/ProposalDetails.js index e509a6b60..dfe808a3e 100644 --- a/src/components/ProposalDetails.js +++ b/src/components/ProposalDetails.js @@ -138,7 +138,7 @@ function ProposalDetails(props) { address={props.address} wallet={props.wallet} granter={granter} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onVote={onVote} setError={setError} />

      diff --git a/src/components/ProposalModal.js b/src/components/ProposalModal.js index 32e6fbc3f..d09c0f171 100644 --- a/src/components/ProposalModal.js +++ b/src/components/ProposalModal.js @@ -34,7 +34,7 @@ function ProposalModal(props) { granters={props.granters} favouriteAddresses={props.favouriteAddresses} queryClient={props.queryClient} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onVote={props.onVote} /> diff --git a/src/components/RevokeGrant.js b/src/components/RevokeGrant.js index 29d4a6bad..5cec83d75 100644 --- a/src/components/RevokeGrant.js +++ b/src/components/RevokeGrant.js @@ -7,7 +7,7 @@ import { MsgRevoke } from "cosmjs-types/cosmos/authz/v1beta1/tx"; import { buildExecableMessage, buildExecMessage } from '../utils/Helpers.mjs' function RevokeGrant(props) { - const { address, wallet, grantAddress, grants, stargateClient } = props + const { address, wallet, grantAddress, grants, signingClient } = props const buttonText = props.buttonText || 'Revoke' @@ -30,8 +30,8 @@ function RevokeGrant(props) { console.log(messages) try { - const gas = await stargateClient.simulate(wallet.address, messages) - const result = await stargateClient.signAndBroadcast(wallet.address, messages, gas) + const gas = await signingClient.simulate(wallet.address, messages) + const result = await signingClient.signAndBroadcast(wallet.address, messages, gas) console.log("Successfully broadcasted:", result); if(props.setLoading) props.setLoading(false) props.onRevoke(grantAddress, msgTypes) diff --git a/src/components/SendModal.js b/src/components/SendModal.js index 710ca89b3..da97a3a52 100644 --- a/src/components/SendModal.js +++ b/src/components/SendModal.js @@ -57,7 +57,7 @@ function SendModal(props) { ] console.log(messages) - props.stargateClient.signAndBroadcast(wallet.address, messages, null, state.memoValue).then((result) => { + props.signingClient.signAndBroadcast(wallet.address, messages, null, state.memoValue).then((result) => { console.log("Successfully broadcasted:", result); showLoading(false) setState({ @@ -96,8 +96,8 @@ function SendModal(props) { const decimals = pow(10, network.decimals) const coinValue = coin(multiply(props.balance.amount, 0.95), network.denom) const message = buildSendMsg(address, recipient(), [coinValue]) - props.stargateClient.simulate(wallet.address, [message]).then(gas => { - const gasPrice = props.stargateClient.getFee(gas).amount[0].amount + props.signingClient.simulate(wallet.address, [message]).then(gas => { + const gasPrice = props.signingClient.getFee(gas).amount[0].amount const amount = divide(subtract(props.balance.amount, gasPrice), decimals) setState({...state, amountValue: amount > 0 ? amount : 0}) diff --git a/src/components/ValidatorDelegate.js b/src/components/ValidatorDelegate.js index 6443f20d9..dd50cdbfd 100644 --- a/src/components/ValidatorDelegate.js +++ b/src/components/ValidatorDelegate.js @@ -84,7 +84,7 @@ function ValidatorDelegate(props) { address={props.address} wallet={props.wallet} availableBalance={availableBalance} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onDelegate={onDelegate} /> ) diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 1ad8008c2..10f4f7d75 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -88,7 +88,7 @@ function ValidatorGrants(props) { ] console.log(messages) - props.stargateClient.signAndBroadcast(wallet.address, messages).then((result) => { + props.signingClient.signAndBroadcast(wallet.address, messages).then((result) => { console.log("Successfully broadcasted:", result); showLoading(false) props.onGrant(operator.botAddress, { @@ -263,7 +263,7 @@ function ValidatorGrants(props) { operator={operator} grants={[grants.stakeGrant, grants.claimGrant]} grantAddress={operator.botAddress} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onRevoke={props.onRevoke} setLoading={(loading) => showLoading(loading)} setError={setError} diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index 9e5e7595a..4850d1973 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -205,7 +205,7 @@ function ValidatorModal(props) { rewards={rewards()} commission={commission()} validatorApy={props.validatorApy} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onDelegate={onDelegate} /> {operator() && ( @@ -220,7 +220,7 @@ function ValidatorModal(props) { rewards={rewards()} authzSupport={props.authzSupport} restakePossible={props.restakePossible} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onGrant={props.onGrant} onRevoke={props.onRevoke} setError={props.setError} diff --git a/src/components/Validators.js b/src/components/Validators.js index 00bbd1f90..fa33c9718 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -314,7 +314,7 @@ function Validators(props) { grantAddress={operator.botAddress} grants={[grants.stakeGrant, grants.claimGrant]} buttonText="Disable REStake" - stargateClient={props.stargateClient} + signingClient={props.signingClient} onRevoke={props.onRevoke} setLoading={(loading) => props.setValidatorLoading( @@ -333,7 +333,7 @@ function Validators(props) { address={address} wallet={wallet} validatorRewards={props.validatorRewards([validatorAddress])} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onClaimRewards={props.onClaimRewards} setLoading={(loading) => props.setValidatorLoading(validatorAddress, loading) @@ -346,7 +346,7 @@ function Validators(props) { address={address} wallet={wallet} validatorRewards={props.validatorRewards([validatorAddress])} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onClaimRewards={props.onClaimRewards} setLoading={(loading) => props.setValidatorLoading(validatorAddress, loading) @@ -362,7 +362,7 @@ function Validators(props) { address={address} wallet={wallet} validatorRewards={props.validatorRewards([validatorAddress])} - stargateClient={props.stargateClient} + signingClient={props.signingClient} onClaimRewards={props.onClaimRewards} setLoading={(loading) => props.setValidatorLoading(validatorAddress, loading) diff --git a/src/components/VoteForm.js b/src/components/VoteForm.js index 26bf32d0a..c974ec46c 100644 --- a/src/components/VoteForm.js +++ b/src/components/VoteForm.js @@ -82,7 +82,7 @@ function VoteForm(props) { console.log(message) - props.stargateClient.signAndBroadcast(wallet.address, [message]).then((result) => { + props.signingClient.signAndBroadcast(wallet.address, [message]).then((result) => { console.log("Successfully broadcasted:", result); setLoading(false) setError(null) From 31cdd82aa5cad26d9888a9fe7d8305af74a80a9e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 12 Aug 2022 14:26:27 +0100 Subject: [PATCH 587/862] Abstract SignerProviders and add Falcon Wallet --- src/components/App.js | 128 ++++++++++++++--------------- src/components/Validators.js | 2 +- src/utils/FalconSignerProvider.mjs | 20 +++++ src/utils/KeplrSignerProvider.mjs | 20 +++++ src/utils/SignerProvider.mjs | 32 ++++++++ src/utils/SigningClient.mjs | 18 ++-- 6 files changed, 148 insertions(+), 72 deletions(-) create mode 100644 src/utils/FalconSignerProvider.mjs create mode 100644 src/utils/KeplrSignerProvider.mjs create mode 100644 src/utils/SignerProvider.mjs diff --git a/src/components/App.js b/src/components/App.js index 2a00a4e3f..a6ddd72b1 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -54,6 +54,8 @@ import Favourite from './Favourite'; import AddressModal from './AddressModal'; import Wallet from '../utils/Wallet.mjs'; import SendModal from './SendModal'; +import KeplrSignerProvider from '../utils/KeplrSignerProvider.mjs'; +import FalconSignerProvider from '../utils/FalconSignerProvider.mjs'; class App extends React.Component { constructor(props) { @@ -66,7 +68,6 @@ class App extends React.Component { } this.connect = this.connect.bind(this); this.disconnect = this.disconnect.bind(this); - this.connectKeplr = this.connectKeplr.bind(this); this.showNetworkSelect = this.showNetworkSelect.bind(this); this.getBalance = this.getBalance.bind(this); this.onSend = this.onSend.bind(this); @@ -78,17 +79,15 @@ class App extends React.Component { } async componentDidMount() { - this.connectKeplr() - window.addEventListener("load", this.connectKeplr) + this.connect() + window.addEventListener("load", this.connect) window.addEventListener("keplr_keystorechange", this.connect) } async componentDidUpdate(prevProps, prevState) { if (!this.props.network) return - if (this.state.keplr != prevState.keplr) { - this.connect() - } else if (this.props.network !== prevProps.network) { + if (this.props.network !== prevProps.network) { this.setState({ balance: undefined, address: undefined, wallet: undefined, grants: undefined }) this.connect() }else if(this.state.address !== prevState.address){ @@ -108,7 +107,7 @@ class App extends React.Component { componentWillUnmount() { this.clearRefreshInterval() - window.removeEventListener("load", this.connectKeplr) + window.removeEventListener("load", this.connect) window.removeEventListener("keplr_keystorechange", this.connect) } @@ -120,12 +119,22 @@ class App extends React.Component { return this.props.network?.connected } - connectKeplr() { - if (this.state.keplr && !window.keplr) { - this.setState({ keplr: false }) - } else if (!this.state.keplr && window.keplr) { - this.setState({ keplr: true }) - } + signerProviders(){ + return [{ + key: 'keplr', + label: 'Keplr Extension', + provider: window.keplr && new KeplrSignerProvider(window.keplr) + }, { + key: 'falcon', + label: 'Falcon Wallet', + provider: window.falcon && new FalconSignerProvider(window.falcon) + }] + } + + getSignerProvider(providerKey){ + if(!providerKey) providerKey = localStorage.getItem('connected') + + return providerKey && this.signerProviders().find(el => el.key === providerKey) } disconnect() { @@ -138,77 +147,68 @@ class App extends React.Component { }) } - async connect(manual) { + async connect(providerKey, manual) { if (this.props.network && !this.connected()) { return this.setState({ error: 'Could not connect to any available API servers' }) } - if (manual && !this.state.keplr) { + const signerProvider = this.getSignerProvider(providerKey) + + if(!signerProvider) return + + providerKey = signerProvider.key + + if (manual && !signerProvider.provider) { return this.setState({ - keplrError: true + providerError: providerKey }) } - if (localStorage.getItem('connected') !== '1') { + const provider = signerProvider.provider + + if (localStorage.getItem('connected') !== providerKey) { if (manual) { - localStorage.setItem('connected', '1') + localStorage.setItem('connected', providerKey) } else { return } } const { network } = this.props - if (!network) return - - const chainId = network.chainId + if (!network || !provider) return try { - if (window.keplr) { - if (network.gasPricePrefer) { - window.keplr.defaultOptions = { - sign: { preferNoSetFee: true } - } - } - await window.keplr.enable(chainId); - } + await provider.enable(network) } catch (e) { console.log(e.message, e) - await this.suggestChain(network) + await provider.suggestChain(network) } - if (window.getOfflineSigner) { - try { - const offlineSigner = await window.getOfflineSignerAuto(chainId) - const key = await window.keplr.getKey(chainId); - const wallet = new Wallet(network, offlineSigner, key) - const signingClient = wallet.signingClient - signingClient.registry.register("/cosmos.authz.v1beta1.MsgGrant", MsgGrant) - signingClient.registry.register("/cosmos.authz.v1beta1.MsgRevoke", MsgRevoke) - - const address = await wallet.getAddress(); - - this.setState({ - address: address, - wallet: wallet, - signingClient: signingClient, - error: false - }) - } catch (e) { - console.log(e) - return this.setState({ - error: 'Failed to connect to signer: ' + e.message - }) - } + try { + const offlineSigner = await provider.getSigner(network) + const key = await provider.getKey(network); + const wallet = new Wallet(network, offlineSigner, key) + const signingClient = wallet.signingClient + signingClient.registry.register("/cosmos.authz.v1beta1.MsgGrant", MsgGrant) + signingClient.registry.register("/cosmos.authz.v1beta1.MsgRevoke", MsgRevoke) + + const address = await wallet.getAddress(); + + this.setState({ + address: address, + wallet: wallet, + signingClient: signingClient, + error: false + }) + } catch (e) { + console.log(e) + return this.setState({ + error: `Failed to connect to ${signerProvider?.label || 'signer'}: ${e.message}` + }) } } - suggestChain(network) { - if (!window.keplr) return - - return window.keplr.experimentalSuggestChain(network.suggestChain()) - } - refreshInterval() { this.setState({ refresh: true }) this.refreshTimeout() @@ -681,8 +681,8 @@ class App extends React.Component { ) : ( <> - this.connect(true)} disabled={!window.keplr}>Connect Keplr Extension - {/* this.connect(true)} disabled={!window.falcon}>Connect Falcon Wallet */} + this.connect('keplr', true)} disabled={!window.keplr}>Connect Keplr Extension + this.connect('falcon', true)} disabled={!window.falcon}>Connect Falcon Wallet )} @@ -690,7 +690,7 @@ class App extends React.Component { {this.state.address && ( <> - Disconnect + Disconnect {this.getSignerProvider()?.label} )} @@ -710,8 +710,8 @@ class App extends React.Component { )} - {!this.state.keplr && this.state.keplrError && ( - this.setState({ keplrError: false })}> + {!this.state.providerError === 'keplr' && ( + this.setState({ providerError: false })}> Please install the
      Keplr browser extension using desktop Google Chrome.
      WalletConnect and mobile support is coming soon. )} diff --git a/src/components/Validators.js b/src/components/Validators.js index fa33c9718..75deb51d2 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -185,7 +185,7 @@ function Validators(props) { delegation={delegation} isLoading={props.isLoading} authzSupport={props.authzSupport} - restakePossible={props.restakePossible} + restakePossible={props.restakePossible && !props.modal} openGrants={() => props.showValidator(validator, { activeTab: 'restake' })} /> ) : ( diff --git a/src/utils/FalconSignerProvider.mjs b/src/utils/FalconSignerProvider.mjs new file mode 100644 index 000000000..c1db8a284 --- /dev/null +++ b/src/utils/FalconSignerProvider.mjs @@ -0,0 +1,20 @@ +import SignerProvider from "./SignerProvider.mjs"; + +export default class FalconSignerProvider extends SignerProvider { + enable(network){ + const { chainId } = network + return this.provider.connect(chainId) + } + + getSigner(network){ + const { chainId } = network + return this.provider.getOfflineSigner(chainId) + } + + getKey(network){ + } + + suggestChain(network){ + return this.provider.importZone(network.suggestChain()) + } +} \ No newline at end of file diff --git a/src/utils/KeplrSignerProvider.mjs b/src/utils/KeplrSignerProvider.mjs new file mode 100644 index 000000000..652d9bbc5 --- /dev/null +++ b/src/utils/KeplrSignerProvider.mjs @@ -0,0 +1,20 @@ +import SignerProvider from "./SignerProvider.mjs" + +export default class KeplrSignerProvider extends SignerProvider { + enable(network){ + if (network.gasPricePrefer) { + this.setOptions({ + sign: { preferNoSetFee: true } + }) + } + super.enable(network) + } + + setOptions(options){ + return this.provider.defaultOptions = options + } + + getOptions(){ + return this.provider.defaultOptions + } +} \ No newline at end of file diff --git a/src/utils/SignerProvider.mjs b/src/utils/SignerProvider.mjs new file mode 100644 index 000000000..490451fd1 --- /dev/null +++ b/src/utils/SignerProvider.mjs @@ -0,0 +1,32 @@ +export default class SignerProvider { + constructor(provider){ + this.provider = provider + } + + enable(network){ + const { chainId } = network + return this.provider.enable(chainId) + } + + getSigner(network){ + const { chainId } = network + return this.provider.getOfflineSignerAuto(chainId) + } + + getKey(network){ + const { chainId } = network + return this.provider.getKey(chainId) + } + + suggestChain(network){ + return this.provider.experimentalSuggestChain(network.suggestChain()) + } + + setOptions(options){ + return {} + } + + getOptions(){ + return {} + } +} \ No newline at end of file diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 182d452c8..2ee4aae7a 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -111,14 +111,17 @@ function SigningClient(network, signer) { } async function signAndBroadcastWithoutBalanceCheck(address, msgs, gas, memo, gasPrice) { - const defaultOptions = _.clone(signer.keplr.defaultOptions); - _.merge(signer.keplr.defaultOptions, { - sign: { disableBalanceCheck: true } - }); + let defaultOptions + if(signer.keplr.defaultOptions){ + defaultOptions = _.clone(signer.keplr.defaultOptions); + signer.keplr.defaultOptions = {...defaultOptions, sign: { disableBalanceCheck: true }} + } try { return await signAndBroadcast(address, msgs, gas, memo, gasPrice) } finally { - signer.keplr.defaultOptions = defaultOptions + if(defaultOptions){ + signer.keplr.defaultOptions = defaultOptions + } } } @@ -183,10 +186,11 @@ function SigningClient(network, signer) { try { aminoMsgs = messages.map(el => aminoTypes.toAmino(el)) } catch { } - if(aminoMsgs){ + if(aminoMsgs && signer.signAmino){ // Sign as amino if possible for Ledger and Keplr support const signDoc = makeAminoSignDoc(aminoMsgs, fee, chainId, memo, accountNumber, sequence); - const { signature, signed } = await signer.sign(address, signDoc); + console.log(signer) + const { signature, signed } = await signer.signAmino(address, signDoc); const authInfoBytes = await makeAuthInfoBytes(account, { amount: signed.fee.amount, gasLimit: signed.fee.gas, From 22b1a6f7116e4f5e9a375e4dc69302ceaeddb744 Mon Sep 17 00:00:00 2001 From: Keefer Taylor Date: Sun, 14 Aug 2022 01:41:52 -0700 Subject: [PATCH 588/862] attempt 2 --- scripts/autostake.mjs | 25 ++++++++++++++++-- scripts/base.mjs | 59 ++++++++++++++++++++++--------------------- test.js | 18 +++++++++++++ 3 files changed, 71 insertions(+), 31 deletions(-) create mode 100644 test.js diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 94da2d6d4..1649476ec 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -1,5 +1,26 @@ import { Autostake } from "./base.mjs"; const autostake = new Autostake(); -const networkName = process.argv.slice(2, process.argv.length) -autostake.run(networkName) +const networkNames = process.argv.slice(2, process.argv.length) + +const main = async () => { + const RETRIES = 10 + for (let i = 0; i < networkNames; i++) { + const networkName = networkNames[i] + console.log(`Starting Restake for ${networkName}...`) + + for (let j = 0; j < RETRIES; j++) { + try { + await autostake.run(networkName) + + // Success! Set j to mx to short circuit the loop. + j = tries + } catch (e) { + console.log(`[${i + 1}/${RETRIES}]Caught an error running on ${networkName}: ${e}`) + console.log(`Retrying after a delay...`) + await new Promise(r => setTimeout(r, (Math.random() * 31) * 1000)); + } + } + } +} +main() diff --git a/scripts/base.mjs b/scripts/base.mjs index 73bc0cad9..b24fd1ad0 100644 --- a/scripts/base.mjs +++ b/scripts/base.mjs @@ -14,30 +14,30 @@ import { MsgExec } from "cosmjs-types/cosmos/authz/v1beta1/tx.js"; import Network from '../src/utils/Network.mjs' import Wallet from '../src/utils/Wallet.mjs'; import AutostakeHealth from "../src/utils/AutostakeHealth.mjs"; -import {coin, timeStamp, mapSync, executeSync, overrideNetworks, parseGrants} from '../src/utils/Helpers.mjs' +import { coin, timeStamp, mapSync, executeSync, overrideNetworks, parseGrants } from '../src/utils/Helpers.mjs' import EthSigner from '../src/utils/EthSigner.mjs'; import 'dotenv/config' export class Autostake { - constructor(opts){ + constructor(opts) { this.opts = opts || {} this.mnemonic = process.env.MNEMONIC - if(!this.mnemonic){ + if (!this.mnemonic) { timeStamp('Please provide a MNEMONIC environment variable') process.exit() } } - async run(networkNames){ + async run(networkNames) { const networks = this.getNetworksData() - for(const name of networkNames){ + for (const name of networkNames) { if (name && !networks.map(el => el.name).includes(name)) return timeStamp('Invalid network name:', name) } const calls = networks.map(data => { return async () => { - if(networkNames && networkNames.length && !networkNames.includes(data.name)) return - if(data.enabled === false) return + if (networkNames && networkNames.length && !networkNames.includes(data.name)) return + if (data.enabled === false) return let client let health = new AutostakeHealth(data.healthCheck, { dryRun: this.opts.dryRun }) @@ -48,13 +48,13 @@ export class Autostake { return health.failed('Failed to connect', error.message) } - if(!client) return health.success('Skipping') + if (!client) return health.success('Skipping') const { restUrl, usingDirectory } = client.network timeStamp('Using REST URL', restUrl) - if(usingDirectory){ + if (usingDirectory) { timeStamp('You are using public nodes, script may fail with many delegations. Check the README to use your own') timeStamp('Delaying briefly to reduce load...') await new Promise(r => setTimeout(r, (Math.random() * 31) * 1000)); @@ -70,9 +70,9 @@ export class Autostake { await executeSync(calls, 1) } - async runNetwork(client){ + async runNetwork(client) { timeStamp('Running autostake') - const { network, health } = client + const { network, health } = client const balance = await this.checkBalance(client) if (!balance || smaller(balance, 1_000)) { return health.failed('Bot balance is too low') @@ -136,12 +136,12 @@ export class Autostake { } } - async getSigner(network){ + async getSigner(network) { let slip44 - if(network.data.autostake?.correctSlip44 || network.slip44 === 60){ - if(network.slip44 === 60) timeStamp('Found ETH coin type') + if (network.data.autostake?.correctSlip44 || network.slip44 === 60) { + if (network.slip44 === 60) timeStamp('Found ETH coin type') slip44 = network.slip44 || 118 - }else{ + } else { slip44 = network.data.autostake?.slip44 || 118 } let hdPath = [ @@ -158,7 +158,7 @@ export class Autostake { hdPaths: [hdPath] }); - if(network.slip44 === 60){ + if (network.slip44 === 60) { const ethSigner = EthWallet.fromMnemonic(this.mnemonic); signer = EthSigner(signer, ethSigner, network.prefix) } @@ -184,8 +184,9 @@ export class Autostake { return client.queryClient.getAllValidatorDelegations(client.operator.address, batchSize, (pages) => { timeStamp("...batch", pages.length) }).catch(error => { + const message = `Failed to get delegations: ${error.message || error}` client.health.error("Failed to get delegations:", error.message || error) - return [] + throw new Error(message) }) } @@ -194,10 +195,10 @@ export class Autostake { let allGrants try { allGrants = await client.queryClient.getGranteeGrants(botAddress) - } catch (e) { } + } catch (e) { } let grantCalls = addresses.map(item => { return async () => { - if(allGrants) return this.parseGrantResponse(allGrants, botAddress, item, address) + if (allGrants) return this.parseGrantResponse(allGrants, botAddress, item, address) try { return await this.getGrants(client, item) } catch (error) { @@ -226,14 +227,14 @@ export class Autostake { ) } - parseGrantResponse(grants, botAddress, delegatorAddress, validatorAddress){ + parseGrantResponse(grants, botAddress, delegatorAddress, validatorAddress) { const result = parseGrants(grants, botAddress, delegatorAddress) let grantValidators, maxTokens if (result.stakeGrant) { if (result.stakeGrant.authorization['@type'] === "/cosmos.authz.v1beta1.GenericAuthorization") { timeStamp(delegatorAddress, "Using GenericAuthorization, allowed") grantValidators = [validatorAddress]; - }else{ + } else { grantValidators = result.stakeGrant.authorization.allow_list.address if (!grantValidators.includes(validatorAddress)) { timeStamp(delegatorAddress, "Not autostaking for this validator, skipping") @@ -255,28 +256,28 @@ export class Autostake { let timeout = client.network.data.autostake?.delegatorTimeout || 5000 const withdrawAddress = await client.queryClient.getWithdrawAddress(address, { timeout }) - if(withdrawAddress && withdrawAddress !== address){ + if (withdrawAddress && withdrawAddress !== address) { timeStamp(address, 'has a different withdraw address:', withdrawAddress) return } const totalRewards = await this.totalRewards(client, address) - if(totalRewards === undefined) return + if (totalRewards === undefined) return let autostakeAmount = floor(totalRewards) - if (smaller(bignumber(autostakeAmount), bignumber(client.operator.minimumReward))) { + if (smaller(bignumber(autostakeAmount), bignumber(client.operator.minimumReward / 1000))) { timeStamp(address, autostakeAmount, client.network.denom, 'reward is too low, skipping') return } - if (grant.maxTokens){ - if(smallerEq(grant.maxTokens, 0)) { + if (grant.maxTokens) { + if (smallerEq(grant.maxTokens, 0)) { timeStamp(address, grant.maxTokens, client.network.denom, 'grant balance is empty, skipping') return } - if(smaller(grant.maxTokens, autostakeAmount)) { + if (smaller(grant.maxTokens, autostakeAmount)) { autostakeAmount = grant.maxTokens timeStamp(address, grant.maxTokens, client.network.denom, 'grant balance is too low, using remaining') } @@ -321,7 +322,7 @@ export class Autostake { health.complete(`${network.prettyName} finished: Sent ${results.length - errors.length}/${results.length} messages`) } - async sendInBatches(client, messages, batchSize, total){ + async sendInBatches(client, messages, batchSize, total) { if (messages) { this.batch = this.batch.concat(messages) } @@ -342,7 +343,7 @@ export class Autostake { } } - async sendMessages(client, messages){ + async sendMessages(client, messages) { try { const execMsg = this.buildExecMessage(client.operator.botAddress, messages) const memo = 'REStaked by ' + client.operator.moniker diff --git a/test.js b/test.js new file mode 100644 index 000000000..b44e805de --- /dev/null +++ b/test.js @@ -0,0 +1,18 @@ +const willThrow = () => { + try { + throw new Error('throw inner') + } catch (e) { + console.log(`caught`) + throw new Error('rethrown') + } +} + +const main = () => { + try { + willThrow() + } catch (e) { + console.log('caught at top level ' + e.message) + } +} + +main() \ No newline at end of file From b9f9adcee1813a86e714de7a99770fa2c2a25af5 Mon Sep 17 00:00:00 2001 From: Keefer Taylor Date: Sun, 14 Aug 2022 01:56:37 -0700 Subject: [PATCH 589/862] booting message --- scripts/autostake.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/autostake.mjs b/scripts/autostake.mjs index 1649476ec..1352220d0 100644 --- a/scripts/autostake.mjs +++ b/scripts/autostake.mjs @@ -4,6 +4,8 @@ const autostake = new Autostake(); const networkNames = process.argv.slice(2, process.argv.length) const main = async () => { + console.log("Restake is booting...!") + const RETRIES = 10 for (let i = 0; i < networkNames; i++) { const networkName = networkNames[i] From 81ba07de04e03cfbcbd99f8aa95293cd4aa9bdf7 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 17 Aug 2022 13:40:07 +0100 Subject: [PATCH 590/862] Remove log --- src/utils/SigningClient.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 2ee4aae7a..5b573e765 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -189,7 +189,6 @@ function SigningClient(network, signer) { if(aminoMsgs && signer.signAmino){ // Sign as amino if possible for Ledger and Keplr support const signDoc = makeAminoSignDoc(aminoMsgs, fee, chainId, memo, accountNumber, sequence); - console.log(signer) const { signature, signed } = await signer.signAmino(address, signDoc); const authInfoBytes = await makeAuthInfoBytes(account, { amount: signed.fee.amount, From 9fd282ed3835043f39df570af31d238e7f516f18 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 17 Aug 2022 13:42:16 +0100 Subject: [PATCH 591/862] Add Passage support --- src/networks.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/networks.json b/src/networks.json index df7edef32..b4ab76316 100644 --- a/src/networks.json +++ b/src/networks.json @@ -215,5 +215,9 @@ { "name": "kichaintestnet", "testnet": true + }, + { + "name": "passage", + "ownerAddress": "pasgvaloper196rujtjehu0dfc7y85lkcaps6tel76g3l9knjy" } ] From f8039d50d9b474b560e9d6708bf5dfd6072074c0 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 24 Aug 2022 16:31:21 +0100 Subject: [PATCH 592/862] Handle ledger and adjust events --- src/components/App.js | 26 +++++++++++++++++++++----- src/utils/FalconSignerProvider.mjs | 8 -------- src/utils/Wallet.mjs | 2 +- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index a6ddd72b1..f6ce9c696 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -66,7 +66,9 @@ class App extends React.Component { favourites: favouriteJson ? JSON.parse(favouriteJson) : [], favouriteAddresses: favouriteAddressJson ? JSON.parse(favouriteAddressJson) : {} } - this.connect = this.connect.bind(this); + this.connectAny = this.connectAny.bind(this); + this.connectKeplr = this.connectKeplr.bind(this); + this.connectFalcon = this.connectFalcon.bind(this); this.disconnect = this.disconnect.bind(this); this.showNetworkSelect = this.showNetworkSelect.bind(this); this.getBalance = this.getBalance.bind(this); @@ -80,8 +82,9 @@ class App extends React.Component { async componentDidMount() { this.connect() - window.addEventListener("load", this.connect) - window.addEventListener("keplr_keystorechange", this.connect) + window.addEventListener("load", this.connectAny) + window.addEventListener("keplr_keystorechange", this.connectKeplr) + window.addEventListener("falcon_keystorechange", this.connectFalcon) } async componentDidUpdate(prevProps, prevState) { @@ -107,8 +110,9 @@ class App extends React.Component { componentWillUnmount() { this.clearRefreshInterval() - window.removeEventListener("load", this.connect) - window.removeEventListener("keplr_keystorechange", this.connect) + window.removeEventListener("load", this.connectAny) + window.removeEventListener("keplr_keystorechange", this.connectKeplr) + window.removeEventListener("falcon_keystorechange", this.connectFalcon) } showNetworkSelect() { @@ -147,6 +151,18 @@ class App extends React.Component { }) } + connectAny(event){ + return this.connect() + } + + connectKeplr(event){ + return this.connect('keplr') + } + + connectFalcon(event){ + return this.connect('falcon') + } + async connect(providerKey, manual) { if (this.props.network && !this.connected()) { return this.setState({ diff --git a/src/utils/FalconSignerProvider.mjs b/src/utils/FalconSignerProvider.mjs index c1db8a284..a2c64727f 100644 --- a/src/utils/FalconSignerProvider.mjs +++ b/src/utils/FalconSignerProvider.mjs @@ -6,14 +6,6 @@ export default class FalconSignerProvider extends SignerProvider { return this.provider.connect(chainId) } - getSigner(network){ - const { chainId } = network - return this.provider.getOfflineSigner(chainId) - } - - getKey(network){ - } - suggestChain(network){ return this.provider.importZone(network.suggestChain()) } diff --git a/src/utils/Wallet.mjs b/src/utils/Wallet.mjs index 36946b360..82439794c 100644 --- a/src/utils/Wallet.mjs +++ b/src/utils/Wallet.mjs @@ -61,7 +61,7 @@ class Wallet { getIsNanoLedger() { if(!this.key) return false - return this.key.isNanoLedger; + return this.key.isNanoLedger || this.key.isHardware; } } From 92d96a172fbe7fbade3b5c0175bc88dd09f31beb Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 24 Aug 2022 17:40:08 +0100 Subject: [PATCH 593/862] Handle missing chain and enable --- src/components/App.js | 4 +++- src/utils/KeplrSignerProvider.mjs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index f6ce9c696..e41975131 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -195,15 +195,17 @@ class App extends React.Component { const { network } = this.props if (!network || !provider) return + let key try { await provider.enable(network) + key = await provider.getKey(network); } catch (e) { console.log(e.message, e) await provider.suggestChain(network) + key = await provider.getKey(network); } try { const offlineSigner = await provider.getSigner(network) - const key = await provider.getKey(network); const wallet = new Wallet(network, offlineSigner, key) const signingClient = wallet.signingClient signingClient.registry.register("/cosmos.authz.v1beta1.MsgGrant", MsgGrant) diff --git a/src/utils/KeplrSignerProvider.mjs b/src/utils/KeplrSignerProvider.mjs index 652d9bbc5..e01431695 100644 --- a/src/utils/KeplrSignerProvider.mjs +++ b/src/utils/KeplrSignerProvider.mjs @@ -7,7 +7,7 @@ export default class KeplrSignerProvider extends SignerProvider { sign: { preferNoSetFee: true } }) } - super.enable(network) + return super.enable(network) } setOptions(options){ From 4a510349f3a9e6df4ffe1e22f272fa785e52b51c Mon Sep 17 00:00:00 2001 From: SATMan <101592073+satman81@users.noreply.github.com> Date: Sat, 27 Aug 2022 11:28:28 +0200 Subject: [PATCH 594/862] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5abf6da94..cfba19abc 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ A script is also provided which allows a validator to automatically search their Becoming an operator is extremely easy. You need to do three things: -### Setup a bot wallet +### Setup a hot wallet Generate a new hot wallet you will use to automatically carry out the staking transactions. The mnemonic will need to be provided to the script so **use a dedicated wallet and only keep enough funds for transaction fees**. The ONLY menmonic required here is for the hot wallet, do not put your validator operator mnemonic anywhere. From 7d7007a49fd40cac214912b4b164d8ba8e2a97ca Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 29 Aug 2022 19:07:13 +0100 Subject: [PATCH 595/862] Signer provider meta --- src/components/App.js | 58 ++++++++++++++---------------- src/utils/FalconSignerProvider.mjs | 7 ++++ src/utils/KeplrSignerProvider.mjs | 3 ++ src/utils/SignerProvider.mjs | 4 +++ 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index e41975131..1dd78ee43 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -124,20 +124,13 @@ class App extends React.Component { } signerProviders(){ - return [{ - key: 'keplr', - label: 'Keplr Extension', - provider: window.keplr && new KeplrSignerProvider(window.keplr) - }, { - key: 'falcon', - label: 'Falcon Wallet', - provider: window.falcon && new FalconSignerProvider(window.falcon) - }] + return [ + new KeplrSignerProvider(window.keplr), + new FalconSignerProvider(window.falcon) + ] } getSignerProvider(providerKey){ - if(!providerKey) providerKey = localStorage.getItem('connected') - return providerKey && this.signerProviders().find(el => el.key === providerKey) } @@ -170,21 +163,25 @@ class App extends React.Component { }) } - const signerProvider = this.getSignerProvider(providerKey) + let storedKey = localStorage.getItem('connected') + if(storedKey === '1'){ + storedKey = 'keplr' + localStorage.setItem('connected', storedKey) + } + + const signerProvider = this.getSignerProvider(providerKey || storedKey) if(!signerProvider) return providerKey = signerProvider.key - if (manual && !signerProvider.provider) { + if (manual && !signerProvider.connected()) { return this.setState({ providerError: providerKey }) } - const provider = signerProvider.provider - - if (localStorage.getItem('connected') !== providerKey) { + if (storedKey !== providerKey) { if (manual) { localStorage.setItem('connected', providerKey) } else { @@ -193,19 +190,19 @@ class App extends React.Component { } const { network } = this.props - if (!network || !provider) return + if (!network || !signerProvider.connected()) return let key try { - await provider.enable(network) - key = await provider.getKey(network); + await signerProvider.enable(network) + key = await signerProvider.getKey(network); } catch (e) { console.log(e.message, e) - await provider.suggestChain(network) - key = await provider.getKey(network); + await signerProvider.suggestChain(network) + key = await signerProvider.getKey(network); } try { - const offlineSigner = await provider.getSigner(network) + const offlineSigner = await signerProvider.getSigner(network) const wallet = new Wallet(network, offlineSigner, key) const signingClient = wallet.signingClient signingClient.registry.register("/cosmos.authz.v1beta1.MsgGrant", MsgGrant) @@ -214,9 +211,10 @@ class App extends React.Component { const address = await wallet.getAddress(); this.setState({ - address: address, - wallet: wallet, - signingClient: signingClient, + address, + wallet, + signerProvider, + signingClient, error: false }) } catch (e) { @@ -698,17 +696,15 @@ class App extends React.Component { ) : ( - <> - this.connect('keplr', true)} disabled={!window.keplr}>Connect Keplr Extension - this.connect('falcon', true)} disabled={!window.falcon}>Connect Falcon Wallet - - + this.signerProviders().map(provider => { + return this.connect(provider.key, true)} disabled={!provider.connected()}>Connect {provider.label} + }) )} this.setState({ showAddressModal: true })}>Saved Addresses {this.state.address && ( <> - Disconnect {this.getSignerProvider()?.label} + Disconnect {this.state.signerProvider?.label} )} diff --git a/src/utils/FalconSignerProvider.mjs b/src/utils/FalconSignerProvider.mjs index a2c64727f..f9480d7dd 100644 --- a/src/utils/FalconSignerProvider.mjs +++ b/src/utils/FalconSignerProvider.mjs @@ -1,6 +1,13 @@ import SignerProvider from "./SignerProvider.mjs"; export default class FalconSignerProvider extends SignerProvider { + key = 'falcon' + label = 'Falcon Wallet' + + connected(){ + return !!this.provider + } + enable(network){ const { chainId } = network return this.provider.connect(chainId) diff --git a/src/utils/KeplrSignerProvider.mjs b/src/utils/KeplrSignerProvider.mjs index e01431695..56bea8d5c 100644 --- a/src/utils/KeplrSignerProvider.mjs +++ b/src/utils/KeplrSignerProvider.mjs @@ -1,6 +1,9 @@ import SignerProvider from "./SignerProvider.mjs" export default class KeplrSignerProvider extends SignerProvider { + key = 'keplr' + label = 'Keplr Extension' + enable(network){ if (network.gasPricePrefer) { this.setOptions({ diff --git a/src/utils/SignerProvider.mjs b/src/utils/SignerProvider.mjs index 490451fd1..0cf9b60b5 100644 --- a/src/utils/SignerProvider.mjs +++ b/src/utils/SignerProvider.mjs @@ -3,6 +3,10 @@ export default class SignerProvider { this.provider = provider } + connected(){ + return !!this.provider + } + enable(network){ const { chainId } = network return this.provider.enable(chainId) From 281c1f017d3be6c8e5d4a8ee6f903e81e0a933db Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 29 Aug 2022 19:16:11 +0100 Subject: [PATCH 596/862] Remove useless override --- src/utils/FalconSignerProvider.mjs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/utils/FalconSignerProvider.mjs b/src/utils/FalconSignerProvider.mjs index f9480d7dd..82761b9f8 100644 --- a/src/utils/FalconSignerProvider.mjs +++ b/src/utils/FalconSignerProvider.mjs @@ -4,10 +4,6 @@ export default class FalconSignerProvider extends SignerProvider { key = 'falcon' label = 'Falcon Wallet' - connected(){ - return !!this.provider - } - enable(network){ const { chainId } = network return this.provider.connect(chainId) From 2c8ce35f375428aa918462bd6e42118e38ecabb0 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 1 Sep 2022 17:16:55 +0100 Subject: [PATCH 597/862] Catch wallet errors and label wallet in select --- src/components/App.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 1dd78ee43..fe3e02efe 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -198,8 +198,14 @@ class App extends React.Component { key = await signerProvider.getKey(network); } catch (e) { console.log(e.message, e) - await signerProvider.suggestChain(network) - key = await signerProvider.getKey(network); + try { + await signerProvider.suggestChain(network) + key = await signerProvider.getKey(network); + } catch (e) { + return this.setState({ + error: `Failed to connect to ${signerProvider?.label || 'signer'}: ${e.message}` + }) + } } try { const offlineSigner = await signerProvider.getSigner(network) @@ -629,7 +635,7 @@ class App extends React.Component { ) : ( From 1e18f432dabcc1a3a169503c8993449cbbc7937a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 21 Sep 2022 15:48:43 +0100 Subject: [PATCH 640/862] Fix variable assignment for generic grants --- src/converters/Authz.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/converters/Authz.mjs b/src/converters/Authz.mjs index c21f9ff83..bf843d9bd 100644 --- a/src/converters/Authz.mjs +++ b/src/converters/Authz.mjs @@ -53,7 +53,7 @@ export function createAuthzAminoConverters() { "/cosmos.authz.v1beta1.MsgGrant": { aminoType: "cosmos-sdk/MsgGrant", toAmino: ({ granter, grantee, grant }) => { - converter = grantConverter[grant.authorization.typeUrl] + const converter = grantConverter[grant.authorization.typeUrl] return { granter, grantee, @@ -67,8 +67,8 @@ export function createAuthzAminoConverters() { } }, fromAmino: ({ granter, grantee, grant }) => { - protoType = Object.keys(grantConverter).find(type => grantConverter[type].aminoType === grant.authorization.type) - converter = grantConverter[protoType] + const protoType = Object.keys(grantConverter).find(type => grantConverter[type].aminoType === grant.authorization.type) + const converter = grantConverter[protoType] return { granter, grantee, From d6c964d716644c52fb5a820c65bae7e2c2e8beec Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 21 Sep 2022 17:23:25 +0100 Subject: [PATCH 641/862] Handle no result from autostaking --- src/autostake/index.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 355624220..28e601a56 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -39,7 +39,7 @@ export default function Autostake(mnemonic, opts) { let lastResult if (Array.isArray(results)) { lastResult = results[results.length - 1] - health.log(`Autostake ${lastResult.didSucceed() ? 'completed' : 'failed'} after ${results.length} attempt(s)`) + health.log(`Autostake ${lastResult?.didSucceed() ? 'completed' : 'failed'} after ${results.length} attempt(s)`) results.forEach((result, index) => { health.log(`Attempt ${index + 1}:`) logSummary(health, result) From 81012fbf05c25bfcc0f753857b1c7f91588362d7 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 21 Sep 2022 20:51:29 +0100 Subject: [PATCH 642/862] Fix TX poll failures --- src/utils/SigningClient.mjs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 92427d239..5d99699d5 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -164,19 +164,17 @@ function SigningClient(network, signer) { }) const result = parseTxResult(response.data.tx_response) assertIsDeliverTxSuccess(result) - return new Promise((resolve, reject) => - pollForTx(result.transactionHash).then( - (value) => { - clearTimeout(txPollTimeout); - assertIsDeliverTxSuccess(value) - resolve(value); - }, - (error) => { - clearTimeout(txPollTimeout); - reject(error); - }, - ), - ); + return pollForTx(result.transactionHash).then( + (value) => { + clearTimeout(txPollTimeout); + assertIsDeliverTxSuccess(value) + return value + }, + (error) => { + clearTimeout(txPollTimeout); + return error + }, + ) } async function sign(address, messages, memo, fee){ From 61794b9f8e3b1524313ba788c0029ec3b3a4fd64 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 21 Sep 2022 22:58:34 +0100 Subject: [PATCH 643/862] Refactor retry handling --- src/autostake/index.mjs | 55 ++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 28e601a56..9f94bc674 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -36,16 +36,13 @@ export default function Autostake(mnemonic, opts) { const health = new Health(data.healthCheck, { dryRun: opts.dryRun }) health.started('⚛') const results = await runWithRetry(data, health) - let lastResult - if (Array.isArray(results)) { - lastResult = results[results.length - 1] - health.log(`Autostake ${lastResult?.didSucceed() ? 'completed' : 'failed'} after ${results.length} attempt(s)`) - results.forEach((result, index) => { - health.log(`Attempt ${index + 1}:`) - logSummary(health, result) - }) - } - if (lastResult?.didSucceed() || results === true) { + const { success } = results[results.length - 1] || {} + health.log(`Autostake ${success ? 'completed' : 'failed'} after ${results.length} attempt(s)`) + results.forEach(({networkRunner, error}, index) => { + health.log(`Attempt ${index + 1}:`) + logSummary(health, networkRunner, error) + }) + if (success) { return health.success('Autostake finished') } else { return health.failed('Autostake failed') @@ -58,26 +55,26 @@ export default function Autostake(mnemonic, opts) { async function runWithRetry(data, health, retries, runners) { retries = retries || 0 runners = runners || [] - const lastRunner = runners[runners.length - 1] const maxRetries = data.autostake?.retries ?? 2 + let { failedAddresses } = runners[runners.length - 1] || {} let networkRunner, error try { networkRunner = await getNetworkRunner(data) - if (!networkRunner) return true - - runners.push(networkRunner) - const limitAddresses = lastRunner?.failedAddresses() - await networkRunner.run(limitAddresses?.length && limitAddresses) - if (networkRunner.didSucceed()) { - await logResults(health, networkRunner) + if (!networkRunner){ + runners.push({ success: true }) return runners - } else { + } + + await networkRunner.run(failedAddresses?.length && failedAddresses) + if (!networkRunner.didSucceed()) { error = networkRunner.error?.message + failedAddresses = networkRunner.failedAddresses() } } catch (e) { error = e.message } - if (retries < maxRetries && !networkRunner?.forceFail) { + runners.push({ success: networkRunner?.didSucceed(), networkRunner, error, failedAddresses }) + if (!networkRunner?.didSucceed() && !networkRunner?.forceFail && retries < maxRetries) { await logResults(health, networkRunner, error, `Failed attempt ${retries + 1}/${maxRetries + 1}, retrying in 30 seconds...`) await new Promise(r => setTimeout(r, 30 * 1000)); return await runWithRetry(data, health, retries + 1, runners) @@ -89,20 +86,22 @@ export default function Autostake(mnemonic, opts) { function logResults(health, networkRunner, error, message) { if(networkRunner){ health.addLogs(networkRunner.queryErrors()) - logSummary(health, networkRunner) } - if (error) health.log(`Failed with error: ${error}`) + logSummary(health, networkRunner, error) if (message) health.log(message) return health.sendLog() } - function logSummary(health, networkRunner) { - const { results } = networkRunner - const errors = results.filter(result => result.error) - health.log(`Sent ${results.length - errors.length}/${results.length} transactions`) - for (let [index, result] of results.entries()) { - health.log(`TX ${index + 1}:`, result.message); + function logSummary(health, networkRunner, error) { + if(networkRunner){ + const { results } = networkRunner + const errors = results.filter(result => result.error) + health.log(`Sent ${results.length - errors.length}/${results.length} transactions`) + for (let [index, result] of results.entries()) { + health.log(`TX ${index + 1}:`, result.message); + } } + if (error) health.log(`Failed with error: ${error}`) } async function getNetworkRunner(data) { From 8417ed2695bdad2379afdfd5098bdbefcdc65c15 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 21 Sep 2022 23:19:39 +0100 Subject: [PATCH 644/862] Handle skipped quietly --- src/autostake/index.mjs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 9f94bc674..9b0eb5b20 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -36,13 +36,15 @@ export default function Autostake(mnemonic, opts) { const health = new Health(data.healthCheck, { dryRun: opts.dryRun }) health.started('⚛') const results = await runWithRetry(data, health) - const { success } = results[results.length - 1] || {} - health.log(`Autostake ${success ? 'completed' : 'failed'} after ${results.length} attempt(s)`) - results.forEach(({networkRunner, error}, index) => { - health.log(`Attempt ${index + 1}:`) - logSummary(health, networkRunner, error) - }) - if (success) { + const { success, skipped } = results[results.length - 1] || {} + if(!skipped){ + health.log(`Autostake ${success ? 'completed' : 'failed'} after ${results.length} attempt(s)`) + results.forEach(({networkRunner, error}, index) => { + health.log(`Attempt ${index + 1}:`) + logSummary(health, networkRunner, error) + }) + } + if (success || skipped) { return health.success('Autostake finished') } else { return health.failed('Autostake failed') @@ -61,7 +63,7 @@ export default function Autostake(mnemonic, opts) { try { networkRunner = await getNetworkRunner(data) if (!networkRunner){ - runners.push({ success: true }) + runners.push({ skipped: true }) return runners } From 24e5f54f47e97bb7f5a9a57cbf0483b20cc4b09f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 22 Sep 2022 22:20:33 +0100 Subject: [PATCH 645/862] Initial implementation --- package-lock.json | 1893 ++++++++++++++++++++++- package.json | 3 + src/components/App.js | 2 + src/utils/KeplrMobileSignerProvider.mjs | 50 + 4 files changed, 1892 insertions(+), 56 deletions(-) create mode 100644 src/utils/KeplrMobileSignerProvider.mjs diff --git a/package-lock.json b/package-lock.json index b41331668..d9661e7a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,10 +13,13 @@ "@cosmjs/proto-signing": "^0.28.1", "@cosmjs/stargate": "^0.28.1", "@ethersproject/wallet": "^5.6.2", + "@keplr-wallet/wc-client": "^0.11.3", + "@keplr-wallet/wc-qrcode-modal": "^0.11.3", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^13.5.0", "@tharsis/address-converter": "^0.1.8", + "@walletconnect/client": "^1.8.0", "axios": "^0.26.0", "bootstrap": "^5.1.3", "buffer": "^6.0.3", @@ -515,6 +518,74 @@ "xstream": "^11.14.0" } }, + "node_modules/@cosmjs/launchpad": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/launchpad/-/launchpad-0.24.1.tgz", + "integrity": "sha512-syqVGKRH6z1vw4DdAJOSu4OgUXJdkXQozqvDde0cXYwnvhb7EXGSg5CTtp+2GqTBJuNVfMZ2DSvrC2Ig8cWBQQ==", + "dependencies": { + "@cosmjs/crypto": "^0.24.1", + "@cosmjs/encoding": "^0.24.1", + "@cosmjs/math": "^0.24.1", + "@cosmjs/utils": "^0.24.1", + "axios": "^0.21.1", + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@cosmjs/launchpad/node_modules/@cosmjs/crypto": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.24.1.tgz", + "integrity": "sha512-GPhaWmQO06mXldKj/b+oKF5o3jMNfRKpAw+Q8XQhrD7ItinVPDMu8Xgl6frUXWTUdgpYwqpvqOcpm85QUsYV0Q==", + "dependencies": { + "@cosmjs/encoding": "^0.24.1", + "@cosmjs/math": "^0.24.1", + "@cosmjs/utils": "^0.24.1", + "bip39": "^3.0.2", + "bn.js": "^4.11.8", + "elliptic": "^6.5.3", + "js-sha3": "^0.8.0", + "libsodium-wrappers": "^0.7.6", + "pbkdf2": "^3.1.1", + "ripemd160": "^2.0.2", + "sha.js": "^2.4.11", + "unorm": "^1.5.0" + } + }, + "node_modules/@cosmjs/launchpad/node_modules/@cosmjs/encoding": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.24.1.tgz", + "integrity": "sha512-PMr+gaXAuM0XgjeXwB1zdX1QI0t+PgVhbmjgI/RSgswDzdExNH97qUopecL0/HG3p64vhIT/6ZjXYYTljZL7WA==", + "dependencies": { + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" + } + }, + "node_modules/@cosmjs/launchpad/node_modules/@cosmjs/math": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.24.1.tgz", + "integrity": "sha512-eBQk8twgzmpHFCVkoNjTZhsZwWRbR+JXt0FhjXJoD85SBm4K8b2OnOyTg68uPHVKOJjLRwzyRVYgMrg5TBVgwQ==", + "dependencies": { + "bn.js": "^4.11.8" + } + }, + "node_modules/@cosmjs/launchpad/node_modules/@cosmjs/utils": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.24.1.tgz", + "integrity": "sha512-VA3WFx1lMFb7esp9BqHWkDgMvHoA3D9w+uDRvWhVRpUpDc7RYHxMbWExASjz+gNblTCg556WJGzF64tXnf9tdQ==" + }, + "node_modules/@cosmjs/launchpad/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/@cosmjs/launchpad/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/@cosmjs/math": { "version": "0.28.7", "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.7.tgz", @@ -1345,6 +1416,50 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@iov/crypto": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@iov/crypto/-/crypto-2.1.0.tgz", + "integrity": "sha512-jnb4XuK50admolm7fBxOcxfAW2TO+wYrZlhDWiMETItY/Y5gNNa1zaDSO2wNIjjfGng+8nQ1yqnNhqy7busV2Q==", + "dependencies": { + "@iov/encoding": "^2.1.0", + "bip39": "^3.0.2", + "bn.js": "^4.11.8", + "elliptic": "^6.4.0", + "js-sha3": "^0.8.0", + "libsodium-wrappers": "^0.7.6", + "pbkdf2": "^3.0.16", + "ripemd160": "^2.0.2", + "sha.js": "^2.4.11", + "type-tagger": "^1.0.0", + "unorm": "^1.5.0" + } + }, + "node_modules/@iov/crypto/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/@iov/encoding": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@iov/encoding/-/encoding-2.1.0.tgz", + "integrity": "sha512-5IOdLO7Xg/uRykuiCqeMYghQ3IjWDtGxv7NTWXkgpHuna0aewx43mRpT2NPCpOZd1tpuorDtQ7/zbDNRaIIF/w==", + "dependencies": { + "base64-js": "^1.3.0", + "bech32": "^1.1.3", + "bn.js": "^4.11.8", + "readonly-date": "^1.0.0" + } + }, + "node_modules/@iov/encoding/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/@iov/utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@iov/utils/-/utils-2.0.2.tgz", + "integrity": "sha512-4D8MEvTcFc/DVy5q25vHxRItmgJyeX85dixMH+MxdKr+yy71h3sYk+sVBEIn70uqGP7VqAJkGOPNFs08/XYELw==" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -1412,6 +1527,256 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@keplr-wallet/common": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/common/-/common-0.11.3.tgz", + "integrity": "sha512-3izIyRX9nXTrc+u+24yYpl2qiLyHaweqiXI3fk2SHucWWicPqksSw5wkmuA+4Bef3/CUQPTgtcufDgPyBB+bNw==", + "dependencies": { + "@keplr-wallet/crypto": "0.11.3", + "buffer": "^6.0.3", + "delay": "^4.4.0" + } + }, + "node_modules/@keplr-wallet/crypto": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/crypto/-/crypto-0.11.3.tgz", + "integrity": "sha512-BWSEU97qBu2M7DmSWPFxG+eL6k8UHQe230MBkDzLpVQsouFeJbwsip0mTxxJWaZXMuhyK9gaMsAQSK4YkHyI4Q==", + "dependencies": { + "bip32": "^2.0.6", + "bip39": "^3.0.3", + "bs58check": "^2.1.2", + "buffer": "^6.0.3", + "crypto-js": "^4.0.0", + "elliptic": "^6.5.3", + "sha.js": "^2.4.11" + } + }, + "node_modules/@keplr-wallet/provider": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/provider/-/provider-0.11.3.tgz", + "integrity": "sha512-4KWRiJTyIBW+qOOYP2CN/IIvGMLsBgRi1D5/FT6q7EaYkIR/peJlKSR6fJukmQHyI/fmkvGH2+Td9fhZsVv5mA==", + "dependencies": { + "@cosmjs/launchpad": "^0.24.0-alpha.25", + "@cosmjs/proto-signing": "^0.24.0-alpha.25", + "@keplr-wallet/router": "0.11.3", + "@keplr-wallet/types": "0.11.3", + "buffer": "^6.0.3", + "deepmerge": "^4.2.2", + "long": "^4.0.0", + "secretjs": "0.17.7" + } + }, + "node_modules/@keplr-wallet/provider/node_modules/@cosmjs/proto-signing": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.24.1.tgz", + "integrity": "sha512-/rnyNx+FlG6b6O+igsb42eMN1/RXY+pTrNnAE8/YZaRloP9A6MXiTMO5JdYSTcjaD0mEVhejiy96bcyflKYXBg==", + "dependencies": { + "@cosmjs/launchpad": "^0.24.1", + "long": "^4.0.0", + "protobufjs": "~6.10.2" + } + }, + "node_modules/@keplr-wallet/provider/node_modules/@types/node": { + "version": "13.13.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", + "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" + }, + "node_modules/@keplr-wallet/provider/node_modules/protobufjs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.3.tgz", + "integrity": "sha512-yvAslS0hNdBhlSKckI4R1l7wunVilX66uvrjzE4MimiAt7/qw1nLpMhZrn/ObuUTM/c3Xnfl01LYMdcSJe6dwg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/@keplr-wallet/router": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/router/-/router-0.11.3.tgz", + "integrity": "sha512-2Kh5+8ZqUY4l+Gf3mEl8q6vDeb7ino1SnCHA0bl5+ny9039zQ8AcIHjrZStX64nTPYuM/G60HRmNKhG51vMubQ==" + }, + "node_modules/@keplr-wallet/types": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/types/-/types-0.11.3.tgz", + "integrity": "sha512-OA0OoimsUI22iTXgup3BAlLt+FJdGKniJqzJyQji+rqfXqreBT+Ie9c5Ng7qY81+RO/nJ5SZfa1gIns+3A0YxA==", + "dependencies": { + "@cosmjs/launchpad": "^0.24.0-alpha.25", + "@cosmjs/proto-signing": "^0.24.0-alpha.25", + "axios": "^0.27.2", + "long": "^4.0.0", + "secretjs": "0.17.7" + } + }, + "node_modules/@keplr-wallet/types/node_modules/@cosmjs/proto-signing": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.24.1.tgz", + "integrity": "sha512-/rnyNx+FlG6b6O+igsb42eMN1/RXY+pTrNnAE8/YZaRloP9A6MXiTMO5JdYSTcjaD0mEVhejiy96bcyflKYXBg==", + "dependencies": { + "@cosmjs/launchpad": "^0.24.1", + "long": "^4.0.0", + "protobufjs": "~6.10.2" + } + }, + "node_modules/@keplr-wallet/types/node_modules/@types/node": { + "version": "13.13.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", + "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" + }, + "node_modules/@keplr-wallet/types/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/@keplr-wallet/types/node_modules/protobufjs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.3.tgz", + "integrity": "sha512-yvAslS0hNdBhlSKckI4R1l7wunVilX66uvrjzE4MimiAt7/qw1nLpMhZrn/ObuUTM/c3Xnfl01LYMdcSJe6dwg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/@keplr-wallet/wc-client": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-client/-/wc-client-0.11.3.tgz", + "integrity": "sha512-u7A+c5XL8+8Qa9g6Qsn6PCZBYBtOgNgIBIqpbLFpbxzBCIHH8GE+PRp1AVNsU8R5f7HHyGWHO1T14sTrdUpGEQ==", + "dependencies": { + "@cosmjs/launchpad": "^0.24.0-alpha.25", + "@cosmjs/proto-signing": "^0.24.0-alpha.25", + "@keplr-wallet/common": "0.11.3", + "@keplr-wallet/provider": "0.11.3", + "@keplr-wallet/types": "0.11.3", + "@walletconnect/types": "^1.6.4", + "@walletconnect/utils": "^1.6.4", + "buffer": "^6.0.3", + "deepmerge": "^4.2.2", + "secretjs": "0.17.7" + } + }, + "node_modules/@keplr-wallet/wc-client/node_modules/@cosmjs/proto-signing": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.24.1.tgz", + "integrity": "sha512-/rnyNx+FlG6b6O+igsb42eMN1/RXY+pTrNnAE8/YZaRloP9A6MXiTMO5JdYSTcjaD0mEVhejiy96bcyflKYXBg==", + "dependencies": { + "@cosmjs/launchpad": "^0.24.1", + "long": "^4.0.0", + "protobufjs": "~6.10.2" + } + }, + "node_modules/@keplr-wallet/wc-client/node_modules/@types/node": { + "version": "13.13.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", + "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" + }, + "node_modules/@keplr-wallet/wc-client/node_modules/protobufjs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.3.tgz", + "integrity": "sha512-yvAslS0hNdBhlSKckI4R1l7wunVilX66uvrjzE4MimiAt7/qw1nLpMhZrn/ObuUTM/c3Xnfl01LYMdcSJe6dwg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/@keplr-wallet/wc-qrcode-modal": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-qrcode-modal/-/wc-qrcode-modal-0.11.3.tgz", + "integrity": "sha512-dif+OqViavYny+r3PO5s4+zM85b8Gc2PTqQvoeBGVaKzk4tLYy3i4ICDrOku6Z2nOWrZg6Wl5iDlGRxQSfnlNA==", + "dependencies": { + "@walletconnect/browser-utils": "^1.6.5", + "@walletconnect/types": "^1.6.5", + "qrcode.react": "^1.0.1", + "react": "^16.14.0", + "react-dom": "^16.14.0" + } + }, + "node_modules/@keplr-wallet/wc-qrcode-modal/node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@keplr-wallet/wc-qrcode-modal/node_modules/react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + }, + "peerDependencies": { + "react": "^16.14.0" + } + }, + "node_modules/@keplr-wallet/wc-qrcode-modal/node_modules/scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "node_modules/@lezer/common": { "version": "0.15.12", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz", @@ -3310,6 +3675,11 @@ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, + "node_modules/@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -3364,6 +3734,179 @@ "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", "integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=" }, + "node_modules/@walletconnect/browser-utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/browser-utils/-/browser-utils-1.8.0.tgz", + "integrity": "sha512-Wcqqx+wjxIo9fv6eBUFHPsW1y/bGWWRboni5dfD8PtOmrihrEpOCmvRJe4rfl7xgJW8Ea9UqKEaq0bIRLHlK4A==", + "dependencies": { + "@walletconnect/safe-json": "1.0.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/window-getters": "1.0.0", + "@walletconnect/window-metadata": "1.0.0", + "detect-browser": "5.2.0" + } + }, + "node_modules/@walletconnect/client": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/client/-/client-1.8.0.tgz", + "integrity": "sha512-svyBQ14NHx6Cs2j4TpkQaBI/2AF4+LXz64FojTjMtV4VMMhl81jSO1vNeg+yYhQzvjcGH/GpSwixjyCW0xFBOQ==", + "dependencies": { + "@walletconnect/core": "^1.8.0", + "@walletconnect/iso-crypto": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "node_modules/@walletconnect/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-1.8.0.tgz", + "integrity": "sha512-aFTHvEEbXcZ8XdWBw6rpQDte41Rxwnuk3SgTD8/iKGSRTni50gI9S3YEzMj05jozSiOBxQci4pJDMVhIUMtarw==", + "dependencies": { + "@walletconnect/socket-transport": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "node_modules/@walletconnect/crypto": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/crypto/-/crypto-1.0.2.tgz", + "integrity": "sha512-+OlNtwieUqVcOpFTvLBvH+9J9pntEqH5evpINHfVxff1XIgwV55PpbdvkHu6r9Ib4WQDOFiD8OeeXs1vHw7xKQ==", + "dependencies": { + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/environment": "^1.0.0", + "@walletconnect/randombytes": "^1.0.2", + "aes-js": "^3.1.2", + "hash.js": "^1.1.7" + } + }, + "node_modules/@walletconnect/crypto/node_modules/aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "node_modules/@walletconnect/encoding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/encoding/-/encoding-1.0.1.tgz", + "integrity": "sha512-8opL2rs6N6E3tJfsqwS82aZQDL3gmupWUgmvuZ3CGU7z/InZs3R9jkzH8wmYtpbq0sFK3WkJkQRZFFk4BkrmFA==", + "dependencies": { + "is-typedarray": "1.0.0", + "typedarray-to-buffer": "3.1.5" + } + }, + "node_modules/@walletconnect/environment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.0.tgz", + "integrity": "sha512-4BwqyWy6KpSvkocSaV7WR3BlZfrxLbJSLkg+j7Gl6pTDE+U55lLhJvQaMuDVazXYxcjBsG09k7UlH7cGiUI5vQ==" + }, + "node_modules/@walletconnect/iso-crypto": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/iso-crypto/-/iso-crypto-1.8.0.tgz", + "integrity": "sha512-pWy19KCyitpfXb70hA73r9FcvklS+FvO9QUIttp3c2mfW8frxgYeRXfxLRCIQTkaYueRKvdqPjbyhPLam508XQ==", + "dependencies": { + "@walletconnect/crypto": "^1.0.2", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "node_modules/@walletconnect/jsonrpc-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.1.tgz", + "integrity": "sha512-+6coTtOuChCqM+AoYyi4Q83p9l/laI6NvuM2/AHaZFuf0gT0NjW7IX2+86qGyizn7Ptq4AYZmfxurAxTnhefuw==", + "dependencies": { + "keyvaluestorage-interface": "^1.0.0" + } + }, + "node_modules/@walletconnect/jsonrpc-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.3.tgz", + "integrity": "sha512-3yb49bPk16MNLk6uIIHPSHQCpD6UAo1OMOx1rM8cW/MPEAYAzrSW5hkhG7NEUwX9SokRIgnZK3QuQkiyNzBMhQ==", + "dependencies": { + "@walletconnect/environment": "^1.0.0", + "@walletconnect/jsonrpc-types": "^1.0.1" + } + }, + "node_modules/@walletconnect/randombytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/randombytes/-/randombytes-1.0.2.tgz", + "integrity": "sha512-ivgOtAyqQnN0rLQmOFPemsgYGysd/ooLfaDA/ACQ3cyqlca56t3rZc7pXfqJOIETx/wSyoF5XbwL+BqYodw27A==", + "dependencies": { + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/environment": "^1.0.0", + "randombytes": "^2.1.0" + } + }, + "node_modules/@walletconnect/safe-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.0.tgz", + "integrity": "sha512-QJzp/S/86sUAgWY6eh5MKYmSfZaRpIlmCJdi5uG4DJlKkZrHEF7ye7gA+VtbVzvTtpM/gRwO2plQuiooIeXjfg==" + }, + "node_modules/@walletconnect/socket-transport": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/socket-transport/-/socket-transport-1.8.0.tgz", + "integrity": "sha512-5DyIyWrzHXTcVp0Vd93zJ5XMW61iDM6bcWT4p8DTRfFsOtW46JquruMhxOLeCOieM4D73kcr3U7WtyR4JUsGuQ==", + "dependencies": { + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0", + "ws": "7.5.3" + } + }, + "node_modules/@walletconnect/socket-transport/node_modules/ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@walletconnect/types": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-1.8.0.tgz", + "integrity": "sha512-Cn+3I0V0vT9ghMuzh1KzZvCkiAxTq+1TR2eSqw5E5AVWfmCtECFkVZBP6uUJZ8YjwLqXheI+rnjqPy7sVM4Fyg==" + }, + "node_modules/@walletconnect/utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-1.8.0.tgz", + "integrity": "sha512-zExzp8Mj1YiAIBfKNm5u622oNw44WOESzo6hj+Q3apSMIb0Jph9X3GDIdbZmvVZsNPxWDL7uodKgZcCInZv2vA==", + "dependencies": { + "@walletconnect/browser-utils": "^1.8.0", + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/jsonrpc-utils": "^1.0.3", + "@walletconnect/types": "^1.8.0", + "bn.js": "4.11.8", + "js-sha3": "0.8.0", + "query-string": "6.13.5" + } + }, + "node_modules/@walletconnect/utils/node_modules/bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "node_modules/@walletconnect/window-getters": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.0.tgz", + "integrity": "sha512-xB0SQsLaleIYIkSsl43vm8EwETpBzJ2gnzk7e0wMF3ktqiTGS6TFHxcprMl5R44KKh4tCcHCJwolMCaDSwtAaA==" + }, + "node_modules/@walletconnect/window-metadata": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.0.tgz", + "integrity": "sha512-9eFvmJxIKCC3YWOL97SgRkKhlyGXkrHwamfechmqszbypFspaSk+t2jQXAEU7YClHF6Qjw5eYOmy1//zFi9/GA==", + "dependencies": { + "@walletconnect/window-getters": "^1.0.0" + } + }, "node_modules/abortcontroller-polyfill": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", @@ -3455,6 +3998,11 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -3519,6 +4067,47 @@ "node": ">=0.6" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bip32": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", + "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "dependencies": { + "@types/node": "10.12.18", + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.1.3", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bip39": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz", + "integrity": "sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==", + "dependencies": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" + } + }, + "node_modules/bip39/node_modules/@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" + }, "node_modules/blakejs": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", @@ -3680,6 +4269,16 @@ "base-x": "^3.0.2" } }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -3759,6 +4358,14 @@ "node": ">=4" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -3772,7 +4379,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -3805,6 +4411,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -3892,7 +4509,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -3905,7 +4521,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -3929,6 +4544,14 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, "node_modules/crypto-addr-codec": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz", @@ -3984,6 +4607,11 @@ "node": "*" } }, + "node_modules/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, "node_modules/css": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", @@ -4074,6 +4702,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" }, + "node_modules/curve25519-js": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/curve25519-js/-/curve25519-js-0.0.4.tgz", + "integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==" + }, "node_modules/debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -4109,6 +4742,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -4120,6 +4761,25 @@ "node": ">= 0.4" } }, + "node_modules/delay": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/delay/-/delay-4.4.1.tgz", + "integrity": "sha512-aL3AhqtfhOlT/3ai6sWXeqwnw63ATNpnUiN4HL7x9q+My5QtHlO3OIkasmug9LKzpheLdmUKGRKnYXYAS7FQkQ==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", @@ -4138,6 +4798,11 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/detect-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.0.tgz", + "integrity": "sha512-tr7XntDAu50BVENgQfajMLzacmSe34D+qZc4zjnniz0ZVuw/TZcLcyxHQjYpJTM36sGEkZZlYLnIM1hH7alTMA==" + }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -4710,8 +5375,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", @@ -4737,6 +5401,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -4780,6 +5449,19 @@ } } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -4904,7 +5586,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, "dependencies": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -4918,7 +5599,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -5143,6 +5823,11 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-core-module": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -5181,6 +5866,11 @@ "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", "dev": true }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "node_modules/iserror": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/iserror/-/iserror-0.0.2.tgz", @@ -5291,6 +5981,65 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/js-crypto-env": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-crypto-env/-/js-crypto-env-0.3.2.tgz", + "integrity": "sha512-F1uHiCkSOo36qBuuZABA4sBf+xeFBzhJZ0Sd7af8FAruszIhm1Xxv+Zr5Ne90Zlh7/fnxCsrdkj0N8f0a3lVlQ==" + }, + "node_modules/js-crypto-hash": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/js-crypto-hash/-/js-crypto-hash-0.6.3.tgz", + "integrity": "sha512-SG8c9tM8y3sUb4k7WvpVfu5vU7zfPvX+eaYR5578TvehkehdaQbqAc+y+1FwxnqQ3WZ0gsYoOKp/mW+mqtNoWA==", + "dependencies": { + "buffer": "~5.4.3", + "hash.js": "~1.1.7", + "js-crypto-env": "^0.3.2", + "md5": "~2.2.1", + "sha3": "~2.1.0" + } + }, + "node_modules/js-crypto-hash/node_modules/buffer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", + "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "node_modules/js-crypto-hkdf": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/js-crypto-hkdf/-/js-crypto-hkdf-0.7.3.tgz", + "integrity": "sha512-eAaVArAjS2GCacWGXY4hjBiexrLQYlI0PMOcbwtrSEj84XU3kUfMYZm9bpTyaTXgdHC/eQoXe/Of6biG+RSEaQ==", + "dependencies": { + "js-crypto-env": "^0.3.2", + "js-crypto-hmac": "^0.6.3", + "js-crypto-random": "^0.4.3", + "js-encoding-utils": "0.5.6" + } + }, + "node_modules/js-crypto-hmac": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/js-crypto-hmac/-/js-crypto-hmac-0.6.3.tgz", + "integrity": "sha512-T0pKOaHACOSG6Xs6/06G8RDDeZouQwIQNBq9L/zoUGsd4F67gAjpT3q2lGigAGpUd1hiyy7vnhvLpz7VDt6DbA==", + "dependencies": { + "js-crypto-env": "^0.3.2", + "js-crypto-hash": "^0.6.3" + } + }, + "node_modules/js-crypto-random": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/js-crypto-random/-/js-crypto-random-0.4.3.tgz", + "integrity": "sha512-C3gzphPPfw9jfQ9Q/LjhJMZxQNp3AaoVRDvyZkiB+zYltfs8tKQPsskWkXACpg1Nzh01PtSRUvVijjptd2qGHQ==", + "dependencies": { + "js-crypto-env": "^0.3.2" + } + }, + "node_modules/js-encoding-utils": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/js-encoding-utils/-/js-encoding-utils-0.5.6.tgz", + "integrity": "sha512-qnAGsUIWrmzh5n+3AXqbxX1KsB9hkQmJZf3aA9DLAS7GpL/NEHCBreFFbW+imramoU+Q0TDyvkwhRbBRH1TVkg==" + }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -5353,6 +6102,11 @@ "node": ">=6" } }, + "node_modules/keyvaluestorage-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==" + }, "node_modules/libsodium": { "version": "0.7.10", "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz", @@ -5656,11 +6410,20 @@ "node": ">= 12" } }, + "node_modules/md5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", + "integrity": "sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==", + "dependencies": { + "charenc": "~0.0.1", + "crypt": "~0.0.1", + "is-buffer": "~1.1.1" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -5697,6 +6460,25 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -5731,6 +6513,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "node_modules/miscreant": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/miscreant/-/miscreant-0.3.2.tgz", + "integrity": "sha512-fL9KxsQz9BJB2KGPMHFrReioywkiomBiuaLk6EuChijK0BsJsIKJXdVomR+/bPj5mvbFD6wM0CM3bZio9g7OHA==" + }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -5768,9 +6555,7 @@ "node_modules/nan": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "dev": true, - "optional": true + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -5859,6 +6644,11 @@ "integrity": "sha512-A/csN0d3n+igxBPfUrjbV5GC69LWj2pjZzAAeeHXLukQ4+fytfP4T1Lg0ju7MSPSwq7KtHkGaiwO8URZN5IpLg==", "dev": true }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "node_modules/parcel": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.7.0.tgz", @@ -6065,7 +6855,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, "dependencies": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -6260,11 +7049,44 @@ "once": "^1.3.1" } }, + "node_modules/qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" + }, + "node_modules/qrcode.react": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-1.0.1.tgz", + "integrity": "sha512-8d3Tackk8IRLXTo67Y+c1rpaiXjoz/Dd2HpcMdW//62/x8J1Nbho14Kh8x974t9prsLHN6XqVgcnRiBGFptQmg==", + "dependencies": { + "loose-envify": "^1.4.0", + "prop-types": "^15.6.0", + "qr.js": "0.0.0" + }, + "peerDependencies": { + "react": "^15.5.3 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/query-string": { + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.5.tgz", + "integrity": "sha512-svk3xg9qHR39P3JlHuD7g3nRnyay5mHbrPctEBDUxUkHRifPHXJDhBUycdCC0NBjXoDf44Gb+IsOZL1Uwn8M/Q==", + "dependencies": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -6470,7 +7292,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -6560,7 +7381,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -6599,6 +7419,42 @@ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" }, + "node_modules/secretjs": { + "version": "0.17.7", + "resolved": "https://registry.npmjs.org/secretjs/-/secretjs-0.17.7.tgz", + "integrity": "sha512-j39l9+vR2A8067QBqDDejS7LmRLgdkG4uRw2Ar6HMfzDGo26eTh7cIXVlVu/yHBumxtQzKun20epOXwuYHXjQg==", + "dependencies": { + "@iov/crypto": "2.1.0", + "@iov/encoding": "2.1.0", + "@iov/utils": "2.0.2", + "axios": "0.21.1", + "curve25519-js": "0.0.4", + "fast-deep-equal": "3.1.1", + "js-crypto-hkdf": "0.7.3", + "miscreant": "0.3.2", + "pako": "1.0.11", + "protobufjs": "6.11.3", + "secure-random": "1.1.2" + } + }, + "node_modules/secretjs/node_modules/axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dependencies": { + "follow-redirects": "^1.10.0" + } + }, + "node_modules/secretjs/node_modules/fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + }, + "node_modules/secure-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/secure-random/-/secure-random-1.1.2.tgz", + "integrity": "sha512-H2bdSKERKdBV1SwoqYm6C0y+9EA94v6SUBOWO8kDndc4NoUih7Dv6Tsgma7zO1lv27wIvjlD0ZpMQk7um5dheQ==" + }, "node_modules/seedrandom": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", @@ -6617,7 +7473,6 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -6723,6 +7578,14 @@ "node": ">=0.10.0" } }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "engines": { + "node": ">=6" + } + }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -6761,11 +7624,18 @@ "readable-stream": "^3.5.0" } }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -6774,7 +7644,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -6934,6 +7803,27 @@ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, + "node_modules/tiny-secp256k1": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", + "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.3.0", + "bn.js": "^4.11.8", + "create-hmac": "^1.1.7", + "elliptic": "^6.4.0", + "nan": "^2.13.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/tiny-secp256k1/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -6965,6 +7855,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-tagger": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-tagger/-/type-tagger-1.0.0.tgz", + "integrity": "sha512-FIPqqpmDgdaulCnRoKv1/d3U4xVBUrYn42QXWNP3XYmgfPUDuBUsgFOb9ntT0aIe0UsUP+lknpQ5d9Kn36RssA==" + }, "node_modules/typed-function": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", @@ -6973,6 +7868,19 @@ "node": ">= 10" } }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, "node_modules/uncontrollable": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", @@ -6987,6 +7895,14 @@ "react": ">=15.0.0" } }, + "node_modules/unorm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", + "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", @@ -7041,8 +7957,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "node_modules/util/node_modules/inherits": { "version": "2.0.3", @@ -7098,6 +8013,14 @@ "node": ">= 8" } }, + "node_modules/wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==", + "dependencies": { + "bs58check": "<3.0.0" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -7531,6 +8454,76 @@ "xstream": "^11.14.0" } }, + "@cosmjs/launchpad": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/launchpad/-/launchpad-0.24.1.tgz", + "integrity": "sha512-syqVGKRH6z1vw4DdAJOSu4OgUXJdkXQozqvDde0cXYwnvhb7EXGSg5CTtp+2GqTBJuNVfMZ2DSvrC2Ig8cWBQQ==", + "requires": { + "@cosmjs/crypto": "^0.24.1", + "@cosmjs/encoding": "^0.24.1", + "@cosmjs/math": "^0.24.1", + "@cosmjs/utils": "^0.24.1", + "axios": "^0.21.1", + "fast-deep-equal": "^3.1.3" + }, + "dependencies": { + "@cosmjs/crypto": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.24.1.tgz", + "integrity": "sha512-GPhaWmQO06mXldKj/b+oKF5o3jMNfRKpAw+Q8XQhrD7ItinVPDMu8Xgl6frUXWTUdgpYwqpvqOcpm85QUsYV0Q==", + "requires": { + "@cosmjs/encoding": "^0.24.1", + "@cosmjs/math": "^0.24.1", + "@cosmjs/utils": "^0.24.1", + "bip39": "^3.0.2", + "bn.js": "^4.11.8", + "elliptic": "^6.5.3", + "js-sha3": "^0.8.0", + "libsodium-wrappers": "^0.7.6", + "pbkdf2": "^3.1.1", + "ripemd160": "^2.0.2", + "sha.js": "^2.4.11", + "unorm": "^1.5.0" + } + }, + "@cosmjs/encoding": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.24.1.tgz", + "integrity": "sha512-PMr+gaXAuM0XgjeXwB1zdX1QI0t+PgVhbmjgI/RSgswDzdExNH97qUopecL0/HG3p64vhIT/6ZjXYYTljZL7WA==", + "requires": { + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" + } + }, + "@cosmjs/math": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.24.1.tgz", + "integrity": "sha512-eBQk8twgzmpHFCVkoNjTZhsZwWRbR+JXt0FhjXJoD85SBm4K8b2OnOyTg68uPHVKOJjLRwzyRVYgMrg5TBVgwQ==", + "requires": { + "bn.js": "^4.11.8" + } + }, + "@cosmjs/utils": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.24.1.tgz", + "integrity": "sha512-VA3WFx1lMFb7esp9BqHWkDgMvHoA3D9w+uDRvWhVRpUpDc7RYHxMbWExASjz+gNblTCg556WJGzF64tXnf9tdQ==" + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, "@cosmjs/math": { "version": "0.28.7", "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.7.tgz", @@ -8078,6 +9071,54 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@iov/crypto": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@iov/crypto/-/crypto-2.1.0.tgz", + "integrity": "sha512-jnb4XuK50admolm7fBxOcxfAW2TO+wYrZlhDWiMETItY/Y5gNNa1zaDSO2wNIjjfGng+8nQ1yqnNhqy7busV2Q==", + "requires": { + "@iov/encoding": "^2.1.0", + "bip39": "^3.0.2", + "bn.js": "^4.11.8", + "elliptic": "^6.4.0", + "js-sha3": "^0.8.0", + "libsodium-wrappers": "^0.7.6", + "pbkdf2": "^3.0.16", + "ripemd160": "^2.0.2", + "sha.js": "^2.4.11", + "type-tagger": "^1.0.0", + "unorm": "^1.5.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "@iov/encoding": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@iov/encoding/-/encoding-2.1.0.tgz", + "integrity": "sha512-5IOdLO7Xg/uRykuiCqeMYghQ3IjWDtGxv7NTWXkgpHuna0aewx43mRpT2NPCpOZd1tpuorDtQ7/zbDNRaIIF/w==", + "requires": { + "base64-js": "^1.3.0", + "bech32": "^1.1.3", + "bn.js": "^4.11.8", + "readonly-date": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "@iov/utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@iov/utils/-/utils-2.0.2.tgz", + "integrity": "sha512-4D8MEvTcFc/DVy5q25vHxRItmgJyeX85dixMH+MxdKr+yy71h3sYk+sVBEIn70uqGP7VqAJkGOPNFs08/XYELw==" + }, "@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -8114,25 +9155,262 @@ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", + "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", + "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@keplr-wallet/common": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/common/-/common-0.11.3.tgz", + "integrity": "sha512-3izIyRX9nXTrc+u+24yYpl2qiLyHaweqiXI3fk2SHucWWicPqksSw5wkmuA+4Bef3/CUQPTgtcufDgPyBB+bNw==", + "requires": { + "@keplr-wallet/crypto": "0.11.3", + "buffer": "^6.0.3", + "delay": "^4.4.0" + } + }, + "@keplr-wallet/crypto": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/crypto/-/crypto-0.11.3.tgz", + "integrity": "sha512-BWSEU97qBu2M7DmSWPFxG+eL6k8UHQe230MBkDzLpVQsouFeJbwsip0mTxxJWaZXMuhyK9gaMsAQSK4YkHyI4Q==", + "requires": { + "bip32": "^2.0.6", + "bip39": "^3.0.3", + "bs58check": "^2.1.2", + "buffer": "^6.0.3", + "crypto-js": "^4.0.0", + "elliptic": "^6.5.3", + "sha.js": "^2.4.11" + } + }, + "@keplr-wallet/provider": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/provider/-/provider-0.11.3.tgz", + "integrity": "sha512-4KWRiJTyIBW+qOOYP2CN/IIvGMLsBgRi1D5/FT6q7EaYkIR/peJlKSR6fJukmQHyI/fmkvGH2+Td9fhZsVv5mA==", + "requires": { + "@cosmjs/launchpad": "^0.24.0-alpha.25", + "@cosmjs/proto-signing": "^0.24.0-alpha.25", + "@keplr-wallet/router": "0.11.3", + "@keplr-wallet/types": "0.11.3", + "buffer": "^6.0.3", + "deepmerge": "^4.2.2", + "long": "^4.0.0", + "secretjs": "0.17.7" + }, + "dependencies": { + "@cosmjs/proto-signing": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.24.1.tgz", + "integrity": "sha512-/rnyNx+FlG6b6O+igsb42eMN1/RXY+pTrNnAE8/YZaRloP9A6MXiTMO5JdYSTcjaD0mEVhejiy96bcyflKYXBg==", + "requires": { + "@cosmjs/launchpad": "^0.24.1", + "long": "^4.0.0", + "protobufjs": "~6.10.2" + } + }, + "@types/node": { + "version": "13.13.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", + "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" + }, + "protobufjs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.3.tgz", + "integrity": "sha512-yvAslS0hNdBhlSKckI4R1l7wunVilX66uvrjzE4MimiAt7/qw1nLpMhZrn/ObuUTM/c3Xnfl01LYMdcSJe6dwg==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + } + } + } + }, + "@keplr-wallet/router": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/router/-/router-0.11.3.tgz", + "integrity": "sha512-2Kh5+8ZqUY4l+Gf3mEl8q6vDeb7ino1SnCHA0bl5+ny9039zQ8AcIHjrZStX64nTPYuM/G60HRmNKhG51vMubQ==" + }, + "@keplr-wallet/types": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/types/-/types-0.11.3.tgz", + "integrity": "sha512-OA0OoimsUI22iTXgup3BAlLt+FJdGKniJqzJyQji+rqfXqreBT+Ie9c5Ng7qY81+RO/nJ5SZfa1gIns+3A0YxA==", + "requires": { + "@cosmjs/launchpad": "^0.24.0-alpha.25", + "@cosmjs/proto-signing": "^0.24.0-alpha.25", + "axios": "^0.27.2", + "long": "^4.0.0", + "secretjs": "0.17.7" + }, + "dependencies": { + "@cosmjs/proto-signing": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.24.1.tgz", + "integrity": "sha512-/rnyNx+FlG6b6O+igsb42eMN1/RXY+pTrNnAE8/YZaRloP9A6MXiTMO5JdYSTcjaD0mEVhejiy96bcyflKYXBg==", + "requires": { + "@cosmjs/launchpad": "^0.24.1", + "long": "^4.0.0", + "protobufjs": "~6.10.2" + } + }, + "@types/node": { + "version": "13.13.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", + "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" + }, + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "protobufjs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.3.tgz", + "integrity": "sha512-yvAslS0hNdBhlSKckI4R1l7wunVilX66uvrjzE4MimiAt7/qw1nLpMhZrn/ObuUTM/c3Xnfl01LYMdcSJe6dwg==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + } + } + } + }, + "@keplr-wallet/wc-client": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-client/-/wc-client-0.11.3.tgz", + "integrity": "sha512-u7A+c5XL8+8Qa9g6Qsn6PCZBYBtOgNgIBIqpbLFpbxzBCIHH8GE+PRp1AVNsU8R5f7HHyGWHO1T14sTrdUpGEQ==", + "requires": { + "@cosmjs/launchpad": "^0.24.0-alpha.25", + "@cosmjs/proto-signing": "^0.24.0-alpha.25", + "@keplr-wallet/common": "0.11.3", + "@keplr-wallet/provider": "0.11.3", + "@keplr-wallet/types": "0.11.3", + "@walletconnect/types": "^1.6.4", + "@walletconnect/utils": "^1.6.4", + "buffer": "^6.0.3", + "deepmerge": "^4.2.2", + "secretjs": "0.17.7" + }, + "dependencies": { + "@cosmjs/proto-signing": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.24.1.tgz", + "integrity": "sha512-/rnyNx+FlG6b6O+igsb42eMN1/RXY+pTrNnAE8/YZaRloP9A6MXiTMO5JdYSTcjaD0mEVhejiy96bcyflKYXBg==", + "requires": { + "@cosmjs/launchpad": "^0.24.1", + "long": "^4.0.0", + "protobufjs": "~6.10.2" + } + }, + "@types/node": { + "version": "13.13.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", + "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" + }, + "protobufjs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.3.tgz", + "integrity": "sha512-yvAslS0hNdBhlSKckI4R1l7wunVilX66uvrjzE4MimiAt7/qw1nLpMhZrn/ObuUTM/c3Xnfl01LYMdcSJe6dwg==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" } } } }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", - "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", + "@keplr-wallet/wc-qrcode-modal": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-qrcode-modal/-/wc-qrcode-modal-0.11.3.tgz", + "integrity": "sha512-dif+OqViavYny+r3PO5s4+zM85b8Gc2PTqQvoeBGVaKzk4tLYy3i4ICDrOku6Z2nOWrZg6Wl5iDlGRxQSfnlNA==", "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@walletconnect/browser-utils": "^1.6.5", + "@walletconnect/types": "^1.6.5", + "qrcode.react": "^1.0.1", + "react": "^16.14.0", + "react-dom": "^16.14.0" + }, + "dependencies": { + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } } }, "@lezer/common": { @@ -9422,6 +10700,11 @@ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -9476,6 +10759,171 @@ "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", "integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=" }, + "@walletconnect/browser-utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/browser-utils/-/browser-utils-1.8.0.tgz", + "integrity": "sha512-Wcqqx+wjxIo9fv6eBUFHPsW1y/bGWWRboni5dfD8PtOmrihrEpOCmvRJe4rfl7xgJW8Ea9UqKEaq0bIRLHlK4A==", + "requires": { + "@walletconnect/safe-json": "1.0.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/window-getters": "1.0.0", + "@walletconnect/window-metadata": "1.0.0", + "detect-browser": "5.2.0" + } + }, + "@walletconnect/client": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/client/-/client-1.8.0.tgz", + "integrity": "sha512-svyBQ14NHx6Cs2j4TpkQaBI/2AF4+LXz64FojTjMtV4VMMhl81jSO1vNeg+yYhQzvjcGH/GpSwixjyCW0xFBOQ==", + "requires": { + "@walletconnect/core": "^1.8.0", + "@walletconnect/iso-crypto": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "@walletconnect/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-1.8.0.tgz", + "integrity": "sha512-aFTHvEEbXcZ8XdWBw6rpQDte41Rxwnuk3SgTD8/iKGSRTni50gI9S3YEzMj05jozSiOBxQci4pJDMVhIUMtarw==", + "requires": { + "@walletconnect/socket-transport": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "@walletconnect/crypto": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/crypto/-/crypto-1.0.2.tgz", + "integrity": "sha512-+OlNtwieUqVcOpFTvLBvH+9J9pntEqH5evpINHfVxff1XIgwV55PpbdvkHu6r9Ib4WQDOFiD8OeeXs1vHw7xKQ==", + "requires": { + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/environment": "^1.0.0", + "@walletconnect/randombytes": "^1.0.2", + "aes-js": "^3.1.2", + "hash.js": "^1.1.7" + }, + "dependencies": { + "aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + } + } + }, + "@walletconnect/encoding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/encoding/-/encoding-1.0.1.tgz", + "integrity": "sha512-8opL2rs6N6E3tJfsqwS82aZQDL3gmupWUgmvuZ3CGU7z/InZs3R9jkzH8wmYtpbq0sFK3WkJkQRZFFk4BkrmFA==", + "requires": { + "is-typedarray": "1.0.0", + "typedarray-to-buffer": "3.1.5" + } + }, + "@walletconnect/environment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.0.tgz", + "integrity": "sha512-4BwqyWy6KpSvkocSaV7WR3BlZfrxLbJSLkg+j7Gl6pTDE+U55lLhJvQaMuDVazXYxcjBsG09k7UlH7cGiUI5vQ==" + }, + "@walletconnect/iso-crypto": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/iso-crypto/-/iso-crypto-1.8.0.tgz", + "integrity": "sha512-pWy19KCyitpfXb70hA73r9FcvklS+FvO9QUIttp3c2mfW8frxgYeRXfxLRCIQTkaYueRKvdqPjbyhPLam508XQ==", + "requires": { + "@walletconnect/crypto": "^1.0.2", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "@walletconnect/jsonrpc-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.1.tgz", + "integrity": "sha512-+6coTtOuChCqM+AoYyi4Q83p9l/laI6NvuM2/AHaZFuf0gT0NjW7IX2+86qGyizn7Ptq4AYZmfxurAxTnhefuw==", + "requires": { + "keyvaluestorage-interface": "^1.0.0" + } + }, + "@walletconnect/jsonrpc-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.3.tgz", + "integrity": "sha512-3yb49bPk16MNLk6uIIHPSHQCpD6UAo1OMOx1rM8cW/MPEAYAzrSW5hkhG7NEUwX9SokRIgnZK3QuQkiyNzBMhQ==", + "requires": { + "@walletconnect/environment": "^1.0.0", + "@walletconnect/jsonrpc-types": "^1.0.1" + } + }, + "@walletconnect/randombytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/randombytes/-/randombytes-1.0.2.tgz", + "integrity": "sha512-ivgOtAyqQnN0rLQmOFPemsgYGysd/ooLfaDA/ACQ3cyqlca56t3rZc7pXfqJOIETx/wSyoF5XbwL+BqYodw27A==", + "requires": { + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/environment": "^1.0.0", + "randombytes": "^2.1.0" + } + }, + "@walletconnect/safe-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.0.tgz", + "integrity": "sha512-QJzp/S/86sUAgWY6eh5MKYmSfZaRpIlmCJdi5uG4DJlKkZrHEF7ye7gA+VtbVzvTtpM/gRwO2plQuiooIeXjfg==" + }, + "@walletconnect/socket-transport": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/socket-transport/-/socket-transport-1.8.0.tgz", + "integrity": "sha512-5DyIyWrzHXTcVp0Vd93zJ5XMW61iDM6bcWT4p8DTRfFsOtW46JquruMhxOLeCOieM4D73kcr3U7WtyR4JUsGuQ==", + "requires": { + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0", + "ws": "7.5.3" + }, + "dependencies": { + "ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "requires": {} + } + } + }, + "@walletconnect/types": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-1.8.0.tgz", + "integrity": "sha512-Cn+3I0V0vT9ghMuzh1KzZvCkiAxTq+1TR2eSqw5E5AVWfmCtECFkVZBP6uUJZ8YjwLqXheI+rnjqPy7sVM4Fyg==" + }, + "@walletconnect/utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-1.8.0.tgz", + "integrity": "sha512-zExzp8Mj1YiAIBfKNm5u622oNw44WOESzo6hj+Q3apSMIb0Jph9X3GDIdbZmvVZsNPxWDL7uodKgZcCInZv2vA==", + "requires": { + "@walletconnect/browser-utils": "^1.8.0", + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/jsonrpc-utils": "^1.0.3", + "@walletconnect/types": "^1.8.0", + "bn.js": "4.11.8", + "js-sha3": "0.8.0", + "query-string": "6.13.5" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + } + } + }, + "@walletconnect/window-getters": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.0.tgz", + "integrity": "sha512-xB0SQsLaleIYIkSsl43vm8EwETpBzJ2gnzk7e0wMF3ktqiTGS6TFHxcprMl5R44KKh4tCcHCJwolMCaDSwtAaA==" + }, + "@walletconnect/window-metadata": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.0.tgz", + "integrity": "sha512-9eFvmJxIKCC3YWOL97SgRkKhlyGXkrHwamfechmqszbypFspaSk+t2jQXAEU7YClHF6Qjw5eYOmy1//zFi9/GA==", + "requires": { + "@walletconnect/window-getters": "^1.0.0" + } + }, "abortcontroller-polyfill": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", @@ -9551,6 +10999,11 @@ } } }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -9592,6 +11045,46 @@ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz", "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bip32": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", + "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "requires": { + "@types/node": "10.12.18", + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.1.3", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + } + }, + "bip39": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz", + "integrity": "sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==", + "requires": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" + }, + "dependencies": { + "@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" + } + } + }, "blakejs": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", @@ -9719,6 +11212,16 @@ "base-x": "^3.0.2" } }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, "buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -9765,6 +11268,11 @@ "supports-color": "^5.3.0" } }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" + }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -9775,7 +11283,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -9805,6 +11312,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -9881,7 +11396,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, "requires": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -9894,7 +11408,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -9915,6 +11428,11 @@ "which": "^2.0.1" } }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==" + }, "crypto-addr-codec": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz", @@ -9955,6 +11473,11 @@ "randomfill": "^1.0.3" } }, + "crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, "css": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", @@ -10028,6 +11551,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" }, + "curve25519-js": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/curve25519-js/-/curve25519-js-0.0.4.tgz", + "integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==" + }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -10052,6 +11580,11 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -10060,6 +11593,16 @@ "object-keys": "^1.0.12" } }, + "delay": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/delay/-/delay-4.4.1.tgz", + "integrity": "sha512-aL3AhqtfhOlT/3ai6sWXeqwnw63ATNpnUiN4HL7x9q+My5QtHlO3OIkasmug9LKzpheLdmUKGRKnYXYAS7FQkQ==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "dequal": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", @@ -10075,6 +11618,11 @@ "minimalistic-assert": "^1.0.0" } }, + "detect-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.0.tgz", + "integrity": "sha512-tr7XntDAu50BVENgQfajMLzacmSe34D+qZc4zjnniz0ZVuw/TZcLcyxHQjYpJTM36sGEkZZlYLnIM1hH7alTMA==" + }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -10519,8 +12067,7 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-json-stable-stringify": { "version": "2.1.0", @@ -10543,6 +12090,11 @@ "flat-cache": "^3.0.4" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -10569,6 +12121,16 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -10659,7 +12221,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, "requires": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -10669,8 +12230,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -10809,6 +12369,11 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "is-core-module": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", @@ -10838,6 +12403,11 @@ "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", "dev": true }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "iserror": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/iserror/-/iserror-0.0.2.tgz", @@ -10921,6 +12491,67 @@ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" }, + "js-crypto-env": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-crypto-env/-/js-crypto-env-0.3.2.tgz", + "integrity": "sha512-F1uHiCkSOo36qBuuZABA4sBf+xeFBzhJZ0Sd7af8FAruszIhm1Xxv+Zr5Ne90Zlh7/fnxCsrdkj0N8f0a3lVlQ==" + }, + "js-crypto-hash": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/js-crypto-hash/-/js-crypto-hash-0.6.3.tgz", + "integrity": "sha512-SG8c9tM8y3sUb4k7WvpVfu5vU7zfPvX+eaYR5578TvehkehdaQbqAc+y+1FwxnqQ3WZ0gsYoOKp/mW+mqtNoWA==", + "requires": { + "buffer": "~5.4.3", + "hash.js": "~1.1.7", + "js-crypto-env": "^0.3.2", + "md5": "~2.2.1", + "sha3": "~2.1.0" + }, + "dependencies": { + "buffer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", + "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + } + } + }, + "js-crypto-hkdf": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/js-crypto-hkdf/-/js-crypto-hkdf-0.7.3.tgz", + "integrity": "sha512-eAaVArAjS2GCacWGXY4hjBiexrLQYlI0PMOcbwtrSEj84XU3kUfMYZm9bpTyaTXgdHC/eQoXe/Of6biG+RSEaQ==", + "requires": { + "js-crypto-env": "^0.3.2", + "js-crypto-hmac": "^0.6.3", + "js-crypto-random": "^0.4.3", + "js-encoding-utils": "0.5.6" + } + }, + "js-crypto-hmac": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/js-crypto-hmac/-/js-crypto-hmac-0.6.3.tgz", + "integrity": "sha512-T0pKOaHACOSG6Xs6/06G8RDDeZouQwIQNBq9L/zoUGsd4F67gAjpT3q2lGigAGpUd1hiyy7vnhvLpz7VDt6DbA==", + "requires": { + "js-crypto-env": "^0.3.2", + "js-crypto-hash": "^0.6.3" + } + }, + "js-crypto-random": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/js-crypto-random/-/js-crypto-random-0.4.3.tgz", + "integrity": "sha512-C3gzphPPfw9jfQ9Q/LjhJMZxQNp3AaoVRDvyZkiB+zYltfs8tKQPsskWkXACpg1Nzh01PtSRUvVijjptd2qGHQ==", + "requires": { + "js-crypto-env": "^0.3.2" + } + }, + "js-encoding-utils": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/js-encoding-utils/-/js-encoding-utils-0.5.6.tgz", + "integrity": "sha512-qnAGsUIWrmzh5n+3AXqbxX1KsB9hkQmJZf3aA9DLAS7GpL/NEHCBreFFbW+imramoU+Q0TDyvkwhRbBRH1TVkg==" + }, "js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -10968,6 +12599,11 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" }, + "keyvaluestorage-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==" + }, "libsodium": { "version": "0.7.10", "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz", @@ -11139,11 +12775,20 @@ "typed-function": "^2.1.0" } }, + "md5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", + "integrity": "sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==", + "requires": { + "charenc": "~0.0.1", + "crypt": "~0.0.1", + "is-buffer": "~1.1.1" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -11179,6 +12824,19 @@ } } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -11207,6 +12865,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "miscreant": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/miscreant/-/miscreant-0.3.2.tgz", + "integrity": "sha512-fL9KxsQz9BJB2KGPMHFrReioywkiomBiuaLk6EuChijK0BsJsIKJXdVomR+/bPj5mvbFD6wM0CM3bZio9g7OHA==" + }, "moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -11240,9 +12903,7 @@ "nan": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "dev": true, - "optional": true + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" }, "natural-compare": { "version": "1.4.0", @@ -11312,6 +12973,11 @@ "integrity": "sha512-A/csN0d3n+igxBPfUrjbV5GC69LWj2pjZzAAeeHXLukQ4+fytfP4T1Lg0ju7MSPSwq7KtHkGaiwO8URZN5IpLg==", "dev": true }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "parcel": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.7.0.tgz", @@ -11468,7 +13134,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -11640,11 +13305,35 @@ "once": "^1.3.1" } }, + "qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" + }, + "qrcode.react": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-1.0.1.tgz", + "integrity": "sha512-8d3Tackk8IRLXTo67Y+c1rpaiXjoz/Dd2HpcMdW//62/x8J1Nbho14Kh8x974t9prsLHN6XqVgcnRiBGFptQmg==", + "requires": { + "loose-envify": "^1.4.0", + "prop-types": "^15.6.0", + "qr.js": "0.0.0" + } + }, + "query-string": { + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.5.tgz", + "integrity": "sha512-svk3xg9qHR39P3JlHuD7g3nRnyay5mHbrPctEBDUxUkHRifPHXJDhBUycdCC0NBjXoDf44Gb+IsOZL1Uwn8M/Q==", + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "requires": { "safe-buffer": "^5.1.0" } @@ -11808,7 +13497,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11871,7 +13559,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -11907,6 +13594,44 @@ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" }, + "secretjs": { + "version": "0.17.7", + "resolved": "https://registry.npmjs.org/secretjs/-/secretjs-0.17.7.tgz", + "integrity": "sha512-j39l9+vR2A8067QBqDDejS7LmRLgdkG4uRw2Ar6HMfzDGo26eTh7cIXVlVu/yHBumxtQzKun20epOXwuYHXjQg==", + "requires": { + "@iov/crypto": "2.1.0", + "@iov/encoding": "2.1.0", + "@iov/utils": "2.0.2", + "axios": "0.21.1", + "curve25519-js": "0.0.4", + "fast-deep-equal": "3.1.1", + "js-crypto-hkdf": "0.7.3", + "miscreant": "0.3.2", + "pako": "1.0.11", + "protobufjs": "6.11.3", + "secure-random": "1.1.2" + }, + "dependencies": { + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + } + } + }, + "secure-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/secure-random/-/secure-random-1.1.2.tgz", + "integrity": "sha512-H2bdSKERKdBV1SwoqYm6C0y+9EA94v6SUBOWO8kDndc4NoUih7Dv6Tsgma7zO1lv27wIvjlD0ZpMQk7um5dheQ==" + }, "seedrandom": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", @@ -11922,7 +13647,6 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -12002,6 +13726,11 @@ } } }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" + }, "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -12039,11 +13768,15 @@ "readable-stream": "^3.5.0" } }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" }, @@ -12051,8 +13784,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -12160,6 +13892,25 @@ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, + "tiny-secp256k1": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", + "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "requires": { + "bindings": "^1.3.0", + "bn.js": "^4.11.8", + "create-hmac": "^1.1.7", + "elliptic": "^6.4.0", + "nan": "^2.13.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -12182,11 +13933,29 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "type-tagger": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-tagger/-/type-tagger-1.0.0.tgz", + "integrity": "sha512-FIPqqpmDgdaulCnRoKv1/d3U4xVBUrYn42QXWNP3XYmgfPUDuBUsgFOb9ntT0aIe0UsUP+lknpQ5d9Kn36RssA==" + }, "typed-function": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==" }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, "uncontrollable": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", @@ -12198,6 +13967,11 @@ "react-lifecycles-compat": "^3.0.4" } }, + "unorm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", + "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==" + }, "update-browserslist-db": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz", @@ -12242,8 +14016,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utility-types": { "version": "3.10.0", @@ -12285,6 +14058,14 @@ "isexe": "^2.0.0" } }, + "wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==", + "requires": { + "bs58check": "<3.0.0" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index b0775d811..dbb4f1b8e 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,13 @@ "@cosmjs/proto-signing": "^0.28.1", "@cosmjs/stargate": "^0.28.1", "@ethersproject/wallet": "^5.6.2", + "@keplr-wallet/wc-client": "^0.11.3", + "@keplr-wallet/wc-qrcode-modal": "^0.11.3", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^13.5.0", "@tharsis/address-converter": "^0.1.8", + "@walletconnect/client": "^1.8.0", "axios": "^0.26.0", "bootstrap": "^5.1.3", "buffer": "^6.0.3", diff --git a/src/components/App.js b/src/components/App.js index 2b5c8ec45..a7ad31f34 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -57,6 +57,7 @@ import SendModal from './SendModal'; import KeplrSignerProvider from '../utils/KeplrSignerProvider.mjs'; import FalconSignerProvider from '../utils/FalconSignerProvider.mjs'; import LeapSignerProvider from '../utils/LeapSignerProvider.mjs'; +import KeplrMobileSignerProvider from '../utils/KeplrMobileSignerProvider.mjs'; class App extends React.Component { constructor(props) { @@ -69,6 +70,7 @@ class App extends React.Component { } this.signerProviders = [ new KeplrSignerProvider(window.keplr), + new KeplrMobileSignerProvider(), new LeapSignerProvider(window.leap), // new FalconSignerProvider(window.falcon) ] diff --git a/src/utils/KeplrMobileSignerProvider.mjs b/src/utils/KeplrMobileSignerProvider.mjs new file mode 100644 index 000000000..2097ca906 --- /dev/null +++ b/src/utils/KeplrMobileSignerProvider.mjs @@ -0,0 +1,50 @@ +import { Keplr } from "@keplr-wallet/types"; +import WalletConnect from "@walletconnect/client"; +import { KeplrQRCodeModalV1 } from "@keplr-wallet/wc-qrcode-modal"; +import { KeplrWalletConnectV1 } from "@keplr-wallet/wc-client"; + +import SignerProvider from "./SignerProvider.mjs" + +export default class KeplrMobileSignerProvider extends SignerProvider { + key = 'keplr-mobile' + label = 'Keplr Mobile' + keychangeEvent = 'keplr_keystorechange' + + connected(){ + return true + } + + suggestChain(network){ + } + + async enable(network){ + const connector = new WalletConnect({ + bridge: "https://bridge.walletconnect.org", // Required + signingMethods: [ + "keplr_enable_wallet_connect_v1", + "keplr_sign_amino_wallet_connect_v1", + ], + qrcodeModal: new KeplrQRCodeModalV1(), + }); + + // Check if connection is already established + if (!connector.connected) { + // create new session + connector.createSession(); + + return new Promise((resolve, reject) => { + connector.on("connect", (error) => { + if (error) { + reject(error); + } else { + this.provider = new KeplrWalletConnectV1(connector); + resolve(this.provider); + } + }); + }); + } else { + this.provider = new KeplrWalletConnectV1(connector); + return Promise.resolve(this.provider); + } + } +} \ No newline at end of file From b7a974b8dc82eadb9fbe841340ee0c698a2d7c1e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 23 Sep 2022 23:35:45 +0100 Subject: [PATCH 646/862] Improve connection process --- package-lock.json | 129 ++---------------------- package.json | 2 +- src/components/App.js | 40 +++++--- src/components/QRCodeModal.js | 98 ++++++++++++++++++ src/utils/KeplrMobileSignerProvider.mjs | 45 +++++---- src/utils/SignerProvider.mjs | 6 +- 6 files changed, 166 insertions(+), 154 deletions(-) create mode 100644 src/components/QRCodeModal.js diff --git a/package-lock.json b/package-lock.json index d9661e7a3..7f406ccf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "@cosmjs/stargate": "^0.28.1", "@ethersproject/wallet": "^5.6.2", "@keplr-wallet/wc-client": "^0.11.3", - "@keplr-wallet/wc-qrcode-modal": "^0.11.3", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^13.5.0", @@ -30,6 +29,7 @@ "moment": "^2.29.2", "parse-duration": "^1.0.2", "path": "^0.12.7", + "qrcode.react": "^3.1.0", "react": "^17.0.2", "react-bootstrap": "^2.1.2", "react-bootstrap-icons": "^1.8.3", @@ -1729,54 +1729,6 @@ "pbts": "bin/pbts" } }, - "node_modules/@keplr-wallet/wc-qrcode-modal": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-qrcode-modal/-/wc-qrcode-modal-0.11.3.tgz", - "integrity": "sha512-dif+OqViavYny+r3PO5s4+zM85b8Gc2PTqQvoeBGVaKzk4tLYy3i4ICDrOku6Z2nOWrZg6Wl5iDlGRxQSfnlNA==", - "dependencies": { - "@walletconnect/browser-utils": "^1.6.5", - "@walletconnect/types": "^1.6.5", - "qrcode.react": "^1.0.1", - "react": "^16.14.0", - "react-dom": "^16.14.0" - } - }, - "node_modules/@keplr-wallet/wc-qrcode-modal/node_modules/react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@keplr-wallet/wc-qrcode-modal/node_modules/react-dom": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", - "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" - }, - "peerDependencies": { - "react": "^16.14.0" - } - }, - "node_modules/@keplr-wallet/wc-qrcode-modal/node_modules/scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, "node_modules/@lezer/common": { "version": "0.15.12", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz", @@ -7049,22 +7001,12 @@ "once": "^1.3.1" } }, - "node_modules/qr.js": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", - "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" - }, "node_modules/qrcode.react": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-1.0.1.tgz", - "integrity": "sha512-8d3Tackk8IRLXTo67Y+c1rpaiXjoz/Dd2HpcMdW//62/x8J1Nbho14Kh8x974t9prsLHN6XqVgcnRiBGFptQmg==", - "dependencies": { - "loose-envify": "^1.4.0", - "prop-types": "^15.6.0", - "qr.js": "0.0.0" - }, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-3.1.0.tgz", + "integrity": "sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==", "peerDependencies": { - "react": "^15.5.3 || ^16.0.0 || ^17.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/query-string": { @@ -9369,50 +9311,6 @@ } } }, - "@keplr-wallet/wc-qrcode-modal": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-qrcode-modal/-/wc-qrcode-modal-0.11.3.tgz", - "integrity": "sha512-dif+OqViavYny+r3PO5s4+zM85b8Gc2PTqQvoeBGVaKzk4tLYy3i4ICDrOku6Z2nOWrZg6Wl5iDlGRxQSfnlNA==", - "requires": { - "@walletconnect/browser-utils": "^1.6.5", - "@walletconnect/types": "^1.6.5", - "qrcode.react": "^1.0.1", - "react": "^16.14.0", - "react-dom": "^16.14.0" - }, - "dependencies": { - "react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - } - }, - "react-dom": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", - "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" - } - }, - "scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - } - } - }, "@lezer/common": { "version": "0.15.12", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz", @@ -13305,20 +13203,11 @@ "once": "^1.3.1" } }, - "qr.js": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", - "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" - }, "qrcode.react": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-1.0.1.tgz", - "integrity": "sha512-8d3Tackk8IRLXTo67Y+c1rpaiXjoz/Dd2HpcMdW//62/x8J1Nbho14Kh8x974t9prsLHN6XqVgcnRiBGFptQmg==", - "requires": { - "loose-envify": "^1.4.0", - "prop-types": "^15.6.0", - "qr.js": "0.0.0" - } + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-3.1.0.tgz", + "integrity": "sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==", + "requires": {} }, "query-string": { "version": "6.13.5", diff --git a/package.json b/package.json index dbb4f1b8e..3838b746e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "@cosmjs/stargate": "^0.28.1", "@ethersproject/wallet": "^5.6.2", "@keplr-wallet/wc-client": "^0.11.3", - "@keplr-wallet/wc-qrcode-modal": "^0.11.3", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^13.5.0", @@ -25,6 +24,7 @@ "moment": "^2.29.2", "parse-duration": "^1.0.2", "path": "^0.12.7", + "qrcode.react": "^3.1.0", "react": "^17.0.2", "react-bootstrap": "^2.1.2", "react-bootstrap-icons": "^1.8.3", diff --git a/src/components/App.js b/src/components/App.js index a7ad31f34..82ba2e687 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -58,6 +58,7 @@ import KeplrSignerProvider from '../utils/KeplrSignerProvider.mjs'; import FalconSignerProvider from '../utils/FalconSignerProvider.mjs'; import LeapSignerProvider from '../utils/LeapSignerProvider.mjs'; import KeplrMobileSignerProvider from '../utils/KeplrMobileSignerProvider.mjs'; +import QRCodeModal from './QRCodeModal'; class App extends React.Component { constructor(props) { @@ -70,7 +71,16 @@ class App extends React.Component { } this.signerProviders = [ new KeplrSignerProvider(window.keplr), - new KeplrMobileSignerProvider(), + new KeplrMobileSignerProvider({ + qrcodeModal: { + open: (uri, callback) => { + this.setState({ qrCode: uri, qrCodeCallback: callback }) + }, + close: () => { + this.setState({ qrCode: null, qrCodeCallback: null }) + } + } + }), new LeapSignerProvider(window.leap), // new FalconSignerProvider(window.falcon) ] @@ -101,7 +111,7 @@ class App extends React.Component { if (!this.props.network) return if (this.props.network !== prevProps.network) { - this.setState({ balance: undefined, address: undefined, wallet: undefined, grants: undefined }) + this.setState({ balance: undefined, address: undefined, wallet: undefined, grants: undefined, error: undefined }) this.connect() }else if(this.state.address !== prevState.address){ this.clearRefreshInterval() @@ -144,7 +154,8 @@ class App extends React.Component { address: null, balance: null, wallet: null, - signingClient: null + signingClient: null, + signerProvider: null }) } @@ -160,7 +171,7 @@ class App extends React.Component { } let storedKey = localStorage.getItem('connected') - if(storedKey === '1'){ + if(storedKey === '1'){ // deprecate storedKey = 'keplr' localStorage.setItem('connected', storedKey) } @@ -171,24 +182,21 @@ class App extends React.Component { providerKey = signerProvider.key - if (manual && !signerProvider.connected()) { + if (manual && !signerProvider.available()) { return this.setState({ providerError: providerKey }) } - if (storedKey !== providerKey) { - if (manual) { - localStorage.setItem('connected', providerKey) - } else { - return - } - } - const { network } = this.props - if (!network || !signerProvider.connected()) return + if (!network || !signerProvider.available()) return + + if (!manual && (providerKey !== storedKey || !signerProvider.connected())) { + return + } let key, error + try { await signerProvider.enable(network) key = await signerProvider.getKey(network); @@ -215,6 +223,7 @@ class App extends React.Component { const address = await wallet.getAddress(); + localStorage.setItem('connected', providerKey) this.setState({ address, wallet, @@ -702,7 +711,7 @@ class App extends React.Component { ) : ( this.signerProviders.map(provider => { - return this.connect(provider.key, true)} disabled={!provider.connected()}>Connect {provider.label} + return this.connect(provider.key, true)} disabled={!provider.available()}>Connect {provider.label} }) )} this.setState({ showAddressModal: true })}>Saved Addresses @@ -838,6 +847,7 @@ class App extends React.Component { updateFavouriteAddresses={this.updateFavouriteAddresses} setAddress={(value) => this.setState({address: value, showAddressModal: false})} /> + this.setState({qrCode: null, qrCodeCallback: null})} /> {this.props.network && ( isMobile()); + const [checkAndroid] = useState(() => isAndroid()); + + const navigateToAppURL = useMemo(() => { + if (uri && checkMobile) { + if (checkAndroid) { + // Save the mobile link. + saveMobileLinkInfo({ + name: "Keplr", + href: + "intent://wcV1#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;", + }); + + return `intent://wcV1?${uri}#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;`; + } else { + // Save the mobile link. + saveMobileLinkInfo({ + name: "Keplr", + href: "keplrwallet://wcV1", + }); + + return `keplrwallet://wcV1?${uri}`; + } + } + }, [checkAndroid, checkMobile, uri]); + + useEffect(() => { + // Try opening the app without interaction. + if (navigateToAppURL) { + window.location.href = navigateToAppURL; + } + }, [navigateToAppURL]); + + const handleClose = () => { + onClose() + callback() + } + + return ( + <> + + {!checkMobile ? ( + <> + + + Scan QR Code + + + + {uri && ( +
      + +
      + )} +
      + + ) : ( + <> + + + Open App + + + + + + + )} +
      + + ); +} + +export default QRCodeModal diff --git a/src/utils/KeplrMobileSignerProvider.mjs b/src/utils/KeplrMobileSignerProvider.mjs index 2097ca906..8abecf4a0 100644 --- a/src/utils/KeplrMobileSignerProvider.mjs +++ b/src/utils/KeplrMobileSignerProvider.mjs @@ -1,6 +1,4 @@ -import { Keplr } from "@keplr-wallet/types"; import WalletConnect from "@walletconnect/client"; -import { KeplrQRCodeModalV1 } from "@keplr-wallet/wc-qrcode-modal"; import { KeplrWalletConnectV1 } from "@keplr-wallet/wc-client"; import SignerProvider from "./SignerProvider.mjs" @@ -10,40 +8,53 @@ export default class KeplrMobileSignerProvider extends SignerProvider { label = 'Keplr Mobile' keychangeEvent = 'keplr_keystorechange' - connected(){ + constructor({ qrcodeModal }){ + super() + this.qrcodeModal = qrcodeModal + this.connector = new WalletConnect({ + bridge: "https://bridge.walletconnect.org", // Required + signingMethods: [ + "keplr_enable_wallet_connect_v1", + "keplr_sign_amino_wallet_connect_v1", + ], + qrcodeModal: this.qrcodeModal, + }); + } + + available(){ return true } + connected(){ + return this.connector.connected + } + suggestChain(network){ } async enable(network){ - const connector = new WalletConnect({ - bridge: "https://bridge.walletconnect.org", // Required - signingMethods: [ - "keplr_enable_wallet_connect_v1", - "keplr_sign_amino_wallet_connect_v1", - ], - qrcodeModal: new KeplrQRCodeModalV1(), - }); + await this.createSession() + await this.provider.enable(network.chainId) + } + async createSession(){ // Check if connection is already established - if (!connector.connected) { + if (!this.connector.connected) { // create new session - connector.createSession(); + this.connector.createSession(); return new Promise((resolve, reject) => { - connector.on("connect", (error) => { + this.connector.on("connect", (error) => { if (error) { reject(error); } else { - this.provider = new KeplrWalletConnectV1(connector); - resolve(this.provider); + this.provider = new KeplrWalletConnectV1(this.connector); + resolve(); } }); }); } else { - this.provider = new KeplrWalletConnectV1(connector); + this.provider = new KeplrWalletConnectV1(this.connector); return Promise.resolve(this.provider); } } diff --git a/src/utils/SignerProvider.mjs b/src/utils/SignerProvider.mjs index 0cf9b60b5..daf919901 100644 --- a/src/utils/SignerProvider.mjs +++ b/src/utils/SignerProvider.mjs @@ -3,10 +3,14 @@ export default class SignerProvider { this.provider = provider } - connected(){ + available(){ return !!this.provider } + connected(){ + return this.available() + } + enable(network){ const { chainId } = network return this.provider.enable(chainId) From 628fc9f1fbed18499825020221805901d7289c43 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 25 Sep 2022 14:53:15 +0100 Subject: [PATCH 647/862] Add configurable throttle to autostake --- src/autostake/NetworkRunner.mjs | 43 +++++++++++++++++++++++---------- src/autostake/index.mjs | 3 ++- src/utils/Helpers.mjs | 2 +- src/utils/QueryClient.mjs | 10 ++++---- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 9dec8698a..717b54bae 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -11,11 +11,19 @@ export default class NetworkRunner { this.operator = operator this.signingClient = signingClient this.queryClient = network.queryClient - this.errors = {} - this.opts = opts || {} + this.opts = { + batchPageSize: 100, + batchQueries: 25, + batchTxs: 50, + queryTimeout: network.data.autostake?.delegatorTimeout || 5000, // deprecate + queryThrottle: 100, + ...network.data.autostake, + ...opts + } this.batch = {} this.messages = [] this.processed = {} + this.errors = {} this.results = [] } @@ -50,6 +58,7 @@ export default class NetworkRunner { async run(addresses) { try { + timeStamp("Running with options:", this.opts) this.balance = await this.getBalance() || 0 if (addresses) { @@ -93,9 +102,9 @@ export default class NetworkRunner { } getDelegations() { - let batchSize = this.network.data.autostake?.batchQueries || 100 - return this.queryClient.getAllValidatorDelegations(this.operator.address, batchSize, (pages) => { + return this.queryClient.getAllValidatorDelegations(this.operator.address, this.opts.batchPageSize, (pages) => { timeStamp("...batch", pages.length) + return this.throttleQuery() }).catch(error => { throw new Error(`Failed to get delegations: ${error.message || error}`) }) @@ -105,7 +114,10 @@ export default class NetworkRunner { const { botAddress, address } = this.operator let allGrants try { - allGrants = await this.queryClient.getGranteeGrants(botAddress) + allGrants = await this.queryClient.getGranteeGrants(botAddress, (pages) => { + timeStamp("...batch", pages.length) + return this.throttleQuery() + }) } catch (e) { } let grantCalls = addresses.map(item => { return async () => { @@ -117,16 +129,16 @@ export default class NetworkRunner { } } }) - let batchSize = this.network.data.autostake?.batchQueries || 50 - let grantedAddresses = await mapSync(grantCalls, batchSize, (batch, index) => { + let grantedAddresses = await mapSync(grantCalls, this.opts.batchQueries, (batch, index) => { timeStamp('...batch', index + 1) + return this.throttleQuery() }) return _.compact(grantedAddresses.flat()) } getGrants(delegatorAddress) { const { botAddress, address } = this.operator - let timeout = this.network.data.autostake?.delegatorTimeout || 5000 + let timeout = this.opts.queryTimeout return this.queryClient.getGrants(botAddress, delegatorAddress, { timeout }) .then( (result) => { @@ -165,7 +177,7 @@ export default class NetworkRunner { async getAutostakeMessage(grantAddress) { const { address, grant } = grantAddress - let timeout = this.network.data.autostake?.delegatorTimeout || 5000 + let timeout = this.opts.queryTimeout let withdrawAddress, totalRewards try { withdrawAddress = await this.queryClient.getWithdrawAddress(address, { timeout }) @@ -210,7 +222,7 @@ export default class NetworkRunner { } async autostake(grantedAddresses) { - let batchSize = this.network.data.autostake?.batchTxs || 50 + let batchSize = this.opts.batchTxs timeStamp('Calculating and autostaking in batches of', batchSize) const calls = grantedAddresses.map((item, index) => { @@ -226,8 +238,7 @@ export default class NetworkRunner { await this.sendInBatches(item.address, messages, batchSize, grantedAddresses.length) } }) - let querySize = this.network.data.autostake?.batchQueries || _.clamp(batchSize / 2, 50) - await executeSync(calls, querySize) + await executeSync(calls, this.opts.batchQueries) this.results = await Promise.all(this.messages) } @@ -290,6 +301,12 @@ export default class NetworkRunner { } } + async throttleQuery(){ + if(!this.opts.queryThrottle) return + + await new Promise(r => setTimeout(r, this.opts.queryThrottle)); + } + buildExecMessage(botAddress, messages) { return { typeUrl: "/cosmos.authz.v1beta1.MsgExec", @@ -312,7 +329,7 @@ export default class NetworkRunner { } totalRewards(address) { - let timeout = this.network.data.autostake?.delegatorTimeout || 5000 + let timeout = this.opts.queryTimeout return this.queryClient.getRewards(address, { timeout }) .then( (rewards) => { diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 9b0eb5b20..ca02b97ff 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -142,7 +142,8 @@ export default function Autostake(mnemonic, opts) { if (usingDirectory) { timeStamp('You are using public nodes, they may not be reliable. Check the README to use your own') - timeStamp('Delaying briefly to reduce load...') + timeStamp('Delaying briefly and adjusting config to reduce load...') + opts = {...opts, batchPageSize: 50, batchQueries: 10, queryThrottle: 1000} await new Promise(r => setTimeout(r, (Math.random() * 31) * 1000)); } diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 6a39fae1b..8ac822cdc 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -112,7 +112,7 @@ export async function mapSync(calls, count, batchCallback) { for (const batchCall of batchCalls) { const batchResults = await mapAsync(batchCall, call => call()) results.push(batchResults) - if (batchCallback) batchCallback(batchResults, index) + if (batchCallback) await batchCallback(batchResults, index) index++ } return results.flat() diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 11e780f43..04aeea736 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -137,7 +137,7 @@ const QueryClient = async (chainId, restUrls) => { .then((res) => res.data) }; - const getGranteeGrants = (grantee, opts) => { + const getGranteeGrants = (grantee, opts, pageCallback) => { const { pageSize } = opts || {} return getAllPages((nextKey) => { const searchParams = new URLSearchParams(); @@ -148,12 +148,12 @@ const QueryClient = async (chainId, restUrls) => { .get(restUrl + "/cosmos/authz/v1beta1/grants/grantee/" + grantee + "?" + searchParams.toString(), opts) .then((res) => res.data) - }).then((pages) => { + }, pageCallback).then((pages) => { return pages.map(el => el.grants).flat(); }); }; - const getGranterGrants = (granter, opts) => { + const getGranterGrants = (granter, opts, pageCallback) => { const { pageSize } = opts || {} return getAllPages((nextKey) => { const searchParams = new URLSearchParams(); @@ -164,7 +164,7 @@ const QueryClient = async (chainId, restUrls) => { .get(restUrl + "/cosmos/authz/v1beta1/grants/granter/" + granter + "?" + searchParams.toString(), opts) .then((res) => res.data) - }).then((pages) => { + }, pageCallback).then((pages) => { return pages.map(el => el.grants).flat(); }); }; @@ -202,7 +202,7 @@ const QueryClient = async (chainId, restUrls) => { const result = await getPage(nextKey); pages.push(result); nextKey = result.pagination.next_key; - if (pageCallback) pageCallback(pages); + if (pageCallback) await pageCallback(pages); } while (nextKey); return pages; }; From bba5338b911df1f8ba89031d7f76f9e237ed6a58 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 25 Sep 2022 19:11:44 +0100 Subject: [PATCH 648/862] Update gasModifier and README --- README.md | 6 ++++-- src/autostake/NetworkRunner.mjs | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cca692ac6..77c221380 100644 --- a/README.md +++ b/README.md @@ -181,9 +181,11 @@ Create a `src/networks.local.json` file and specify the networks you want to ove "gasPrice": "0.0025uosmo", "autostake": { "retries": 3, + "batchPageSize": 100, + "batchQueries": 25, "batchTxs": 50, - "batchQueries": 50, - "delegatorTimeout": 5000, + "queryTimeout": 5000, + "queryThrottle": 100, "gasModifier": 1.1 }, "healthCheck": { diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 717b54bae..9dd706de6 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -17,6 +17,7 @@ export default class NetworkRunner { batchTxs: 50, queryTimeout: network.data.autostake?.delegatorTimeout || 5000, // deprecate queryThrottle: 100, + gasModifier: 1.1, ...network.data.autostake, ...opts } @@ -269,7 +270,7 @@ export default class NetworkRunner { try { const execMsg = this.buildExecMessage(this.operator.botAddress, messages) const memo = 'REStaked by ' + this.operator.moniker - const gasModifier = this.network.data.autostake?.gasModifier || 1.1 + const gasModifier = this.opts.gasModifier const gas = await this.signingClient.simulate(this.operator.botAddress, [execMsg], memo, gasModifier); const fee = this.signingClient.getFee(gas).amount[0] if (smaller(bignumber(this.balance), bignumber(fee.amount))) { From 44dbdfc3dabe12315f4e476c4a1783dbe7ffadab Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 25 Sep 2022 14:05:06 +0100 Subject: [PATCH 649/862] Show UI when connecting to mobile wallet --- src/components/App.js | 16 ++-- src/components/ConnectWalletModal.js | 99 +++++++++++++++++++++++++ src/components/QRCodeModal.js | 98 ------------------------ src/utils/KeplrMobileSignerProvider.mjs | 16 ++-- 4 files changed, 121 insertions(+), 108 deletions(-) create mode 100644 src/components/ConnectWalletModal.js delete mode 100644 src/components/QRCodeModal.js diff --git a/src/components/App.js b/src/components/App.js index 82ba2e687..22cc9159e 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -58,7 +58,7 @@ import KeplrSignerProvider from '../utils/KeplrSignerProvider.mjs'; import FalconSignerProvider from '../utils/FalconSignerProvider.mjs'; import LeapSignerProvider from '../utils/LeapSignerProvider.mjs'; import KeplrMobileSignerProvider from '../utils/KeplrMobileSignerProvider.mjs'; -import QRCodeModal from './QRCodeModal'; +import ConnectWalletModal from './ConnectWalletModal'; class App extends React.Component { constructor(props) { @@ -72,12 +72,12 @@ class App extends React.Component { this.signerProviders = [ new KeplrSignerProvider(window.keplr), new KeplrMobileSignerProvider({ - qrcodeModal: { + connectModal: { open: (uri, callback) => { - this.setState({ qrCode: uri, qrCodeCallback: callback }) + this.setState({ connectWallet: 'Keplr Mobile', qrCodeUri: uri, qrCodeCallback: callback }) }, close: () => { - this.setState({ qrCode: null, qrCodeCallback: null }) + this.setState({ connectWallet: false, qrCodeUri: null, qrCodeCallback: null }) } } }), @@ -847,7 +847,13 @@ class App extends React.Component { updateFavouriteAddresses={this.updateFavouriteAddresses} setAddress={(value) => this.setState({address: value, showAddressModal: false})} /> - this.setState({qrCode: null, qrCodeCallback: null})} /> + this.setState({connectWallet: false, qrCodeUri: null, qrCodeCallback: null})} + /> {this.props.network && ( isMobile()); + const [checkAndroid] = useState(() => isAndroid()); + + const navigateToAppURL = useMemo(() => { + if (uri && checkMobile) { + if (checkAndroid) { + // Save the mobile link. + saveMobileLinkInfo({ + name: "Keplr", + href: + "intent://wcV1#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;", + }); + + return `intent://wcV1?${uri}#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;`; + } else { + // Save the mobile link. + saveMobileLinkInfo({ + name: "Keplr", + href: "keplrwallet://wcV1", + }); + + return `keplrwallet://wcV1?${uri}`; + } + } + }, [checkAndroid, checkMobile, uri]); + + useEffect(() => { + // Try opening the app without interaction. + if (uri && navigateToAppURL) { + window.location.href = navigateToAppURL; + } + }, [uri, navigateToAppURL]); + + const handleClose = () => { + onClose() + callback && callback() + } + + const title = !checkMobile && uri ? 'Scan QR Code' : `Connect ${walletName}` + + return ( + <> + + <> + + + {title} + + + + {uri ? ( + checkMobile ? ( + + ) : ( +
      + +
      + ) + ) : ( +
      +

      {`Open your ${walletName} app to continue...`}

      + + Loading... + +
      + )} +
      + +
      + + ); +} + +export default ConnectWalletModal diff --git a/src/components/QRCodeModal.js b/src/components/QRCodeModal.js deleted file mode 100644 index 5b2c74177..000000000 --- a/src/components/QRCodeModal.js +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useState, useMemo, useEffect } from 'react'; - -import { - Modal, - Button -} from 'react-bootstrap' - -import { - isMobile, - isAndroid, - saveMobileLinkInfo, -} from "@walletconnect/browser-utils"; -import QRCode from "qrcode.react"; - -function QRCodeModal(props) { - const { uri, callback, onClose } = props - const [checkMobile] = useState(() => isMobile()); - const [checkAndroid] = useState(() => isAndroid()); - - const navigateToAppURL = useMemo(() => { - if (uri && checkMobile) { - if (checkAndroid) { - // Save the mobile link. - saveMobileLinkInfo({ - name: "Keplr", - href: - "intent://wcV1#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;", - }); - - return `intent://wcV1?${uri}#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;`; - } else { - // Save the mobile link. - saveMobileLinkInfo({ - name: "Keplr", - href: "keplrwallet://wcV1", - }); - - return `keplrwallet://wcV1?${uri}`; - } - } - }, [checkAndroid, checkMobile, uri]); - - useEffect(() => { - // Try opening the app without interaction. - if (navigateToAppURL) { - window.location.href = navigateToAppURL; - } - }, [navigateToAppURL]); - - const handleClose = () => { - onClose() - callback() - } - - return ( - <> - - {!checkMobile ? ( - <> - - - Scan QR Code - - - - {uri && ( -
      - -
      - )} -
      - - ) : ( - <> - - - Open App - - - - - - - )} -
      - - ); -} - -export default QRCodeModal diff --git a/src/utils/KeplrMobileSignerProvider.mjs b/src/utils/KeplrMobileSignerProvider.mjs index 8abecf4a0..abb5d529b 100644 --- a/src/utils/KeplrMobileSignerProvider.mjs +++ b/src/utils/KeplrMobileSignerProvider.mjs @@ -8,16 +8,16 @@ export default class KeplrMobileSignerProvider extends SignerProvider { label = 'Keplr Mobile' keychangeEvent = 'keplr_keystorechange' - constructor({ qrcodeModal }){ + constructor({ connectModal }){ super() - this.qrcodeModal = qrcodeModal + this.connectModal = connectModal this.connector = new WalletConnect({ bridge: "https://bridge.walletconnect.org", // Required signingMethods: [ "keplr_enable_wallet_connect_v1", "keplr_sign_amino_wallet_connect_v1", ], - qrcodeModal: this.qrcodeModal, + qrcodeModal: this.connectModal, }); } @@ -30,11 +30,17 @@ export default class KeplrMobileSignerProvider extends SignerProvider { } suggestChain(network){ + throw new Error(`${network.prettyName} (${network.chainId}) is not supported`) } async enable(network){ - await this.createSession() - await this.provider.enable(network.chainId) + this.connectModal.open() + try { + await this.createSession() + await this.provider.enable(network.chainId) + } finally { + this.connectModal.close() + } } async createSession(){ From 3989902ba711ac977d18cd3089dfdcd4e57c7165 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 25 Sep 2022 22:21:45 +0100 Subject: [PATCH 650/862] Show varying wallet authz support --- src/components/Delegations.js | 24 ++++++++++++++++-------- src/components/GrantModal.js | 14 +++++++------- src/components/Grants.js | 6 +++--- src/components/RevokeGrant.js | 2 +- src/components/ValidatorModal.js | 4 ++-- src/networks.json | 2 +- src/utils/Chain.mjs | 2 +- src/utils/KeplrMobileSignerProvider.mjs | 6 ++++++ src/utils/Network.mjs | 2 +- src/utils/SigningClient.mjs | 2 +- src/utils/Wallet.mjs | 8 +++----- 11 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 47399e67f..7f2e4a01b 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -35,8 +35,8 @@ class Delegations extends React.Component { } async componentDidMount() { - const ledgerAuthzSupport = this.props.wallet?.ledgerAuthzSupport(); - this.setState({ ledgerAuthzSupport }); + const walletAuthzSupport = this.props.wallet?.authzSupport(); + this.setState({ walletAuthzSupport }); this.refresh(true); if (this.props.validator) { @@ -52,9 +52,9 @@ class Delegations extends React.Component { if ((this.props.network !== prevProps.network && !this.props.address) || (this.props.address !== prevProps.address)) { this.clearRefreshInterval() - const ledgerAuthzSupport = this.props.wallet?.ledgerAuthzSupport(); + const walletAuthzSupport = this.props.wallet?.authzSupport(); this.setState({ - ledgerAuthzSupport: ledgerAuthzSupport, + walletAuthzSupport: walletAuthzSupport, delegations: undefined, rewards: undefined, commission: {}, @@ -297,7 +297,7 @@ class Delegations extends React.Component { } restakePossible() { - return this.props.address && this.state.ledgerAuthzSupport && this.authzSupport(); + return this.props.address && this.state.walletAuthzSupport && this.authzSupport(); } totalRewards(validators) { @@ -414,14 +414,22 @@ class Delegations extends React.Component { {this.authzSupport() && this.props.operators.length > 0 && this.props.wallet && - !this.state.ledgerAuthzSupport && ( + !this.state.walletAuthzSupport && ( <> -

      Ledger devices can't send Authz transactions just yet. Full support will be enabled as soon as it is possible.

      -

      this.setState({ showAboutLedger: true })} role="button" className="text-reset text-decoration-underline">A manual workaround is possible using the CLI.

      + {this.props.wallet.getIsNanoLedger() ? ( + <> +

      Ledger devices can't send Authz transactions on {this.props.network.prettyName} just yet. All other features are supported and Authz is coming soon.

      +

      this.setState({ showAboutLedger: true })} role="button" className="text-reset text-decoration-underline">A manual workaround is possible using the CLI.

      + + ) : ( + <> +

      This wallet can't send Authz transactions on {this.props.network.prettyName} just yet. All other features are supported, and Keplr Extension likely has support if required.

      + + )}
      )} diff --git a/src/components/GrantModal.js b/src/components/GrantModal.js index 4f8fe78cf..f23eeb0f7 100644 --- a/src/components/GrantModal.js +++ b/src/components/GrantModal.js @@ -19,12 +19,12 @@ import { buildExecMessage } from '../utils/Helpers.mjs'; function GrantModal(props) { const { show, network, address, wallet } = props - const ledgerAuthzSupport = wallet?.ledgerAuthzSupport() + const walletAuthzSupport = wallet?.authzSupport() const defaultExpiry = moment().add(1, 'year') const [loading, setLoading] = useState(false); const [error, setError] = useState() const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry.format('YYYY-MM-DD') }); - const [showLedger, setShowLedger] = useState(!ledgerAuthzSupport) + const [showLedger, setShowLedger] = useState(!walletAuthzSupport) const { daemon_name, chain_id } = network.chain.data @@ -37,7 +37,7 @@ function GrantModal(props) { messageTypeValue: messageTypes[0], customMessageTypeValue: '', }) - setShowLedger(!ledgerAuthzSupport) + setShowLedger(!walletAuthzSupport) setError(null) }, [address]) @@ -56,7 +56,7 @@ function GrantModal(props) { function handleSubmit(event) { event.preventDefault() - if(!address || !ledgerAuthzSupport || !valid()) return + if(!address || !walletAuthzSupport || !valid()) return showLoading(true) const expiry = moment(state.expiryDateValue) @@ -147,7 +147,7 @@ function GrantModal(props) { - {address && !!ledgerAuthzSupport ? 'New Grant' : 'CLI/Ledger instructions'} + {address && !!walletAuthzSupport ? 'New Grant' : 'CLI/Ledger instructions'} @@ -156,7 +156,7 @@ function GrantModal(props) { {error} } - {!address || !ledgerAuthzSupport && ( + {!address || !walletAuthzSupport && ( <>

      Enter your grant details to generate the relevant CLI command.

      @@ -214,7 +214,7 @@ function GrantModal(props) { )}

      Incorrect use of Authz grants can be as dangerous as giving away your mnemonic. Make sure you trust the Grantee address and understand the permissions you are granting.

      - {address && !!ledgerAuthzSupport && ( + {address && !!walletAuthzSupport && (

    diff --git a/src/components/RevokeGrant.js b/src/components/RevokeGrant.js index 61626e72e..db8ffdafb 100644 --- a/src/components/RevokeGrant.js +++ b/src/components/RevokeGrant.js @@ -51,7 +51,7 @@ function RevokeGrant(props) { } function disabled(){ - return props.disabled || !wallet?.hasPermission(address, 'Revoke') || !wallet?.ledgerAuthzSupport() + return props.disabled || !wallet?.hasPermission(address, 'Revoke') || !wallet?.authzSupport() } if(props.button){ diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index a06096857..4027cb7eb 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -165,7 +165,7 @@ function ValidatorModal(props) { REStake )} - {network.authzSupport && !network.ledgerAuthzSupport && operator() && ( + {network.authzSupport && !network.authzAminoSupport && operator() && ( Ledger Instructions @@ -177,7 +177,7 @@ function ValidatorModal(props) { {operator() && ( )} - {network.authzSupport && !network.ledgerAuthzSupport && operator() && ( + {network.authzSupport && !network.authzAminoSupport && operator() && ( )} diff --git a/src/networks.json b/src/networks.json index 468b357e6..6518bd1cf 100644 --- a/src/networks.json +++ b/src/networks.json @@ -143,7 +143,7 @@ "name": "secretnetwork", "gasPrice": "0.025uscrt", "gasPricePrefer": "0.05uscrt", - "ledgerAuthzSupport": true + "authzAminoSupport": true }, { "name": "bostrom", diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index 23164b9b3..9ce4c287e 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -11,7 +11,7 @@ const Chain = (data) => { slip44: data.slip44 || 118, estimatedApr: data.params?.calculated_apr, authzSupport: data.authzSupport ?? data.params?.authz, - ledgerAuthzSupport: data.ledgerAuthzSupport ?? false, + authzAminoSupport: data.authzAminoSupport ?? false, denom: data.denom || baseAsset?.base?.denom, display: data.display || baseAsset?.display?.denom, symbol: data.symbol || baseAsset?.symbol, diff --git a/src/utils/KeplrMobileSignerProvider.mjs b/src/utils/KeplrMobileSignerProvider.mjs index abb5d529b..66c6ae8be 100644 --- a/src/utils/KeplrMobileSignerProvider.mjs +++ b/src/utils/KeplrMobileSignerProvider.mjs @@ -43,6 +43,12 @@ export default class KeplrMobileSignerProvider extends SignerProvider { } } + getSigner(network){ + const { chainId } = network + // return this.provider.getOfflineSignerAuto(chainId) // no signDirect support currently + return this.provider.getOfflineSignerOnlyAmino(chainId) + } + async createSession(){ // Check if connection is already established if (!this.connector.connected) { diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index b9236d366..244668286 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -86,7 +86,7 @@ class Network { this.estimatedApr = this.chain.estimatedApr this.apyEnabled = data.apyEnabled !== false && !!this.estimatedApr && this.estimatedApr > 0 this.authzSupport = this.chain.authzSupport - this.ledgerAuthzSupport = this.chain.ledgerAuthzSupport + this.authzAminoSupport = this.chain.authzAminoSupport this.defaultGasPrice = this.decimals && format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom this.gasPrice = this.data.gasPrice || this.defaultGasPrice if(this.gasPrice){ diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 5d99699d5..54cd828d0 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -238,7 +238,7 @@ function SigningClient(network, signer) { function convertToAmino(messages){ return messages.map(message => { - if(message.typeUrl.startsWith('/cosmos.authz') && !network.ledgerAuthzSupport){ + if(message.typeUrl.startsWith('/cosmos.authz') && !network.authzAminoSupport){ throw new Error('This chain does not support amino conversion for Authz messages') } return aminoTypes.toAmino(message) diff --git a/src/utils/Wallet.mjs b/src/utils/Wallet.mjs index 48d233536..11a8e81ca 100644 --- a/src/utils/Wallet.mjs +++ b/src/utils/Wallet.mjs @@ -30,7 +30,7 @@ class Wallet { hasPermission(address, action){ if(address === this.address) return true - if(!this.ledgerAuthzSupport()) return false + if(!this.authzSupport()) return false let message = messageTypes.find(el => { return el.split('.').slice(-1)[0].replace('Msg', '') === action @@ -43,10 +43,8 @@ class Wallet { }) } - ledgerAuthzSupport(){ - if(!this.getIsNanoLedger()) return true - - return this.network.ledgerAuthzSupport + authzSupport(){ + return this.signer.signDirect || (this.network.authzAminoSupport && this.signer.signAmino) } async getAddress(){ From 0a61212d53694977ab9190c3f1536825781a4e48 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 25 Sep 2022 23:13:19 +0100 Subject: [PATCH 651/862] Disconnect from signer provider --- src/components/App.js | 11 ++++++++--- src/utils/KeplrMobileSignerProvider.mjs | 4 ++++ src/utils/SignerProvider.mjs | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 22cc9159e..59a2d5169 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -74,10 +74,14 @@ class App extends React.Component { new KeplrMobileSignerProvider({ connectModal: { open: (uri, callback) => { - this.setState({ connectWallet: 'Keplr Mobile', qrCodeUri: uri, qrCodeCallback: callback }) + this.setState({ + connectWallet: 'Keplr Mobile', + qrCodeUri: uri || this.state.qrCodeUri, + qrCodeCallback: callback || this.state.qrCodeCallback + }) }, close: () => { - this.setState({ connectWallet: false, qrCodeUri: null, qrCodeCallback: null }) + this.setState({ connectWallet: false }) } } }), @@ -150,6 +154,7 @@ class App extends React.Component { disconnect() { localStorage.removeItem('connected') + this.state.signerProvider.disconnect() this.setState({ address: null, balance: null, @@ -852,7 +857,7 @@ class App extends React.Component { walletName={this.state.connectWallet} uri={this.state.qrCodeUri} callback={this.state.qrCodeCallback} - onClose={() => this.setState({connectWallet: false, qrCodeUri: null, qrCodeCallback: null})} + onClose={() => this.setState({connectWallet: false})} /> {this.props.network && ( Date: Sun, 25 Sep 2022 23:17:29 +0100 Subject: [PATCH 652/862] Increase directory throttle --- src/autostake/index.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index ca02b97ff..3a78b3420 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -143,7 +143,7 @@ export default function Autostake(mnemonic, opts) { if (usingDirectory) { timeStamp('You are using public nodes, they may not be reliable. Check the README to use your own') timeStamp('Delaying briefly and adjusting config to reduce load...') - opts = {...opts, batchPageSize: 50, batchQueries: 10, queryThrottle: 1000} + opts = {...opts, batchPageSize: 50, batchQueries: 10, queryThrottle: 2500} await new Promise(r => setTimeout(r, (Math.random() * 31) * 1000)); } From 23ca03531514e1ab8a557bc61c6e42cc5bae953e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 26 Sep 2022 11:23:48 +0100 Subject: [PATCH 653/862] Improve app redirect --- src/components/ConnectWalletModal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ConnectWalletModal.js b/src/components/ConnectWalletModal.js index 9c384db1a..ff3c552de 100644 --- a/src/components/ConnectWalletModal.js +++ b/src/components/ConnectWalletModal.js @@ -43,10 +43,10 @@ function ConnectWalletModal(props) { useEffect(() => { // Try opening the app without interaction. - if (uri && navigateToAppURL) { + if (uri && show && navigateToAppURL) { window.location.href = navigateToAppURL; } - }, [uri, navigateToAppURL]); + }, [navigateToAppURL]); const handleClose = () => { onClose() From 41a24a71fde6fecfed90db042f97d8afdbbf3339 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 26 Sep 2022 11:44:29 +0100 Subject: [PATCH 654/862] Additional timeouts --- src/autostake/NetworkRunner.mjs | 13 +++++++++---- src/utils/QueryClient.mjs | 12 +++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 9dd706de6..6fddaa4c1 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -90,20 +90,23 @@ export default class NetworkRunner { } getBalance() { - return this.queryClient.getBalance(this.operator.botAddress, this.network.denom) + let timeout = this.opts.queryTimeout + return this.queryClient.getBalance(this.operator.botAddress, this.network.denom, { timeout }) .then( (balance) => { timeStamp("Bot balance is", balance.amount, balance.denom) return balance.amount }, (error) => { - throw new Error("Failed to get balance:", error.message || error) + throw new Error(`Failed to get balance: ${error.message || error}`) } ) } getDelegations() { - return this.queryClient.getAllValidatorDelegations(this.operator.address, this.opts.batchPageSize, (pages) => { + let timeout = this.opts.queryTimeout + let pageSize = this.opts.batchPageSize + return this.queryClient.getAllValidatorDelegations(this.operator.address, pageSize, { timeout }, (pages) => { timeStamp("...batch", pages.length) return this.throttleQuery() }).catch(error => { @@ -113,9 +116,11 @@ export default class NetworkRunner { async getGrantedAddresses(addresses) { const { botAddress, address } = this.operator + let timeout = this.opts.queryTimeout + let pageSize = this.opts.batchPageSize let allGrants try { - allGrants = await this.queryClient.getGranteeGrants(botAddress, (pages) => { + allGrants = await this.queryClient.getGranteeGrants(botAddress, { timeout, pageSize }, (pages) => { timeStamp("...batch", pages.length) return this.throttleQuery() }) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 04aeea736..56f013887 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -37,16 +37,17 @@ const QueryClient = async (chainId, restUrls) => { const getAllValidatorDelegations = ( validatorAddress, pageSize, + opts, pageCallback ) => { return getAllPages((nextKey) => { - return getValidatorDelegations(validatorAddress, pageSize, nextKey); + return getValidatorDelegations(validatorAddress, pageSize, opts, nextKey); }, pageCallback).then((pages) => { return pages.map((el) => el.delegation_responses).flat(); }); }; - const getValidatorDelegations = (validatorAddress, pageSize, nextKey) => { + const getValidatorDelegations = (validatorAddress, pageSize, opts, nextKey) => { const searchParams = new URLSearchParams(); if (pageSize) searchParams.append("pagination.limit", pageSize); if (nextKey) searchParams.append("pagination.key", nextKey); @@ -57,14 +58,15 @@ const QueryClient = async (chainId, restUrls) => { "/cosmos/staking/v1beta1/validators/" + validatorAddress + "/delegations?" + - searchParams.toString() + searchParams.toString(), + opts ) .then((res) => res.data); }; - const getBalance = (address, denom) => { + const getBalance = (address, denom, opts) => { return axios - .get(restUrl + "/cosmos/bank/v1beta1/balances/" + address) + .get(restUrl + "/cosmos/bank/v1beta1/balances/" + address, opts) .then((res) => res.data) .then((result) => { const balance = result.balances?.find( From 56937454e8b14a3badc365697702396d60291398 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 27 Sep 2022 11:52:21 +0100 Subject: [PATCH 655/862] Add delegationsTimeout config and default to 20s --- README.md | 1 + src/autostake/NetworkRunner.mjs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 77c221380..879312655 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Create a `src/networks.local.json` file and specify the networks you want to ove "batchPageSize": 100, "batchQueries": 25, "batchTxs": 50, + "delegationsTimeout": 20000, "queryTimeout": 5000, "queryThrottle": 100, "gasModifier": 1.1 diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 6fddaa4c1..2267a1328 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -15,7 +15,8 @@ export default class NetworkRunner { batchPageSize: 100, batchQueries: 25, batchTxs: 50, - queryTimeout: network.data.autostake?.delegatorTimeout || 5000, // deprecate + delegationsTimeout: 20000, + queryTimeout: network.data.autostake?.delegatorTimeout || 5000, // deprecate delegatorTimeout queryThrottle: 100, gasModifier: 1.1, ...network.data.autostake, @@ -104,7 +105,7 @@ export default class NetworkRunner { } getDelegations() { - let timeout = this.opts.queryTimeout + let timeout = this.opts.delegationsTimeout let pageSize = this.opts.batchPageSize return this.queryClient.getAllValidatorDelegations(this.operator.address, pageSize, { timeout }, (pages) => { timeStamp("...batch", pages.length) From a7d487acb0e889ef0abe9c937f44d4315778b716 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 27 Sep 2022 12:17:30 +0100 Subject: [PATCH 656/862] Throw errors from Leap as required --- src/utils/LeapSignerProvider.mjs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/utils/LeapSignerProvider.mjs b/src/utils/LeapSignerProvider.mjs index 2c05e94f4..b6de586a8 100644 --- a/src/utils/LeapSignerProvider.mjs +++ b/src/utils/LeapSignerProvider.mjs @@ -11,7 +11,16 @@ export default class LeapSignerProvider extends SignerProvider { sign: { preferNoSetFee: true } }) } - return super.enable(network) + return super.enable(network).then(result => { + // Leap returns error instead of throwing + if(result instanceof Error){ + throw result + } + }) + } + + suggestChain(network){ + throw new Error(`${network.prettyName} is not supported currently`) } setOptions(options){ From a09bdf0c2e46a9e8ce7db9b5a9e6fbed5326112f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 27 Sep 2022 12:40:33 +0100 Subject: [PATCH 657/862] Improve error handling with Leap wallet --- src/components/App.js | 10 +++------- src/components/DelegateForm.js | 2 +- src/utils/SigningClient.mjs | 2 ++ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 2b5c8ec45..56a62f6fc 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -195,15 +195,11 @@ class App extends React.Component { await signerProvider.suggestChain(network) key = await signerProvider.getKey(network); } catch (e) { - error = e.message + return this.setState({ + error: `Failed to connect to ${signerProvider?.label || 'signer'}: ${e.message}` + }) } } - if(!key || error){ - error = error || 'Chain not supported' - return this.setState({ - error: `Failed to connect to ${signerProvider?.label || 'signer'}: ${error}` - }) - } try { const offlineSigner = await signerProvider.getSigner(network) const wallet = new Wallet(network, offlineSigner, key) diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index ee20c7c72..3cf3fdb09 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -58,7 +58,7 @@ class DelegateForm extends React.Component { this.props.onDelegate() }, (error) => { console.log('Failed to broadcast:', error) - this.setState({ loading: false, error: error.message }) + this.setState({ loading: false, error: `Failed to broadcast: ${error.message}` }) }) } diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 5d99699d5..03d9702fe 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -189,6 +189,8 @@ function SigningClient(network, signer) { // Sign as amino if possible for Ledger and Keplr support const signDoc = makeAminoSignDoc(aminoMsgs, fee, chainId, memo, accountNumber, sequence); const { signature, signed } = await signer.signAmino(address, signDoc); + if(!signed) throw new Error('Invalid response') + const authInfoBytes = await makeAuthInfoBytes(account, { amount: signed.fee.amount, gasLimit: signed.fee.gas, From e58988ad1225a941ec9938148afbb89b7fdde6b3 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 27 Sep 2022 22:43:48 +0100 Subject: [PATCH 658/862] Remove unneeded throws after updates --- src/utils/LeapSignerProvider.mjs | 9 ++------- src/utils/SigningClient.mjs | 2 -- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/utils/LeapSignerProvider.mjs b/src/utils/LeapSignerProvider.mjs index b6de586a8..787a268bf 100644 --- a/src/utils/LeapSignerProvider.mjs +++ b/src/utils/LeapSignerProvider.mjs @@ -11,16 +11,11 @@ export default class LeapSignerProvider extends SignerProvider { sign: { preferNoSetFee: true } }) } - return super.enable(network).then(result => { - // Leap returns error instead of throwing - if(result instanceof Error){ - throw result - } - }) + return super.enable(network) } suggestChain(network){ - throw new Error(`${network.prettyName} is not supported currently`) + throw new Error(`${network.prettyName} is not supported`) } setOptions(options){ diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 03d9702fe..5d99699d5 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -189,8 +189,6 @@ function SigningClient(network, signer) { // Sign as amino if possible for Ledger and Keplr support const signDoc = makeAminoSignDoc(aminoMsgs, fee, chainId, memo, accountNumber, sequence); const { signature, signed } = await signer.signAmino(address, signDoc); - if(!signed) throw new Error('Invalid response') - const authInfoBytes = await makeAuthInfoBytes(account, { amount: signed.fee.amount, gasLimit: signed.fee.gas, From b10d83ef5d577bc2e95a01da820a119fa4e26a56 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 27 Sep 2022 22:45:16 +0100 Subject: [PATCH 659/862] Only lookup needed denom --- src/utils/QueryClient.mjs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 56f013887..d8272e234 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -66,13 +66,10 @@ const QueryClient = async (chainId, restUrls) => { const getBalance = (address, denom, opts) => { return axios - .get(restUrl + "/cosmos/bank/v1beta1/balances/" + address, opts) + .get(`${restUrl}/cosmos/bank/v1beta1/balances/${address}/${denom}`, opts) .then((res) => res.data) .then((result) => { - const balance = result.balances?.find( - (element) => element.denom === denom - ) || { denom: denom, amount: 0 }; - return balance; + return result.balance || { denom: denom, amount: 0 }; }); }; From ab5203367869d2344efacf0fc3e45149ddb3f1dc Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 27 Sep 2022 22:45:26 +0100 Subject: [PATCH 660/862] Use delegationsTimeout for balance --- src/autostake/NetworkRunner.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 2267a1328..476ff06ef 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -91,7 +91,7 @@ export default class NetworkRunner { } getBalance() { - let timeout = this.opts.queryTimeout + let timeout = this.opts.delegationsTimeout return this.queryClient.getBalance(this.operator.botAddress, this.network.denom, { timeout }) .then( (balance) => { From 80c4c3f006993a2c481b58a85728e7e103a8f5ae Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 27 Sep 2022 22:53:45 +0100 Subject: [PATCH 661/862] Revert "Only lookup needed denom" b10d83ef5d577bc2e95a01da820a119fa4e26a56 --- src/utils/QueryClient.mjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index d8272e234..56f013887 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -66,10 +66,13 @@ const QueryClient = async (chainId, restUrls) => { const getBalance = (address, denom, opts) => { return axios - .get(`${restUrl}/cosmos/bank/v1beta1/balances/${address}/${denom}`, opts) + .get(restUrl + "/cosmos/bank/v1beta1/balances/" + address, opts) .then((res) => res.data) .then((result) => { - return result.balance || { denom: denom, amount: 0 }; + const balance = result.balances?.find( + (element) => element.denom === denom + ) || { denom: denom, amount: 0 }; + return balance; }); }; From 82b32444016bfe717f7bfd78d6a14df9761bef2d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 28 Sep 2022 16:39:05 +0100 Subject: [PATCH 662/862] Adjust autostake bulk grants timeout --- src/autostake/NetworkRunner.mjs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 476ff06ef..6a3fcf2c9 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -117,14 +117,14 @@ export default class NetworkRunner { async getGrantedAddresses(addresses) { const { botAddress, address } = this.operator - let timeout = this.opts.queryTimeout + let timeout = this.opts.delegationsTimeout let pageSize = this.opts.batchPageSize let allGrants try { allGrants = await this.queryClient.getGranteeGrants(botAddress, { timeout, pageSize }, (pages) => { - timeStamp("...batch", pages.length) - return this.throttleQuery() - }) + timeStamp("...batch", pages.length) + return this.throttleQuery() + }) } catch (e) { } let grantCalls = addresses.map(item => { return async () => { From 64e9191b1aae25de1892047bae956b6469a6b30f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 28 Sep 2022 20:55:52 +0100 Subject: [PATCH 663/862] Ensure disconnect from WalletConnect --- src/components/App.js | 4 +++- src/utils/KeplrMobileSignerProvider.mjs | 12 ++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 032d7ea7e..eb22085b9 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -230,7 +230,9 @@ class App extends React.Component { wallet, signerProvider, signingClient, - error: false + error: false, + qrCodeUri: null, + qrCodeCallback: null }) } catch (e) { console.log(e) diff --git a/src/utils/KeplrMobileSignerProvider.mjs b/src/utils/KeplrMobileSignerProvider.mjs index acd8b1566..78ad0e55f 100644 --- a/src/utils/KeplrMobileSignerProvider.mjs +++ b/src/utils/KeplrMobileSignerProvider.mjs @@ -19,6 +19,7 @@ export default class KeplrMobileSignerProvider extends SignerProvider { ], qrcodeModal: this.connectModal, }); + this.provider = new KeplrWalletConnectV1(this.connector); } available(){ @@ -44,7 +45,12 @@ export default class KeplrMobileSignerProvider extends SignerProvider { } async disconnect(){ - return this.provider?.clearSaved() + try { + this.provider?.clearSaved() + this.connector.killSession() + } catch (error) { + console.log(error) + } } getSigner(network){ @@ -64,14 +70,12 @@ export default class KeplrMobileSignerProvider extends SignerProvider { if (error) { reject(error); } else { - this.provider = new KeplrWalletConnectV1(this.connector); resolve(); } }); }); } else { - this.provider = new KeplrWalletConnectV1(this.connector); - return Promise.resolve(this.provider); + return Promise.resolve(); } } } \ No newline at end of file From f0483dea71b01db7ff6e56d5b23401757f667e22 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 28 Sep 2022 21:26:10 +0100 Subject: [PATCH 664/862] Add force disconnect option to modal --- src/components/App.js | 9 +++++---- src/components/ConnectWalletModal.js | 13 ++++++++++--- src/utils/KeplrMobileSignerProvider.mjs | 6 +++++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index eb22085b9..d604ceade 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -75,7 +75,7 @@ class App extends React.Component { connectModal: { open: (uri, callback) => { this.setState({ - connectWallet: 'Keplr Mobile', + connectWallet: true, qrCodeUri: uri || this.state.qrCodeUri, qrCodeCallback: callback || this.state.qrCodeCallback }) @@ -200,6 +200,8 @@ class App extends React.Component { return } + this.setState({ signerProvider }) + let key, error try { @@ -228,7 +230,6 @@ class App extends React.Component { this.setState({ address, wallet, - signerProvider, signingClient, error: false, qrCodeUri: null, @@ -851,8 +852,8 @@ class App extends React.Component { setAddress={(value) => this.setState({address: value, showAddressModal: false})} /> this.setState({connectWallet: false})} diff --git a/src/components/ConnectWalletModal.js b/src/components/ConnectWalletModal.js index ff3c552de..b68dfa655 100644 --- a/src/components/ConnectWalletModal.js +++ b/src/components/ConnectWalletModal.js @@ -14,7 +14,7 @@ import { import QRCode from "qrcode.react"; function ConnectWalletModal(props) { - const { show, walletName, uri, callback, onClose } = props + const { show, signerProvider, uri, callback, onClose } = props const [checkMobile] = useState(() => isMobile()); const [checkAndroid] = useState(() => isAndroid()); @@ -48,11 +48,17 @@ function ConnectWalletModal(props) { } }, [navigateToAppURL]); - const handleClose = () => { + function handleClose() { + onClose(); + callback && callback(); + } + + function forceDisconnect(){ + signerProvider?.forceDisconnect() onClose() - callback && callback() } + const walletName = signerProvider?.label || 'Wallet' const title = !checkMobile && uri ? 'Scan QR Code' : `Connect ${walletName}` return ( @@ -87,6 +93,7 @@ function ConnectWalletModal(props) { Loading... +

  15. )} diff --git a/src/utils/KeplrMobileSignerProvider.mjs b/src/utils/KeplrMobileSignerProvider.mjs index 78ad0e55f..e1eda1fc9 100644 --- a/src/utils/KeplrMobileSignerProvider.mjs +++ b/src/utils/KeplrMobileSignerProvider.mjs @@ -45,8 +45,12 @@ export default class KeplrMobileSignerProvider extends SignerProvider { } async disconnect(){ + return this.provider?.clearSaved() + } + + async forceDisconnect(){ try { - this.provider?.clearSaved() + this.disconnect() this.connector.killSession() } catch (error) { console.log(error) From ed4c18dd8d4fb1168efc98471ad0d50c03259147 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 28 Sep 2022 23:34:37 +0100 Subject: [PATCH 665/862] Improve connection flow --- src/components/App.js | 20 ++++----- src/components/ConnectWalletModal.js | 60 ++++++++++++------------- src/utils/KeplrMobileSignerProvider.mjs | 40 ++++++++++------- src/utils/SignerProvider.mjs | 24 +++++++--- 4 files changed, 79 insertions(+), 65 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index d604ceade..5539d7bb4 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -202,20 +202,16 @@ class App extends React.Component { this.setState({ signerProvider }) - let key, error - + let key try { - await signerProvider.enable(network) - key = await signerProvider.getKey(network); + key = await signerProvider.connect(network); } catch (e) { - try { - await signerProvider.suggestChain(network) - key = await signerProvider.getKey(network); - } catch (e) { - return this.setState({ - error: `Failed to connect to ${signerProvider?.label || 'signer'}: ${e.message}` - }) - } + return this.setState({ + error: `Failed to connect to ${signerProvider?.label || 'signer'}: ${e.message}`, + address: null, + wallet: null, + signingClient: null + }) } try { const offlineSigner = await signerProvider.getSigner(network) diff --git a/src/components/ConnectWalletModal.js b/src/components/ConnectWalletModal.js index b68dfa655..729746b8f 100644 --- a/src/components/ConnectWalletModal.js +++ b/src/components/ConnectWalletModal.js @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useEffect } from 'react'; +import React, { useState, useMemo } from 'react'; import { Modal, @@ -19,43 +19,37 @@ function ConnectWalletModal(props) { const [checkAndroid] = useState(() => isAndroid()); const navigateToAppURL = useMemo(() => { - if (uri && checkMobile) { + if (checkMobile) { if (checkAndroid) { + const base = "intent://wcV1#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;" // Save the mobile link. saveMobileLinkInfo({ name: "Keplr", - href: - "intent://wcV1#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;", + href: base, }); - return `intent://wcV1?${uri}#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;`; + return uri ? `intent://wcV1?${uri}#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;` : base } else { // Save the mobile link. + const base = "keplrwallet://wcV1" saveMobileLinkInfo({ name: "Keplr", - href: "keplrwallet://wcV1", + href: base, }); - return `keplrwallet://wcV1?${uri}`; + return uri ? `keplrwallet://wcV1?${uri}` : base } } }, [checkAndroid, checkMobile, uri]); - useEffect(() => { - // Try opening the app without interaction. - if (uri && show && navigateToAppURL) { - window.location.href = navigateToAppURL; - } - }, [navigateToAppURL]); - function handleClose() { onClose(); callback && callback(); } - function forceDisconnect(){ + function forceDisconnect() { signerProvider?.forceDisconnect() - onClose() + handleClose() } const walletName = signerProvider?.label || 'Wallet' @@ -71,8 +65,9 @@ function ConnectWalletModal(props) { - {uri ? ( - checkMobile ? ( +
    +

    {`Open your ${walletName} app to continue...`}

    + {checkMobile ? ( ) : ( -
    - -
    - ) - ) : ( -
    -

    {`Open your ${walletName} app to continue...`}

    - - Loading... - -

    -
    - )} + uri ? ( +
    + +
    + ) : ( + + Loading... + + ) + )} + {!uri && ( +

    + +

    + )} +
    diff --git a/src/utils/KeplrMobileSignerProvider.mjs b/src/utils/KeplrMobileSignerProvider.mjs index e1eda1fc9..3fb251d98 100644 --- a/src/utils/KeplrMobileSignerProvider.mjs +++ b/src/utils/KeplrMobileSignerProvider.mjs @@ -8,7 +8,7 @@ export default class KeplrMobileSignerProvider extends SignerProvider { label = 'Keplr Mobile' keychangeEvent = 'keplr_keystorechange' - constructor({ connectModal }){ + constructor({ connectModal }) { super() this.connectModal = connectModal this.connector = new WalletConnect({ @@ -19,36 +19,43 @@ export default class KeplrMobileSignerProvider extends SignerProvider { ], qrcodeModal: this.connectModal, }); - this.provider = new KeplrWalletConnectV1(this.connector); } - available(){ + available() { return true } - connected(){ + connected() { return this.connector.connected } - suggestChain(network){ + suggestChain(network) { throw new Error(`${network.prettyName} (${network.chainId}) is not supported`) } - async enable(network){ - this.connectModal.open() + async connect(network) { + let key try { - await this.createSession() - await this.provider.enable(network.chainId) - } finally { + this.connectModal.open() + key = await super.connect(network) this.connectModal.close() + return key + } catch (error) { + this.connectModal.close() + throw(error) } } - async disconnect(){ + async enable(network) { + await this.createSession() + await this.provider.enable(network.chainId) + } + + async disconnect() { return this.provider?.clearSaved() } - async forceDisconnect(){ + async forceDisconnect() { try { this.disconnect() this.connector.killSession() @@ -57,28 +64,27 @@ export default class KeplrMobileSignerProvider extends SignerProvider { } } - getSigner(network){ + getSigner(network) { const { chainId } = network // return this.provider.getOfflineSignerAuto(chainId) // no signDirect support currently return this.provider.getOfflineSignerOnlyAmino(chainId) } - async createSession(){ - // Check if connection is already established + async createSession() { if (!this.connector.connected) { - // create new session this.connector.createSession(); - return new Promise((resolve, reject) => { this.connector.on("connect", (error) => { if (error) { reject(error); } else { + this.provider = new KeplrWalletConnectV1(this.connector); resolve(); } }); }); } else { + this.provider = new KeplrWalletConnectV1(this.connector); return Promise.resolve(); } } diff --git a/src/utils/SignerProvider.mjs b/src/utils/SignerProvider.mjs index ad24a7901..45d47b965 100644 --- a/src/utils/SignerProvider.mjs +++ b/src/utils/SignerProvider.mjs @@ -11,17 +11,26 @@ export default class SignerProvider { return this.available() } - enable(network){ - const { chainId } = network - return this.provider.enable(chainId) + async connect(network){ + try { + await this.enable(network) + return await this.getKey(network) + } catch (e) { + try { + await this.suggestChain(network) + return await this.getKey(network) + } catch (s) { + throw(e) + } + } } disconnect(){ } - getSigner(network){ + enable(network){ const { chainId } = network - return this.provider.getOfflineSignerAuto(chainId) + return this.provider.enable(chainId) } getKey(network){ @@ -29,6 +38,11 @@ export default class SignerProvider { return this.provider.getKey(chainId) } + getSigner(network){ + const { chainId } = network + return this.provider.getOfflineSignerAuto(chainId) + } + suggestChain(network){ return this.provider.experimentalSuggestChain(network.suggestChain()) } From 2464b73cce39702ca0f2b6bf4f845f56eae31a1f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 28 Sep 2022 23:48:40 +0100 Subject: [PATCH 666/862] Mobile fixes --- src/components/ConnectWalletModal.js | 22 ++++------------------ src/index.html | 4 ++-- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/components/ConnectWalletModal.js b/src/components/ConnectWalletModal.js index 729746b8f..457c78dcf 100644 --- a/src/components/ConnectWalletModal.js +++ b/src/components/ConnectWalletModal.js @@ -8,8 +8,7 @@ import { import { isMobile, - isAndroid, - saveMobileLinkInfo, + isAndroid } from "@walletconnect/browser-utils"; import QRCode from "qrcode.react"; @@ -20,24 +19,11 @@ function ConnectWalletModal(props) { const navigateToAppURL = useMemo(() => { if (checkMobile) { + const uriStr = uri ? `?${uri}` : '' if (checkAndroid) { - const base = "intent://wcV1#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;" - // Save the mobile link. - saveMobileLinkInfo({ - name: "Keplr", - href: base, - }); - - return uri ? `intent://wcV1?${uri}#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;` : base + return `intent://wcV1${uriStr}#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;` } else { - // Save the mobile link. - const base = "keplrwallet://wcV1" - saveMobileLinkInfo({ - name: "Keplr", - href: base, - }); - - return uri ? `keplrwallet://wcV1?${uri}` : base + return `keplrwallet://wcV1${uriStr}` } } }, [checkAndroid, checkMobile, uri]); diff --git a/src/index.html b/src/index.html index ce38c1484..567411646 100644 --- a/src/index.html +++ b/src/index.html @@ -3,8 +3,8 @@ - - + + Date: Thu, 29 Sep 2022 18:39:22 +0100 Subject: [PATCH 667/862] Add timeout option for availableUrl check --- src/autostake/NetworkRunner.mjs | 6 ++---- src/autostake/index.mjs | 2 +- src/utils/Network.mjs | 4 ++-- src/utils/QueryClient.mjs | 11 +++++++---- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 6a3fcf2c9..f2c4821c9 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -11,7 +11,7 @@ export default class NetworkRunner { this.operator = operator this.signingClient = signingClient this.queryClient = network.queryClient - this.opts = { + this.opts = _.merge({ batchPageSize: 100, batchQueries: 25, batchTxs: 50, @@ -19,9 +19,7 @@ export default class NetworkRunner { queryTimeout: network.data.autostake?.delegatorTimeout || 5000, // deprecate delegatorTimeout queryThrottle: 100, gasModifier: 1.1, - ...network.data.autostake, - ...opts - } + }, network.data.autostake, opts) this.batch = {} this.messages = [] this.processed = {} diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 3a78b3420..1fecb1f70 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -132,7 +132,7 @@ export default function Autostake(mnemonic, opts) { if (!network.authzSupport) return timeStamp('No Authz support') - await network.connect() + await network.connect({ timeout: opts.delegationsTimeout || 20000 }) const { restUrl, usingDirectory } = network diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index b9236d366..8b47b2727 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -102,9 +102,9 @@ class Network { this.txTimeout = this.data.txTimeout || 60_000 } - async connect() { + async connect({ timeout }) { try { - this.queryClient = await QueryClient(this.chain.chainId, this.restUrl) + this.queryClient = await QueryClient(this.chain.chainId, this.restUrl, { connectTimeout: timeout }) this.restUrl = this.queryClient.restUrl this.connected = this.queryClient.connected && (!this.usingDirectory || this.connectedDirectory()) } catch (error) { diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 56f013887..c237e9e1b 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -1,8 +1,11 @@ import axios from "axios"; import _ from "lodash"; -const QueryClient = async (chainId, restUrls) => { - let restUrl = await findAvailableUrl(restUrls, "rest") +const QueryClient = async (chainId, restUrls, opts) => { + const config = _.merge({ + connectTimeout: 10000, + }, opts) + const restUrl = await findAvailableUrl(restUrls, "rest", { timeout: config.connectTimeout }) const getAllValidators = (pageSize, opts, pageCallback) => { return getAllPages((nextKey) => { @@ -209,7 +212,7 @@ const QueryClient = async (chainId, restUrls) => { return pages; }; - async function findAvailableUrl(urls, type) { + async function findAvailableUrl(urls, type, { timeout }) { if(!urls) return if (!Array.isArray(urls)) { @@ -223,7 +226,7 @@ const QueryClient = async (chainId, restUrls) => { return Promise.any(urls.map(async (url) => { url = url.replace(/\/$/, '') try { - let data = await axios.get(url + path, { timeout: 10000 }) + let data = await axios.get(url + path, { timeout }) .then((res) => res.data) if (type === "rpc") data = data.result; if (data.block?.header?.chain_id === chainId) { From 9bd781c66704f2c3b3cba2a344c5df6478a54845 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 29 Sep 2022 00:31:47 +0100 Subject: [PATCH 668/862] Link directly to validator profile tabs --- src/components/ValidatorModal.js | 16 +++++++++++----- src/index.js | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index a06096857..38b57a2a2 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -19,24 +19,30 @@ import ValidatorGrants from './ValidatorGrants'; function ValidatorModal(props) { const { redelegate, undelegate, validator, delegations, operators, network, validators, grants } = props - const [selectedValidator, setSelectedValidator] = useState(!redelegate && validator); - const [activeTab, setActiveTab] = useState(); const navigate = useNavigate() const params = useParams(); + const [selectedValidator, setSelectedValidator] = useState(!redelegate && validator); + const [activeTab, setActiveTab] = useState(params.section); useEffect(() => { if(params.network !== network.name) return - if (props.show && selectedValidator && validator?.operator_address === selectedValidator.operator_address && params.validator !== selectedValidator.operator_address) { - navigate(`/${network.name}/${selectedValidator.operator_address}`) + const shouldShow = props.show && selectedValidator && validator?.operator_address === selectedValidator.operator_address + const shouldChangeValidator = params.validator !== selectedValidator?.operator_address + const shouldChangeTab = activeTab === 'profile' ? !!params.section : params.section !== activeTab + const shouldChangeUrl = shouldShow && (shouldChangeValidator || shouldChangeTab) + if (shouldChangeUrl) { + navigate(`/${network.name}/${selectedValidator.operator_address}${activeTab === 'profile' ? '' : `/${activeTab}`}`) } else if (params.validator && props.show === false) { navigate(`/${network.name}`) } - }, [props.show, params.validator, selectedValidator]) + }, [props.show, params.validator, params.section, activeTab, selectedValidator]) useEffect(() => { if (props.activeTab && props.activeTab != activeTab) { setActiveTab(props.activeTab) + } else if (params.section && params.section != activeTab) { + setActiveTab(params.section) } else if (redelegate || undelegate) { setActiveTab('delegate') } else if (!activeTab) { diff --git a/src/index.js b/src/index.js index 2531b46b3..9b6914ab3 100644 --- a/src/index.js +++ b/src/index.js @@ -24,6 +24,7 @@ const app = ( } /> } /> } /> + } /> From aa8ced443ee145107df3ab713b5527e1964b74a4 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 30 Sep 2022 15:14:17 +0100 Subject: [PATCH 669/862] Update Chihuahua gas prices --- src/networks.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 468b357e6..3b593eca1 100644 --- a/src/networks.json +++ b/src/networks.json @@ -21,8 +21,9 @@ }, { "name": "chihuahua", - "gasPrice": "0.025uhuahua", - "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz" + "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", + "gasPrice": "1uhuahua", + "gasPricePrefer": "1uhuahua" }, { "name": "gravitybridge", From 12b8d1df64a69f1b302d619a3f5021fc8720683f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 30 Sep 2022 15:25:26 +0100 Subject: [PATCH 670/862] Handle no opts for connect --- src/utils/Network.mjs | 4 ++-- src/utils/QueryClient.mjs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 8b47b2727..7be0eabc5 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -102,9 +102,9 @@ class Network { this.txTimeout = this.data.txTimeout || 60_000 } - async connect({ timeout }) { + async connect(opts) { try { - this.queryClient = await QueryClient(this.chain.chainId, this.restUrl, { connectTimeout: timeout }) + this.queryClient = await QueryClient(this.chain.chainId, this.restUrl, { connectTimeout: opts?.timeout }) this.restUrl = this.queryClient.restUrl this.connected = this.queryClient.connected && (!this.usingDirectory || this.connectedDirectory()) } catch (error) { diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index c237e9e1b..f645a0fc8 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -212,7 +212,7 @@ const QueryClient = async (chainId, restUrls, opts) => { return pages; }; - async function findAvailableUrl(urls, type, { timeout }) { + async function findAvailableUrl(urls, type, opts) { if(!urls) return if (!Array.isArray(urls)) { @@ -223,6 +223,7 @@ const QueryClient = async (chainId, restUrls, opts) => { } } const path = type === "rest" ? "/blocks/latest" : "/block"; + const { timeout } = opts || {} return Promise.any(urls.map(async (url) => { url = url.replace(/\/$/, '') try { From 90b98ed4b6a49dd2682251aebbba8a9662ceae2a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 3 Oct 2022 12:54:53 +0100 Subject: [PATCH 671/862] Mobile UI adjustments --- src/components/ManageRestake.js | 5 +++-- src/components/ValidatorGrants.js | 10 +++++----- src/components/Validators.js | 23 ++++++++++++----------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/components/ManageRestake.js b/src/components/ManageRestake.js index 1eaacff37..379c46ee7 100644 --- a/src/components/ManageRestake.js +++ b/src/components/ManageRestake.js @@ -5,7 +5,7 @@ import TooltipIcon from "./TooltipIcon"; import { Button, OverlayTrigger, Tooltip } from "react-bootstrap"; -import { CheckCircle, XCircle } from "react-bootstrap-icons"; +import { CheckCircle, XCircle, WrenchAdjustable, Magic } from "react-bootstrap-icons"; function ManageRestake(props) { const { validator, operator, delegation, grants, network, authzSupport, restakePossible } = props @@ -33,7 +33,8 @@ function ManageRestake(props) { } > ) : ( diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index b8e153973..224ac8009 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -227,11 +227,7 @@ function ValidatorGrants(props) { )} {props.restakePossible && (
    -
    -

    Grant details

    - {grantInformation()} -
    -
    +
    @@ -280,6 +276,10 @@ function ValidatorGrants(props) {
    +
    +

    Grant details

    + {grantInformation()} +
    )} diff --git a/src/components/Validators.js b/src/components/Validators.js index 75deb51d2..c188968f6 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -20,7 +20,7 @@ import { Nav, Spinner } from 'react-bootstrap' -import { XCircle } from "react-bootstrap-icons"; +import { XCircle, Gear } from "react-bootstrap-icons"; import ValidatorName from "./ValidatorName"; import ManageRestake from "./ManageRestake"; @@ -233,7 +233,7 @@ function Validators(props) { {format(validator.commission.commission_rates.rate * 100, 2)}% - + {delegations ? ( )} - {!props.modal && ( - + {filter.group === 'delegated' && !props.modal && ( + {props.rewards ? denomRewards && ( - Manage + + Manage {operator && @@ -499,9 +500,9 @@ function Validators(props) { )} Fee - Delegation - {!props.modal && ( - Rewards + Delegation + {filter.group === 'delegated' && !props.modal && ( + Rewards )} {!props.modal && showCommission && ( Commission @@ -522,7 +523,7 @@ function Validators(props) { )} - + - {!props.modal && ( - + {filter.group === 'delegated' && !props.modal && ( + {props.rewards && ( Date: Mon, 3 Oct 2022 19:50:06 +0100 Subject: [PATCH 672/862] Add Territori --- src/networks.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/networks.json b/src/networks.json index 3b593eca1..aa50d4cfc 100644 --- a/src/networks.json +++ b/src/networks.json @@ -238,5 +238,14 @@ { "name": "pulsar", "testnet": true + }, + { + "name": "teritori", + "ownerAddress": "torivaloper1d5u07lhelk6lal44a0myvufurvsqk5d499h9hz", + "gasPriceStep": { + "low": 0, + "average": 0.025, + "high": 0.04 + } } ] From da4d6639f8bc91b2cbf781e9d1685e440a7ba616 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 4 Oct 2022 09:08:52 +0100 Subject: [PATCH 673/862] Enable Osmosis ledger support --- src/networks.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index aa50d4cfc..93a53a3ad 100644 --- a/src/networks.json +++ b/src/networks.json @@ -4,7 +4,8 @@ "gasPrice": "0.0025uosmo", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "default": true, - "maxPerDay": 1 + "maxPerDay": 1, + "ledgerAuthzSupport": true }, { "name": "juno", From 41fcfcc5570eafe1f5f579b5ffaf9ae22464b6e4 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 4 Oct 2022 12:30:37 +0100 Subject: [PATCH 674/862] Handle missing coinGeckoId when suggesting chain --- src/utils/Network.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 7be0eabc5..91b77c122 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -167,8 +167,10 @@ class Network { const currency = { coinDenom: this.symbol, coinMinimalDenom: this.denom, - coinDecimals: this.decimals, - coinGeckoId: this.coinGeckoId + coinDecimals: this.decimals + } + if(this.coinGeckoId){ + currency.coinGeckoId = this.coinGeckoId } const data = { rpc: this.rpcUrl, From 62f8eb836a7ccecd99d0e18e9441834fde38c9b7 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 4 Oct 2022 13:32:01 +0100 Subject: [PATCH 675/862] Use proxy status when checking directory health --- src/utils/Network.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 91b77c122..90bf4b017 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -40,8 +40,8 @@ class Network { } connectedDirectory() { - const apis = this.chain ? this.chain.data['best_apis'] : this.data['best_apis'] - return apis && ['rest'].every(type => apis[type].length > 0) + const proxy_status = this.chain ? this.chain.data['proxy_status'] : this.data['proxy_status'] + return proxy_status && ['rest'].every(type => proxy_status[type]) } estimateOperatorCount() { From bcfea7aae7e118024b35e3b7270ebc21861fb91c Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 4 Oct 2022 19:58:03 +0100 Subject: [PATCH 676/862] Adjust workflow to improve lack of suggest support --- src/components/App.js | 21 ++++++++++++++------- src/utils/LeapSignerProvider.mjs | 5 +---- src/utils/SignerProvider.mjs | 8 +++++++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 56a62f6fc..c0a25b3d1 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -191,15 +191,22 @@ class App extends React.Component { await signerProvider.enable(network) key = await signerProvider.getKey(network); } catch (e) { - try { - await signerProvider.suggestChain(network) - key = await signerProvider.getKey(network); - } catch (e) { - return this.setState({ - error: `Failed to connect to ${signerProvider?.label || 'signer'}: ${e.message}` - }) + if(signerProvider.suggestChainSupport){ + try { + await signerProvider.suggestChain(network) + key = await signerProvider.getKey(network); + } catch (e) { + error = e + } + }else{ + error = e } } + if(error){ + return this.setState({ + error: `Failed to connect to ${signerProvider?.label || 'signer'}: ${error.message}` + }) + } try { const offlineSigner = await signerProvider.getSigner(network) const wallet = new Wallet(network, offlineSigner, key) diff --git a/src/utils/LeapSignerProvider.mjs b/src/utils/LeapSignerProvider.mjs index 787a268bf..8ee1368f5 100644 --- a/src/utils/LeapSignerProvider.mjs +++ b/src/utils/LeapSignerProvider.mjs @@ -4,6 +4,7 @@ export default class LeapSignerProvider extends SignerProvider { key = 'leap' label = 'Leap Wallet' keychangeEvent = 'leap_keystorechange' + suggestChainSupport = false enable(network){ if (network.gasPricePrefer) { @@ -14,10 +15,6 @@ export default class LeapSignerProvider extends SignerProvider { return super.enable(network) } - suggestChain(network){ - throw new Error(`${network.prettyName} is not supported`) - } - setOptions(options){ return this.provider.defaultOptions = options } diff --git a/src/utils/SignerProvider.mjs b/src/utils/SignerProvider.mjs index 0cf9b60b5..91a4cda2b 100644 --- a/src/utils/SignerProvider.mjs +++ b/src/utils/SignerProvider.mjs @@ -1,4 +1,6 @@ export default class SignerProvider { + suggestChainSupport = true + constructor(provider){ this.provider = provider } @@ -23,7 +25,11 @@ export default class SignerProvider { } suggestChain(network){ - return this.provider.experimentalSuggestChain(network.suggestChain()) + if(this.suggestChainSupport){ + return this.provider.experimentalSuggestChain(network.suggestChain()) + }else{ + throw new Error(`${network.prettyName} is not supported`) + } } setOptions(options){ From e17d9c543ec48018308296572a3a924b8b502535 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 5 Oct 2022 16:45:12 +0100 Subject: [PATCH 677/862] Disable Keplr Mobile for merge --- src/components/App.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 5539d7bb4..70993dcb9 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -71,20 +71,20 @@ class App extends React.Component { } this.signerProviders = [ new KeplrSignerProvider(window.keplr), - new KeplrMobileSignerProvider({ - connectModal: { - open: (uri, callback) => { - this.setState({ - connectWallet: true, - qrCodeUri: uri || this.state.qrCodeUri, - qrCodeCallback: callback || this.state.qrCodeCallback - }) - }, - close: () => { - this.setState({ connectWallet: false }) - } - } - }), + // new KeplrMobileSignerProvider({ + // connectModal: { + // open: (uri, callback) => { + // this.setState({ + // connectWallet: true, + // qrCodeUri: uri || this.state.qrCodeUri, + // qrCodeCallback: callback || this.state.qrCodeCallback + // }) + // }, + // close: () => { + // this.setState({ connectWallet: false }) + // } + // } + // }), new LeapSignerProvider(window.leap), // new FalconSignerProvider(window.falcon) ] From 347689b299051e4b4aaf56b4483b3d5370510bfe Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 5 Oct 2022 16:55:55 +0100 Subject: [PATCH 678/862] Enable Keplr mobile --- src/components/App.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 70993dcb9..5539d7bb4 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -71,20 +71,20 @@ class App extends React.Component { } this.signerProviders = [ new KeplrSignerProvider(window.keplr), - // new KeplrMobileSignerProvider({ - // connectModal: { - // open: (uri, callback) => { - // this.setState({ - // connectWallet: true, - // qrCodeUri: uri || this.state.qrCodeUri, - // qrCodeCallback: callback || this.state.qrCodeCallback - // }) - // }, - // close: () => { - // this.setState({ connectWallet: false }) - // } - // } - // }), + new KeplrMobileSignerProvider({ + connectModal: { + open: (uri, callback) => { + this.setState({ + connectWallet: true, + qrCodeUri: uri || this.state.qrCodeUri, + qrCodeCallback: callback || this.state.qrCodeCallback + }) + }, + close: () => { + this.setState({ connectWallet: false }) + } + } + }), new LeapSignerProvider(window.leap), // new FalconSignerProvider(window.falcon) ] From fc482125d993e4efb7950673f24a6d0952712fac Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 5 Oct 2022 17:07:05 +0100 Subject: [PATCH 679/862] Enable Umee Authz Amino support --- src/networks.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index fd70f099f..121739233 100644 --- a/src/networks.json +++ b/src/networks.json @@ -102,7 +102,8 @@ "gasPrice": "25ncheq" }, { - "name": "umee" + "name": "umee", + "authzAminoSupport": true }, { "name": "bitsong" From 67ef89d7b54176402146c50102ed7cc485e2dbee Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 5 Oct 2022 17:34:36 +0100 Subject: [PATCH 680/862] Disable Osmosis amino authz MsgExec --- src/components/AboutLedger.js | 2 +- src/components/GrantModal.js | 2 +- src/networks.json | 3 ++- src/utils/Chain.mjs | 7 +++---- src/utils/Network.mjs | 3 ++- src/utils/SigningClient.mjs | 4 ++++ 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/components/AboutLedger.js b/src/components/AboutLedger.js index 67e72f237..9e4098118 100644 --- a/src/components/AboutLedger.js +++ b/src/components/AboutLedger.js @@ -10,7 +10,7 @@ function AboutLedger(props) { const aboutParam = searchParams.get('about') const { network, validator } = props - const { daemon_name, chain_id, node_home, codebase } = network.chain.data + const { daemon_name, chain_id, node_home, codebase } = network.chain const { git_repo, binaries } = codebase || {} const address = validator?.restake?.address || GrantAddress diff --git a/src/components/GrantModal.js b/src/components/GrantModal.js index f23eeb0f7..418a4333c 100644 --- a/src/components/GrantModal.js +++ b/src/components/GrantModal.js @@ -26,7 +26,7 @@ function GrantModal(props) { const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry.format('YYYY-MM-DD') }); const [showLedger, setShowLedger] = useState(!walletAuthzSupport) - const { daemon_name, chain_id } = network.chain.data + const { daemon_name, chain_id } = network.chain useEffect(() => { setState({ diff --git a/src/networks.json b/src/networks.json index 121739233..d42a2d2e2 100644 --- a/src/networks.json +++ b/src/networks.json @@ -5,7 +5,8 @@ "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "default": true, "maxPerDay": 1, - "authzAminoSupport": true + "authzAminoSupport": true, + "authzAminoExecSupport": false }, { "name": "juno", diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index 9ce4c287e..a86369dc3 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -5,6 +5,7 @@ const Chain = (data) => { const baseAsset = assets[0] return { + ...data, prettyName: data.prettyName || data.pretty_name, chainId: data.chainId || data.chain_id, prefix: data.prefix || data.bech32_prefix, @@ -12,17 +13,15 @@ const Chain = (data) => { estimatedApr: data.params?.calculated_apr, authzSupport: data.authzSupport ?? data.params?.authz, authzAminoSupport: data.authzAminoSupport ?? false, + authzAminoExecSupport: data.authzAminoExecSupport ?? data.authzAminoSupport ?? false, denom: data.denom || baseAsset?.base?.denom, display: data.display || baseAsset?.display?.denom, symbol: data.symbol || baseAsset?.symbol, decimals: data.decimals || baseAsset?.decimals, image: data.image || baseAsset?.image, coinGeckoId: baseAsset?.coingecko_id, - services: data.services, - explorers: data.explorers, assets, - baseAsset, - data + baseAsset } } diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 50eb2e238..71ae1af55 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -40,7 +40,7 @@ class Network { } connectedDirectory() { - const proxy_status = this.chain ? this.chain.data['proxy_status'] : this.data['proxy_status'] + const proxy_status = this.chain ? this.chain['proxy_status'] : this.data['proxy_status'] return proxy_status && ['rest'].every(type => proxy_status[type]) } @@ -87,6 +87,7 @@ class Network { this.apyEnabled = data.apyEnabled !== false && !!this.estimatedApr && this.estimatedApr > 0 this.authzSupport = this.chain.authzSupport this.authzAminoSupport = this.chain.authzAminoSupport + this.authzAminoExecSupport = this.chain.authzAminoExecSupport this.defaultGasPrice = this.decimals && format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom this.gasPrice = this.data.gasPrice || this.defaultGasPrice if(this.gasPrice){ diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 54cd828d0..2ef8a5e74 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -241,6 +241,10 @@ function SigningClient(network, signer) { if(message.typeUrl.startsWith('/cosmos.authz') && !network.authzAminoSupport){ throw new Error('This chain does not support amino conversion for Authz messages') } + if(message.typeUrl === '/cosmos.authz.v1beta1.MsgExec' && !network.authzAminoExecSupport){ + // Osmosis MsgExec is broken with Amino currently + throw new Error('This chain does not support amino conversion for Authz Exec messages') + } return aminoTypes.toAmino(message) }) } From d527c0015facc97bec2c14f73791ecb9debfb470 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 5 Oct 2022 17:37:35 +0100 Subject: [PATCH 681/862] Replace deprecated blocks endpoint with new --- src/utils/QueryClient.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index f645a0fc8..8272fe20e 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -222,7 +222,7 @@ const QueryClient = async (chainId, restUrls, opts) => { urls = [urls] } } - const path = type === "rest" ? "/blocks/latest" : "/block"; + const path = type === "rest" ? "/cosmos/base/tendermint/v1beta1/blocks/latest" : "/block"; const { timeout } = opts || {} return Promise.any(urls.map(async (url) => { url = url.replace(/\/$/, '') From 7845235286579724daea38976d0fd6db7b62cec5 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 5 Oct 2022 19:17:34 +0100 Subject: [PATCH 682/862] Adjust Osmosis MsgExec amino limit --- src/networks.json | 3 +-- src/utils/Chain.mjs | 1 - src/utils/Network.mjs | 1 - src/utils/SigningClient.mjs | 11 +++++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/networks.json b/src/networks.json index d42a2d2e2..121739233 100644 --- a/src/networks.json +++ b/src/networks.json @@ -5,8 +5,7 @@ "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "default": true, "maxPerDay": 1, - "authzAminoSupport": true, - "authzAminoExecSupport": false + "authzAminoSupport": true }, { "name": "juno", diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index a86369dc3..b33cc96db 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -13,7 +13,6 @@ const Chain = (data) => { estimatedApr: data.params?.calculated_apr, authzSupport: data.authzSupport ?? data.params?.authz, authzAminoSupport: data.authzAminoSupport ?? false, - authzAminoExecSupport: data.authzAminoExecSupport ?? data.authzAminoSupport ?? false, denom: data.denom || baseAsset?.base?.denom, display: data.display || baseAsset?.display?.denom, symbol: data.symbol || baseAsset?.symbol, diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 71ae1af55..ff43b2f68 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -87,7 +87,6 @@ class Network { this.apyEnabled = data.apyEnabled !== false && !!this.estimatedApr && this.estimatedApr > 0 this.authzSupport = this.chain.authzSupport this.authzAminoSupport = this.chain.authzAminoSupport - this.authzAminoExecSupport = this.chain.authzAminoExecSupport this.defaultGasPrice = this.decimals && format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom this.gasPrice = this.data.gasPrice || this.defaultGasPrice if(this.gasPrice){ diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 2ef8a5e74..3bfc5981d 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -184,7 +184,7 @@ function SigningClient(network, signer) { let aminoMsgs try { aminoMsgs = convertToAmino(messages) - } catch (e) { } + } catch (e) { console.log(e) } if(aminoMsgs && signer.signAmino){ // Sign as amino if possible for Ledger and Keplr support const signDoc = makeAminoSignDoc(aminoMsgs, fee, chainId, memo, accountNumber, sequence); @@ -241,9 +241,12 @@ function SigningClient(network, signer) { if(message.typeUrl.startsWith('/cosmos.authz') && !network.authzAminoSupport){ throw new Error('This chain does not support amino conversion for Authz messages') } - if(message.typeUrl === '/cosmos.authz.v1beta1.MsgExec' && !network.authzAminoExecSupport){ - // Osmosis MsgExec is broken with Amino currently - throw new Error('This chain does not support amino conversion for Authz Exec messages') + if(message.typeUrl === '/cosmos.authz.v1beta1.MsgExec' && network.path === 'osmosis'){ + // Osmosis MsgExec gov is broken with Amino currently + // See https://github.com/osmosis-labs/cosmos-sdk/pull/342 + if(message.value.msgs.some(msg => msg.typeUrl.startsWith('/cosmos.gov'))){ + throw new Error('Osmosis does not support amino conversion for Authz Exec gov messages') + } } return aminoTypes.toAmino(message) }) From d76bc17ffd8c625784b8c12d0a50e3417356c7c8 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 6 Oct 2022 00:40:01 +0100 Subject: [PATCH 683/862] Improve authz messaging --- src/components/Grants.js | 2 +- src/components/NetworkChecks.js | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/components/Grants.js b/src/components/Grants.js index e3d22d5bc..6e473b147 100644 --- a/src/components/Grants.js +++ b/src/components/Grants.js @@ -202,7 +202,7 @@ function Grants(props) { const alerts = ( <> {!props.grantQuerySupport && ( - This network doesn't fully support this feature just yet. Save addresses to see them here. + Grants cannot be queried on this network yet. Save addresses first to see their grants. )} {props.grantQuerySupport && !walletAuthzSupport && ( 0){ - testedCheck.failTitle = testedCheck.title, - testedCheck.failDescription = "Authz is disabled but all other features have been fully tested." + if(!network.authzSupport && !network.experimental){ + testedCheck.description = "Authz is disabled but all other features have been fully tested." } return ( @@ -60,7 +59,7 @@ function NetworkChecks(props) { {([ renderCheck({ title: {`$${price && price.usd.toLocaleString(undefined, { maximumFractionDigits: 8, minimumFractionDigits: 2 })}`}, - failTitle: 'Price Unknown', + failTitle: 'Price unknown', state: price?.usd, identifier: 'price', icon: , @@ -68,7 +67,7 @@ function NetworkChecks(props) { }), renderCheck({ title: {`${Math.round(network.estimatedApr * 100).toLocaleString()}% APR`}, - failTitle: 'APR Unknown', + failTitle: 'APR unknown', state: network.estimatedApr, identifier: 'apr', }), @@ -81,8 +80,8 @@ function NetworkChecks(props) { identifier: 'network' }), renderCheck({ - title: 'Authz support', - failTitle: 'Authz unsupported', + title: network.authzAminoSupport ? Full Authz support : 'Authz support', + failTitle: 'Authz not supported', failDescription: "This network doesn't support Authz just yet. You can stake and compound manually, REStake will update automatically when support is added.", state: network.authzSupport, identifier: 'authz' @@ -94,7 +93,7 @@ function NetworkChecks(props) { }), renderCheck({ ...testedCheck, - state: network.authzSupport && !network.experimental, + state: !network.experimental, identifier: 'experimental' }), ])} From ea9bdba06be344c7948815611bdb62fc59965448 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 6 Oct 2022 00:40:47 +0100 Subject: [PATCH 684/862] Search network keywords --- src/components/Networks.js | 2 +- src/utils/Network.mjs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/Networks.js b/src/components/Networks.js index ec4ac4570..9feaf5fda 100644 --- a/src/components/Networks.js +++ b/src/components/Networks.js @@ -48,7 +48,7 @@ function Networks(props) { if (!keywords || keywords === '') return searchResults const searcher = new FuzzySearch( - searchResults, ['prettyName'], + searchResults, ['prettyName', 'keywords'], { sort: true } ) diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index ff43b2f68..7fa54ee55 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -100,6 +100,7 @@ class Network { this.gasPricePrefer = this.data.gasPricePrefer this.gasModifier = this.data.gasModifier || 1.5 this.txTimeout = this.data.txTimeout || 60_000 + this.keywords = this.buildKeywords() } async connect(opts) { @@ -197,6 +198,14 @@ class Network { } return data } + + buildKeywords(){ + return _.compact([ + ...this.chain?.keywords || [], + this.authzSupport && 'authz', + this.authzAminoSupport && 'full authz ledger', + ]) + } } export default Network; From 89d3037ad2c6ec37b17a9d28961024d0997230c0 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 6 Oct 2022 00:41:11 +0100 Subject: [PATCH 685/862] Dynamic authz support for v0.46 chains --- package-lock.json | 11 +++++++++++ package.json | 1 + src/networks.json | 6 ++---- src/utils/Chain.mjs | 5 ++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f406ccf8..507f167ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "axios": "^0.26.0", "bootstrap": "^5.1.3", "buffer": "^6.0.3", + "compare-versions": "^5.0.1", "cosmjs-types": "^0.4.1", "dotenv": "^16.0.0", "fuzzy-search": "^3.2.1", @@ -4383,6 +4384,11 @@ "node": ">= 10" } }, + "node_modules/compare-versions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz", + "integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ==" + }, "node_modules/complex.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.0.tgz", @@ -11224,6 +11230,11 @@ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, + "compare-versions": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz", + "integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ==" + }, "complex.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.0.tgz", diff --git a/package.json b/package.json index 3838b746e..ec837a05d 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "axios": "^0.26.0", "bootstrap": "^5.1.3", "buffer": "^6.0.3", + "compare-versions": "^5.0.1", "cosmjs-types": "^0.4.1", "dotenv": "^16.0.0", "fuzzy-search": "^3.2.1", diff --git a/src/networks.json b/src/networks.json index 121739233..1fc88803c 100644 --- a/src/networks.json +++ b/src/networks.json @@ -4,8 +4,7 @@ "gasPrice": "0.0025uosmo", "ownerAddress": "osmovaloper1u5v0m74mql5nzfx2yh43s2tke4mvzghr6m2n5t", "default": true, - "maxPerDay": 1, - "authzAminoSupport": true + "maxPerDay": 1 }, { "name": "juno", @@ -102,8 +101,7 @@ "gasPrice": "25ncheq" }, { - "name": "umee", - "authzAminoSupport": true + "name": "umee" }, { "name": "bitsong" diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index b33cc96db..050025448 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -1,8 +1,11 @@ +import { compareVersions, validate } from 'compare-versions'; import ChainAsset from "./ChainAsset.mjs"; const Chain = (data) => { const assets = data.assets?.map(el => ChainAsset(el)) || [] const baseAsset = assets[0] + const { cosmos_sdk_version } = data.versions + const sdkAuthzAminoSupport = validate(cosmos_sdk_version) && compareVersions(cosmos_sdk_version, '0.46') >= 0 return { ...data, @@ -12,7 +15,7 @@ const Chain = (data) => { slip44: data.slip44 || 118, estimatedApr: data.params?.calculated_apr, authzSupport: data.authzSupport ?? data.params?.authz, - authzAminoSupport: data.authzAminoSupport ?? false, + authzAminoSupport: data.authzAminoSupport ?? sdkAuthzAminoSupport ?? false, denom: data.denom || baseAsset?.base?.denom, display: data.display || baseAsset?.display?.denom, symbol: data.symbol || baseAsset?.symbol, From c2050b923d2dc680d5e7ed7f90031dee3c1df98d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 6 Oct 2022 12:07:04 +0100 Subject: [PATCH 686/862] Handle no signer provider --- src/components/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/App.js b/src/components/App.js index 70993dcb9..f3cfc7f9b 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -154,7 +154,7 @@ class App extends React.Component { disconnect() { localStorage.removeItem('connected') - this.state.signerProvider.disconnect() + this.state.signerProvider?.disconnect() this.setState({ address: null, balance: null, From b06e25ea90b9cbf36db0a8ab9a0754470355efe7 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 6 Oct 2022 19:01:17 +0100 Subject: [PATCH 687/862] Update dependencies --- package-lock.json | 96 +++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index 507f167ee..32ad21bc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1529,19 +1529,19 @@ } }, "node_modules/@keplr-wallet/common": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/common/-/common-0.11.3.tgz", - "integrity": "sha512-3izIyRX9nXTrc+u+24yYpl2qiLyHaweqiXI3fk2SHucWWicPqksSw5wkmuA+4Bef3/CUQPTgtcufDgPyBB+bNw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/common/-/common-0.11.8.tgz", + "integrity": "sha512-HxOmmXT3oWb9rwZGAeZK57+NSh/qVBehV7EME8XjuvUkfV7NwCL8oZiNGfH8GZs22XoYoVXP8MCwEI7DKbLv1g==", "dependencies": { - "@keplr-wallet/crypto": "0.11.3", + "@keplr-wallet/crypto": "0.11.8", "buffer": "^6.0.3", "delay": "^4.4.0" } }, "node_modules/@keplr-wallet/crypto": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/crypto/-/crypto-0.11.3.tgz", - "integrity": "sha512-BWSEU97qBu2M7DmSWPFxG+eL6k8UHQe230MBkDzLpVQsouFeJbwsip0mTxxJWaZXMuhyK9gaMsAQSK4YkHyI4Q==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/crypto/-/crypto-0.11.8.tgz", + "integrity": "sha512-csT7D5YxBr4ZmJ7Q1Q3+G5KRAX5/M1Y9qpeTVJTcTIo9l66atBVXgiweLQTx0KHkCma1o16l+zkPtDFnjT/FeA==", "dependencies": { "bip32": "^2.0.6", "bip39": "^3.0.3", @@ -1553,14 +1553,14 @@ } }, "node_modules/@keplr-wallet/provider": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/provider/-/provider-0.11.3.tgz", - "integrity": "sha512-4KWRiJTyIBW+qOOYP2CN/IIvGMLsBgRi1D5/FT6q7EaYkIR/peJlKSR6fJukmQHyI/fmkvGH2+Td9fhZsVv5mA==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/provider/-/provider-0.11.8.tgz", + "integrity": "sha512-Xl2ovPo+V6fhIliYDk7CqCVa6qclK3oafAd70U7EWxxvhUIR502Hs2uvc+yAhe2F1X1UrJyk4EirH4vArihQzA==", "dependencies": { "@cosmjs/launchpad": "^0.24.0-alpha.25", "@cosmjs/proto-signing": "^0.24.0-alpha.25", - "@keplr-wallet/router": "0.11.3", - "@keplr-wallet/types": "0.11.3", + "@keplr-wallet/router": "0.11.8", + "@keplr-wallet/types": "0.11.8", "buffer": "^6.0.3", "deepmerge": "^4.2.2", "long": "^4.0.0", @@ -1608,14 +1608,14 @@ } }, "node_modules/@keplr-wallet/router": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/router/-/router-0.11.3.tgz", - "integrity": "sha512-2Kh5+8ZqUY4l+Gf3mEl8q6vDeb7ino1SnCHA0bl5+ny9039zQ8AcIHjrZStX64nTPYuM/G60HRmNKhG51vMubQ==" + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/router/-/router-0.11.8.tgz", + "integrity": "sha512-NBHWkpc3Ed6gIfFh+SFc/+17XNFvnuFtYCc5pHLoHlqb3fCMDnvLL75BrIicVtoUS0SW6YDeNOkYZdXrgaaLpA==" }, "node_modules/@keplr-wallet/types": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/types/-/types-0.11.3.tgz", - "integrity": "sha512-OA0OoimsUI22iTXgup3BAlLt+FJdGKniJqzJyQji+rqfXqreBT+Ie9c5Ng7qY81+RO/nJ5SZfa1gIns+3A0YxA==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/types/-/types-0.11.8.tgz", + "integrity": "sha512-TeQiZ5KmN8EfY06B1fvsa5eh20/o6+0+lOwKN6v2wO/zt8vdnwymEcZ0eQ2/gnrXYmdeD9YaFFPrPZHMKPSuMA==", "dependencies": { "@cosmjs/launchpad": "^0.24.0-alpha.25", "@cosmjs/proto-signing": "^0.24.0-alpha.25", @@ -1674,15 +1674,15 @@ } }, "node_modules/@keplr-wallet/wc-client": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-client/-/wc-client-0.11.3.tgz", - "integrity": "sha512-u7A+c5XL8+8Qa9g6Qsn6PCZBYBtOgNgIBIqpbLFpbxzBCIHH8GE+PRp1AVNsU8R5f7HHyGWHO1T14sTrdUpGEQ==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-client/-/wc-client-0.11.8.tgz", + "integrity": "sha512-M69m7B6KmhCwB/fNgl4+RkBG9DmgX7q+fz70elP4AfVBaqm0uICr4zVgrEw39E5rLJqTcRvcEz28lemGpbC5wg==", "dependencies": { "@cosmjs/launchpad": "^0.24.0-alpha.25", "@cosmjs/proto-signing": "^0.24.0-alpha.25", - "@keplr-wallet/common": "0.11.3", - "@keplr-wallet/provider": "0.11.3", - "@keplr-wallet/types": "0.11.3", + "@keplr-wallet/common": "0.11.8", + "@keplr-wallet/provider": "0.11.8", + "@keplr-wallet/types": "0.11.8", "@walletconnect/types": "^1.6.4", "@walletconnect/utils": "^1.6.4", "buffer": "^6.0.3", @@ -9125,19 +9125,19 @@ } }, "@keplr-wallet/common": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/common/-/common-0.11.3.tgz", - "integrity": "sha512-3izIyRX9nXTrc+u+24yYpl2qiLyHaweqiXI3fk2SHucWWicPqksSw5wkmuA+4Bef3/CUQPTgtcufDgPyBB+bNw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/common/-/common-0.11.8.tgz", + "integrity": "sha512-HxOmmXT3oWb9rwZGAeZK57+NSh/qVBehV7EME8XjuvUkfV7NwCL8oZiNGfH8GZs22XoYoVXP8MCwEI7DKbLv1g==", "requires": { - "@keplr-wallet/crypto": "0.11.3", + "@keplr-wallet/crypto": "0.11.8", "buffer": "^6.0.3", "delay": "^4.4.0" } }, "@keplr-wallet/crypto": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/crypto/-/crypto-0.11.3.tgz", - "integrity": "sha512-BWSEU97qBu2M7DmSWPFxG+eL6k8UHQe230MBkDzLpVQsouFeJbwsip0mTxxJWaZXMuhyK9gaMsAQSK4YkHyI4Q==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/crypto/-/crypto-0.11.8.tgz", + "integrity": "sha512-csT7D5YxBr4ZmJ7Q1Q3+G5KRAX5/M1Y9qpeTVJTcTIo9l66atBVXgiweLQTx0KHkCma1o16l+zkPtDFnjT/FeA==", "requires": { "bip32": "^2.0.6", "bip39": "^3.0.3", @@ -9149,14 +9149,14 @@ } }, "@keplr-wallet/provider": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/provider/-/provider-0.11.3.tgz", - "integrity": "sha512-4KWRiJTyIBW+qOOYP2CN/IIvGMLsBgRi1D5/FT6q7EaYkIR/peJlKSR6fJukmQHyI/fmkvGH2+Td9fhZsVv5mA==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/provider/-/provider-0.11.8.tgz", + "integrity": "sha512-Xl2ovPo+V6fhIliYDk7CqCVa6qclK3oafAd70U7EWxxvhUIR502Hs2uvc+yAhe2F1X1UrJyk4EirH4vArihQzA==", "requires": { "@cosmjs/launchpad": "^0.24.0-alpha.25", "@cosmjs/proto-signing": "^0.24.0-alpha.25", - "@keplr-wallet/router": "0.11.3", - "@keplr-wallet/types": "0.11.3", + "@keplr-wallet/router": "0.11.8", + "@keplr-wallet/types": "0.11.8", "buffer": "^6.0.3", "deepmerge": "^4.2.2", "long": "^4.0.0", @@ -9201,14 +9201,14 @@ } }, "@keplr-wallet/router": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/router/-/router-0.11.3.tgz", - "integrity": "sha512-2Kh5+8ZqUY4l+Gf3mEl8q6vDeb7ino1SnCHA0bl5+ny9039zQ8AcIHjrZStX64nTPYuM/G60HRmNKhG51vMubQ==" + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/router/-/router-0.11.8.tgz", + "integrity": "sha512-NBHWkpc3Ed6gIfFh+SFc/+17XNFvnuFtYCc5pHLoHlqb3fCMDnvLL75BrIicVtoUS0SW6YDeNOkYZdXrgaaLpA==" }, "@keplr-wallet/types": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/types/-/types-0.11.3.tgz", - "integrity": "sha512-OA0OoimsUI22iTXgup3BAlLt+FJdGKniJqzJyQji+rqfXqreBT+Ie9c5Ng7qY81+RO/nJ5SZfa1gIns+3A0YxA==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/types/-/types-0.11.8.tgz", + "integrity": "sha512-TeQiZ5KmN8EfY06B1fvsa5eh20/o6+0+lOwKN6v2wO/zt8vdnwymEcZ0eQ2/gnrXYmdeD9YaFFPrPZHMKPSuMA==", "requires": { "@cosmjs/launchpad": "^0.24.0-alpha.25", "@cosmjs/proto-signing": "^0.24.0-alpha.25", @@ -9264,15 +9264,15 @@ } }, "@keplr-wallet/wc-client": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-client/-/wc-client-0.11.3.tgz", - "integrity": "sha512-u7A+c5XL8+8Qa9g6Qsn6PCZBYBtOgNgIBIqpbLFpbxzBCIHH8GE+PRp1AVNsU8R5f7HHyGWHO1T14sTrdUpGEQ==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@keplr-wallet/wc-client/-/wc-client-0.11.8.tgz", + "integrity": "sha512-M69m7B6KmhCwB/fNgl4+RkBG9DmgX7q+fz70elP4AfVBaqm0uICr4zVgrEw39E5rLJqTcRvcEz28lemGpbC5wg==", "requires": { "@cosmjs/launchpad": "^0.24.0-alpha.25", "@cosmjs/proto-signing": "^0.24.0-alpha.25", - "@keplr-wallet/common": "0.11.3", - "@keplr-wallet/provider": "0.11.3", - "@keplr-wallet/types": "0.11.3", + "@keplr-wallet/common": "0.11.8", + "@keplr-wallet/provider": "0.11.8", + "@keplr-wallet/types": "0.11.8", "@walletconnect/types": "^1.6.4", "@walletconnect/utils": "^1.6.4", "buffer": "^6.0.3", From e2b901e7d907e22423e9f361e4acdb771428ee5a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 6 Oct 2022 19:01:22 +0100 Subject: [PATCH 688/862] Handle no pagination response --- src/utils/QueryClient.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 8272fe20e..a260febad 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -206,7 +206,7 @@ const QueryClient = async (chainId, restUrls, opts) => { do { const result = await getPage(nextKey); pages.push(result); - nextKey = result.pagination.next_key; + nextKey = result.pagination?.next_key; if (pageCallback) await pageCallback(pages); } while (nextKey); return pages; From d926b386eaa1d1b9ce5fd2eae306023b135adcda Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 6 Oct 2022 19:12:42 +0100 Subject: [PATCH 689/862] Remove unused imports --- src/components/NetworkSelect.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js index edc4a7391..f06f89214 100644 --- a/src/components/NetworkSelect.js +++ b/src/components/NetworkSelect.js @@ -1,8 +1,5 @@ import React, { useState, useReducer, useEffect } from 'react'; -import CosmosDirectory from '../utils/CosmosDirectory.mjs'; -import Network from '../utils/Network.mjs' - import { Button, Modal, From 98480a7030d2f1115713016de384a07862d904f4 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 6 Oct 2022 19:48:27 +0100 Subject: [PATCH 690/862] Handle no version information --- src/utils/Chain.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index 050025448..ab35b3a19 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -4,7 +4,7 @@ import ChainAsset from "./ChainAsset.mjs"; const Chain = (data) => { const assets = data.assets?.map(el => ChainAsset(el)) || [] const baseAsset = assets[0] - const { cosmos_sdk_version } = data.versions + const { cosmos_sdk_version } = data.versions || {} const sdkAuthzAminoSupport = validate(cosmos_sdk_version) && compareVersions(cosmos_sdk_version, '0.46') >= 0 return { From 1895594d4810aed2ae37a3432abe7bca510211c1 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 6 Oct 2022 22:38:43 +0100 Subject: [PATCH 691/862] Add fallback block path for REST check --- src/utils/QueryClient.mjs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 8272fe20e..3101fc97b 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -223,12 +223,10 @@ const QueryClient = async (chainId, restUrls, opts) => { } } const path = type === "rest" ? "/cosmos/base/tendermint/v1beta1/blocks/latest" : "/block"; - const { timeout } = opts || {} return Promise.any(urls.map(async (url) => { url = url.replace(/\/$/, '') try { - let data = await axios.get(url + path, { timeout }) - .then((res) => res.data) + let data = await getLatestBlock(url, type, path, opts) if (type === "rpc") data = data.result; if (data.block?.header?.chain_id === chainId) { return url; @@ -237,6 +235,20 @@ const QueryClient = async (chainId, restUrls, opts) => { })); } + async function getLatestBlock(url, type, path, opts){ + const { timeout } = opts || {} + try { + return await axios.get(url + path, { timeout }) + .then((res) => res.data) + } catch (error) { + const fallback = type === 'rest' && '/blocks/latest' + if (fallback && fallback !== path && error.response?.status === 501) { + return getLatestBlock(url, type, fallback, opts) + } + throw(error) + } + } + return { connected: !!restUrl, restUrl, From e9561533d668bc3c354120155f02b1c71d1dbca5 Mon Sep 17 00:00:00 2001 From: Jose Lopes Date: Fri, 7 Oct 2022 10:50:12 +0100 Subject: [PATCH 692/862] Add rebus --- src/networks.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/networks.json b/src/networks.json index 1fc88803c..64dbda9ec 100644 --- a/src/networks.json +++ b/src/networks.json @@ -247,5 +247,8 @@ "average": 0.025, "high": 0.04 } + }, + { + "name": "rebus" } ] From a19c916e88814a2ab98d3850443b36da23db8f44 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 7 Oct 2022 11:54:46 +0100 Subject: [PATCH 693/862] Remove amino conversion log --- src/utils/SigningClient.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 3bfc5981d..bc39b69a3 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -184,7 +184,7 @@ function SigningClient(network, signer) { let aminoMsgs try { aminoMsgs = convertToAmino(messages) - } catch (e) { console.log(e) } + } catch (e) { } if(aminoMsgs && signer.signAmino){ // Sign as amino if possible for Ledger and Keplr support const signDoc = makeAminoSignDoc(aminoMsgs, fee, chainId, memo, accountNumber, sequence); From f8156f2cae926015ac616321229988424486bfad Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 10 Oct 2022 22:04:32 +0100 Subject: [PATCH 694/862] Handle Injective pubkey type --- src/utils/SigningClient.mjs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index bc39b69a3..583fd4622 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -284,10 +284,7 @@ function SigningClient(network, signer) { signerInfos: [ { publicKey: { - typeUrl: - coinType === 60 - ? "/ethermint.crypto.v1.ethsecp256k1.PubKey" - : "/cosmos.crypto.secp256k1.PubKey", + typeUrl: pubkeyTypeUrl(), value: PubKey.encode({ key: pubkey, }).finish(), @@ -300,6 +297,17 @@ function SigningClient(network, signer) { }).finish() } + function pubkeyTypeUrl(){ + if(network.path === 'injective'){ + return '/injective.crypto.v1beta1.ethsecp256k1.PubKey' + } + + if(coinType === 60){ + return '/ethermint.crypto.v1.ethsecp256k1.PubKey' + } + return '/cosmos.crypto.secp256k1.PubKey' + } + return { signer, registry, From a52e9b8b56f527d7d4522591afbb78d26c498903 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 13 Oct 2022 18:19:58 +0100 Subject: [PATCH 695/862] Improve leap not supported error --- src/utils/LeapSignerProvider.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/utils/LeapSignerProvider.mjs b/src/utils/LeapSignerProvider.mjs index 8ee1368f5..8b7a67a3d 100644 --- a/src/utils/LeapSignerProvider.mjs +++ b/src/utils/LeapSignerProvider.mjs @@ -4,7 +4,7 @@ export default class LeapSignerProvider extends SignerProvider { key = 'leap' label = 'Leap Wallet' keychangeEvent = 'leap_keystorechange' - suggestChainSupport = false + suggestChainSupport = true enable(network){ if (network.gasPricePrefer) { @@ -15,6 +15,10 @@ export default class LeapSignerProvider extends SignerProvider { return super.enable(network) } + suggestChain(network){ + throw new Error(`${network.prettyName} (${network.chainId}) is not supported`) + } + setOptions(options){ return this.provider.defaultOptions = options } From 1e20ec8d87314f0443dfec577e8b300678414dbc Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 13 Oct 2022 18:58:33 +0100 Subject: [PATCH 696/862] Disable Evmos ledger support --- src/networks.json | 3 ++- src/utils/Network.mjs | 1 + src/utils/SignerProvider.mjs | 8 ++++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/networks.json b/src/networks.json index 1fc88803c..5afef9d98 100644 --- a/src/networks.json +++ b/src/networks.json @@ -79,7 +79,8 @@ "name": "evmos", "txTimeout": 120000, "ownerAddress": "evmosvaloper1umk407eed7af6anvut6llg2zevnf0dn0feqqny", - "maxPerDay": 1 + "maxPerDay": 1, + "ledgerSupport": false }, { "name": "sifchain", diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 7fa54ee55..c386e805b 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -85,6 +85,7 @@ class Network { this.coinGeckoId = this.chain.coinGeckoId this.estimatedApr = this.chain.estimatedApr this.apyEnabled = data.apyEnabled !== false && !!this.estimatedApr && this.estimatedApr > 0 + this.ledgerSupport = this.chain.ledgerSupport ?? true this.authzSupport = this.chain.authzSupport this.authzAminoSupport = this.chain.authzAminoSupport this.defaultGasPrice = this.decimals && format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom diff --git a/src/utils/SignerProvider.mjs b/src/utils/SignerProvider.mjs index b3f2b2196..7609d712a 100644 --- a/src/utils/SignerProvider.mjs +++ b/src/utils/SignerProvider.mjs @@ -38,9 +38,13 @@ export default class SignerProvider { return this.provider.enable(chainId) } - getKey(network){ + async getKey(network){ const { chainId } = network - return this.provider.getKey(chainId) + const key = await this.provider.getKey(chainId) + if(!network.ledgerSupport && (key.isNanoLedger || key.isHardware)){ + throw new Error('Ledger support is coming soon') + } + return key } getSigner(network){ From 3a9d3c217476902118ae8d50efab8aa329dc15d2 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 13 Oct 2022 19:12:50 +0100 Subject: [PATCH 697/862] Fix coin display precision --- src/components/Coins.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Coins.js b/src/components/Coins.js index 7455c2497..9509e482d 100644 --- a/src/components/Coins.js +++ b/src/components/Coins.js @@ -1,5 +1,5 @@ import _ from 'lodash' -import { divide, bignumber, round } from 'mathjs' +import { divide, bignumber, round, format } from 'mathjs' function Coins(props) { const { asset, coins, fullPrecision, inBaseDenom, hideValue, className } = props @@ -10,7 +10,7 @@ function Coins(props) { if(inBaseDenom) return coins.amount const prec = precision(coins, decimals) - return round(divide(bignumber(coins.amount), Math.pow(10, decimals)), prec).toLocaleString(undefined, { maximumFractionDigits: prec }) + return format(round(divide(bignumber(coins.amount), Math.pow(10, decimals)), prec), {notation: 'fixed'}).toLocaleString(undefined, { maximumFractionDigits: prec }) } function value(coins){ From 4af23e687858c90df55cfd698a3c6b5e7ce3109b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 13 Oct 2022 20:59:54 +0100 Subject: [PATCH 698/862] Use on-chain pubkey type if available --- src/utils/SigningClient.mjs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 583fd4622..2e27537e5 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -279,14 +279,14 @@ function SigningClient(network, signer) { if (!accountFromSigner) { throw new Error("Failed to retrieve account from signer"); } - const pubkey = accountFromSigner.pubkey; + const signerPubkey = accountFromSigner.pubkey; return AuthInfo.encode({ signerInfos: [ { publicKey: { - typeUrl: pubkeyTypeUrl(), + typeUrl: pubkeyTypeUrl(account.pub_key), value: PubKey.encode({ - key: pubkey, + key: signerPubkey, }).finish(), }, sequence: Long.fromNumber(sequence, true), @@ -297,7 +297,9 @@ function SigningClient(network, signer) { }).finish() } - function pubkeyTypeUrl(){ + function pubkeyTypeUrl(pub_key){ + if(pub_key && pub_key['@type']) return pub_key['@type'] + if(network.path === 'injective'){ return '/injective.crypto.v1beta1.ethsecp256k1.PubKey' } From e317bd3b94a437cfdbde1a8fda3120fefcf0e05c Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 13 Oct 2022 21:02:03 +0100 Subject: [PATCH 699/862] Update suggestChain format --- src/utils/Network.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 7fa54ee55..ae3b814cc 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -190,8 +190,7 @@ class Network { bech32PrefixConsPub: this.prefix + "valconspub" }, currencies: [currency], - feeCurrencies: [currency], - gasPriceStep: this.gasPriceStep + feeCurrencies: [{...currency, gasPriceStep: this.gasPriceStep }] } if(this.data.keplrFeatures){ data.features = this.data.keplrFeatures From 7c1f2f6839cc50c9ad59a63b945f8e6a6c8d49eb Mon Sep 17 00:00:00 2001 From: schultzie Date: Sun, 16 Oct 2022 07:58:49 -0700 Subject: [PATCH 700/862] Add note to tear down docker images --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 879312655..11d34c4b2 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,15 @@ crontab -e 0 21 * * * /bin/bash -c "cd restake && docker compose run --rm app npm run autostake" > ./restake.log 2>&1 ``` +Warning: Using crontab with docker will continuously create docker images. Add another crontab to tear down the images weekly. + +```bash +crontab -e + +0 0 * * */7 /bin/bash -c "cd restake && docker compose down" > ./restake.log 2>&1 +``` + + or with NPM: ```bash From b5cb04a0b8912303f2e8a43951a516382eb957d5 Mon Sep 17 00:00:00 2001 From: schultzie Date: Sun, 16 Oct 2022 08:04:26 -0700 Subject: [PATCH 701/862] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11d34c4b2..d953f23b1 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ Warning: Using crontab with docker will continuously create docker images. Add a ```bash crontab -e -0 0 * * */7 /bin/bash -c "cd restake && docker compose down" > ./restake.log 2>&1 +0 0 * * */7 /bin/bash -c "cd restake && docker compose down" > ./restake_teardown.log 2>&1 ``` From fb05981e99668699788db8f7d447171b7e1616fe Mon Sep 17 00:00:00 2001 From: schultzie Date: Sun, 16 Oct 2022 08:08:49 -0700 Subject: [PATCH 702/862] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d953f23b1..b9914170f 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ Warning: Using crontab with docker will continuously create docker images. Add a ```bash crontab -e -0 0 * * */7 /bin/bash -c "cd restake && docker compose down" > ./restake_teardown.log 2>&1 +0 0 * * */7 /usr/bin/docker image prune -a -f ``` From 7e843c3c05ac1bb552df786d1843553527dd41bb Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 20 Oct 2022 20:54:38 +0100 Subject: [PATCH 703/862] Query and show last REStake time and warnings --- package-lock.json | 35 +++++++ package.json | 1 + src/components/Governance.js | 1 + src/components/NetworkChecks.js | 3 +- src/components/ValidatorDelegate.js | 4 +- src/components/ValidatorGrants.js | 61 +++++++++++- src/components/ValidatorModal.js | 145 +++++++++++++++++++--------- src/components/ValidatorNetworks.js | 16 ++- src/components/ValidatorProfile.js | 44 ++++++--- src/components/Validators.js | 3 +- src/networks.json | 3 +- src/utils/Chain.mjs | 4 +- src/utils/Helpers.mjs | 10 ++ src/utils/Network.mjs | 1 + src/utils/Operator.mjs | 107 +++++++++++--------- src/utils/QueryClient.mjs | 17 +++- 16 files changed, 335 insertions(+), 120 deletions(-) diff --git a/package-lock.json b/package-lock.json index 32ad21bc1..298053fb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@tharsis/address-converter": "^0.1.8", "@walletconnect/client": "^1.8.0", "axios": "^0.26.0", + "axios-retry": "^3.3.1", "bootstrap": "^5.1.3", "buffer": "^6.0.3", "compare-versions": "^5.0.1", @@ -3975,6 +3976,15 @@ "follow-redirects": "^1.14.8" } }, + "node_modules/axios-retry": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.3.1.tgz", + "integrity": "sha512-RohAUQTDxBSWLFEnoIG/6bvmy8l3TfpkclgStjl5MDCMBDgapAWCmr1r/9harQfWC8bzLC8job6UcL1A1Yc+/Q==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "is-retry-allowed": "^2.2.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -5824,6 +5834,17 @@ "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", "dev": true }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -10921,6 +10942,15 @@ "follow-redirects": "^1.14.8" } }, + "axios-retry": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.3.1.tgz", + "integrity": "sha512-RohAUQTDxBSWLFEnoIG/6bvmy8l3TfpkclgStjl5MDCMBDgapAWCmr1r/9harQfWC8bzLC8job6UcL1A1Yc+/Q==", + "requires": { + "@babel/runtime": "^7.15.4", + "is-retry-allowed": "^2.2.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -12312,6 +12342,11 @@ "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", "dev": true }, + "is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==" + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", diff --git a/package.json b/package.json index ec837a05d..6a9d2f611 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@tharsis/address-converter": "^0.1.8", "@walletconnect/client": "^1.8.0", "axios": "^0.26.0", + "axios-retry": "^3.3.1", "bootstrap": "^5.1.3", "buffer": "^6.0.3", "compare-versions": "^5.0.1", diff --git a/src/components/Governance.js b/src/components/Governance.js index f363baaea..78b0dffca 100644 --- a/src/components/Governance.js +++ b/src/components/Governance.js @@ -85,6 +85,7 @@ function Governance(props) { try { let newProposals = await props.queryClient.getProposals() newProposals = newProposals.map(el => Proposal(el)) + setError() setProposals(sortProposals(newProposals)) setTallies(newProposals.reduce((sum, proposal) => { if (!_.every(Object.values(proposal.final_tally_result), el => el === '0')) { diff --git a/src/components/NetworkChecks.js b/src/components/NetworkChecks.js index 339330b6a..c9ee90f57 100644 --- a/src/components/NetworkChecks.js +++ b/src/components/NetworkChecks.js @@ -1,6 +1,7 @@ import React from 'react'; import _ from 'lodash' import { CheckCircle, XCircle, InfoCircle } from "react-bootstrap-icons"; +import { round } from 'mathjs' import TooltipIcon from './TooltipIcon.js'; import Coingecko from '../assets/coingecko.png' @@ -66,7 +67,7 @@ function NetworkChecks(props) { failClass: 'success', }), renderCheck({ - title: {`${Math.round(network.estimatedApr * 100).toLocaleString()}% APR`}, + title: {`${round(network.estimatedApr * 100, 2).toLocaleString()}% APR`}, failTitle: 'APR unknown', state: network.estimatedApr, identifier: 'apr', diff --git a/src/components/ValidatorDelegate.js b/src/components/ValidatorDelegate.js index dd50cdbfd..2ee5bae8b 100644 --- a/src/components/ValidatorDelegate.js +++ b/src/components/ValidatorDelegate.js @@ -1,4 +1,6 @@ import React from 'react'; +import { round } from 'mathjs' + import Coins from './Coins'; import TooltipIcon from './TooltipIcon' @@ -39,7 +41,7 @@ function ValidatorDelegate(props) { {Object.keys(props.validatorApy).length > 0 ? props.validatorApy[selectedValidator.operator_address] - ? {Math.round(props.validatorApy[selectedValidator.operator_address] * 100).toLocaleString()}% + ? {round(props.validatorApy[selectedValidator.operator_address] * 100, 2).toLocaleString()}% : "-" : ( diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 224ac8009..55e294502 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -1,24 +1,29 @@ import React, { useState, useEffect } from 'react'; import moment from 'moment' -import { pow, multiply, divide, larger, smaller, bignumber } from 'mathjs' +import { pow, multiply, divide, larger, smaller, bignumber, round } from 'mathjs' import { MsgGrant } from "cosmjs-types/cosmos/authz/v1beta1/tx"; import { StakeAuthorization } from "cosmjs-types/cosmos/staking/v1beta1/authz"; import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; import { + Spinner, Button, Form, Table } from 'react-bootstrap' +import { + QuestionCircle, +} from 'react-bootstrap-icons' import Coins from './Coins'; -import { buildExecMessage, coin } from '../utils/Helpers.mjs'; +import { buildExecMessage, coin, lastRestakeClassname } from '../utils/Helpers.mjs'; import RevokeGrant from './RevokeGrant'; import AlertMessage from './AlertMessage'; +import TooltipIcon from './TooltipIcon' function ValidatorGrants(props) { - const { grants, wallet, operator, address, network } = props + const { grants, wallet, operator, address, network, lastExec } = props const { stakeGrant, maxTokens, validators, grantsValid, grantsExist } = grants || {} const defaultExpiry = moment().add(1, 'year') const [loading, setLoading] = useState(false); @@ -62,6 +67,14 @@ function ValidatorGrants(props) { return !maxTokensDenom() || larger(maxTokensDenom(), props.rewards) } + function lastRestakeWarning() { + if (!operator || lastExec == null) + return; + + const missedRuns = operator.missedRunCount(lastExec); + return missedRuns > Math.min(15, operator.runsPerDay() * 2); + } + function showLoading(isLoading) { setLoading(isLoading) props.setLoading && props.setLoading(isLoading) @@ -171,6 +184,11 @@ function ValidatorGrants(props) { You must delegate to {operator.moniker} before they can REStake for you. )} + {lastRestakeWarning() && ( + + This validator has not REStaked recently. + + )} {error && {error} @@ -188,12 +206,49 @@ function ValidatorGrants(props) { {operator.runTimesString()} + {network.authzHistorySupport && ( + + Last REStake + +
    + {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Never'} : } + } identifier={operator.address} tooltip="Based on the last REStake transaction sent by this validator for any of their users. Not every run generates a transaction." /> +
    + + + )} Minimum Reward + {network.apyEnabled && ( + + + APY} + identifier="delegations-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is an estimate and best case scenario.

    +
    +
    + + + {Object.keys(props.validatorApy).length > 0 + ? props.validatorApy[operator.address] + ? {round(props.validatorApy[operator.address] * 100, 2).toLocaleString()}% + : "-" + : ( + + Loading... + + )} + + + )} Current Rewards diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index 04f177df7..70d203afb 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useParams, useNavigate } from "react-router-dom"; import { bignumber } from 'mathjs' +import moment from 'moment' import Validators from './Validators' import ValidatorImage from './ValidatorImage' @@ -23,6 +24,29 @@ function ValidatorModal(props) { const params = useParams(); const [selectedValidator, setSelectedValidator] = useState(!redelegate && validator); const [activeTab, setActiveTab] = useState(params.section); + const [registryData, setRegistryData] = useState({}) + const [lastExec, setLastExec] = useState() + + let operator + if (operators && selectedValidator){ + operator = network.getOperator(selectedValidator.operator_address); + } + + useEffect(() => { + getRegistryData() + const interval = setInterval(() => { + getRegistryData() + }, 15_000); + return () => clearInterval(interval); + }, [selectedValidator]); + + useEffect(() => { + getLastExec() + const interval = setInterval(() => { + getLastExec() + }, 60_000); + return () => clearInterval(interval); + }, [operator]) useEffect(() => { if(params.network !== network.name) return @@ -56,70 +80,101 @@ function ValidatorModal(props) { } }, [validator, redelegate, props.show]) - const handleClose = () => { + function handleClose() { if (selectedValidator && (!validator || redelegate) && selectedValidator !== validator) { - setSelectedValidator(null) + setSelectedValidator(null); } else { - props.hideModal() - setSelectedValidator(null) + props.hideModal(); + setSelectedValidator(null); } } - const selectValidator = (selectedValidator, opts) => { - setSelectedValidator(selectedValidator) - setActiveTab(opts.activeTab || 'profile') + function selectValidator(selectedValidator, opts) { + setSelectedValidator(selectedValidator); + setActiveTab(opts.activeTab || 'profile'); } - const onDelegate = () => { - props.onDelegate() - props.hideModal() + function onDelegate() { + props.onDelegate(); + props.hideModal(); } - const excludeValidators = () => { - if (redelegate) { - return [validator.operator_address] - } else if (delegations) { - return [...Object.keys(delegations)] + function getRegistryData(){ + if(selectedValidator?.path && network.directory){ + network.directory.getRegistryValidator(selectedValidator.path).then(data => { + setRegistryData(data) + }) + }else{ + setRegistryData({}) } } - const operator = () => { - if (!operators || !selectedValidator) return + function getLastExec() { + if(!operator || !network.authzHistorySupport){ + setLastExec() + return + } + + network.queryClient.getTransactions([ + { key: 'events', value: `message.action='/cosmos.authz.v1beta1.MsgExec'` }, + { key: 'events', value: `message.sender='${operator.botAddress}'` } + ], { + pageSize: 1, + order: 2, + retries: 3, + timeout: 15_000 + }).then(data => { + if (data.tx_responses?.length > 0) { + setLastExec(moment(data.tx_responses[0].timestamp)) + } else { + setLastExec(false) + } + }) + } - return network.getOperator(selectedValidator.operator_address) + function excludeValidators() { + if (redelegate) { + return [validator.operator_address]; + } else if (delegations) { + return [...Object.keys(delegations)]; + } } - const availableBalance = () => { + function availableBalance() { if (redelegate || undelegate) { - return (props.delegations[validator.address] || {}).balance + return (props.delegations[validator.address] || {}).balance; } else { - return props.balance + return props.balance; } } - const rewards = () => { - if (!props.rewards) return 0; + function rewards() { + if (!props.rewards) + return 0; const denom = network.denom; const validatorReward = props.rewards[selectedValidator.address]; - const reward = validatorReward && validatorReward.reward.find((el) => el.denom === denom) - return reward ? bignumber(reward.amount) : 0 + const reward = validatorReward && validatorReward.reward.find((el) => el.denom === denom); + return reward ? bignumber(reward.amount) : 0; } - const commission = () => { - if (!props.commission) return 0; + function commission() { + if (!props.commission) + return 0; const denom = network.denom; const validatorCommission = props.commission[selectedValidator.address]; - const commission = validatorCommission && validatorCommission.commission.find((el) => el.denom === denom) - return commission ? bignumber(commission.amount) : 0 + const commission = validatorCommission && validatorCommission.commission.find((el) => el.denom === denom); + return commission ? bignumber(commission.amount) : 0; } - const actionText = () => { - if (redelegate) return Redelegate from - if (undelegate) return 'Undelegate' + function actionText() { + if (redelegate) + return Redelegate from ; + if (undelegate) + return 'Undelegate'; if (validator) { - return 'Delegate' + return 'Delegate'; } else { - return 'Add Validator' + return 'Add Validator'; } } @@ -166,12 +221,12 @@ function ValidatorModal(props) { Delegate - {operator() && ( + {operator && ( REStake )} - {network.authzSupport && !network.authzAminoSupport && operator() && ( + {network.authzSupport && !network.authzAminoSupport && operator && ( Ledger Instructions @@ -180,10 +235,10 @@ function ValidatorModal(props) { @@ -194,7 +249,9 @@ function ValidatorModal(props) { network={network} networks={props.networks} validator={selectedValidator} - operator={operator()} + operator={operator} + registryData={registryData} + lastExec={lastExec} validatorApy={props.validatorApy} /> @@ -214,16 +271,18 @@ function ValidatorModal(props) { signingClient={props.signingClient} onDelegate={onDelegate} /> - {operator() && ( + {operator && ( )} - {network.authzSupport && operator() && ( + {network.authzSupport && operator && ( diff --git a/src/components/ValidatorNetworks.js b/src/components/ValidatorNetworks.js index 01a4d6e6e..ff364ee64 100644 --- a/src/components/ValidatorNetworks.js +++ b/src/components/ValidatorNetworks.js @@ -23,13 +23,15 @@ function ValidatorNetworks(props) { const validatorNetworks = _.uniqBy(_.compact(registryData.chains.map(chain => { const chainNetwork = networks[chain.name] if(chainNetwork){ + const { online } = chainNetwork const active = network.path === chainNetwork.path return { key: chain.name, name: chainNetwork.prettyName, image: , path: `/${chainNetwork.path}/${chain.address}`, - active + active, + online } } })), 'key').sort((a, b) => a.name > b.name ? 1 : -1) @@ -43,12 +45,16 @@ function ValidatorNetworks(props) { rootClose={true} key={validatorNetwork.key} overlay={ - {validatorNetwork.name} + {validatorNetwork.name}{!validatorNetwork.online && ' (API Offline)'} } > - !validatorNetwork.active && setNetworkLoading(validatorNetwork.path)}> - {validatorNetwork.image} - + {!validatorNetwork.online || validatorNetwork.active ? ( + {validatorNetwork.image} + ) : ( + setNetworkLoading(validatorNetwork.path)}> + {validatorNetwork.image} + + )} ) })} diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js index e6790a02e..c5f2c6f19 100644 --- a/src/components/ValidatorProfile.js +++ b/src/components/ValidatorProfile.js @@ -1,5 +1,6 @@ import _ from 'lodash' -import React, { useState, useEffect } from 'react'; +import React from 'react'; +import { round } from 'mathjs' import ValidatorLink from './ValidatorLink' import Coins from './Coins' @@ -11,24 +12,15 @@ import { } from 'react-bootstrap' import { + QuestionCircle, XCircle } from 'react-bootstrap-icons' import ValidatorServices from './ValidatorServices'; import ValidatorNetworks from './ValidatorNetworks'; +import { lastRestakeClassname } from '../utils/Helpers.mjs'; function ValidatorProfile(props) { - const { validator, operator, network, networks } = props - const [registryData, setRegistryData] = useState({}) - - useEffect(() => { - if(validator?.path && network.directory){ - network.directory.getRegistryValidator(validator.path).then(data => { - setRegistryData(data) - }) - }else{ - setRegistryData({}) - } - }, [validator]); + const { validator, operator, network, networks, registryData, lastExec } = props const active = () => { if(!validator) return false @@ -133,7 +125,29 @@ function ValidatorProfile(props) { REStake {!!operator ? ( - {operator.runTimesString()} ( min) + + + + + + + + + + + {network.authzHistorySupport && ( + + + + + )} + +
    Frequency{operator.runTimesString()}
    Minimum rewards
    Last REStake +
    + {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Never'} : } + } identifier={operator.address} tooltip="Based on the last REStake transaction sent by this validator for any of their users. Not every run generates a transaction." /> +
    +
    ) : } identifier={validator.operator_address} tooltip="This validator is not a REStake operator" /> } @@ -155,7 +169,7 @@ function ValidatorProfile(props) { {Object.keys(props.validatorApy).length > 0 ? props.validatorApy[validator.operator_address] - ? {Math.round(props.validatorApy[validator.operator_address] * 100).toLocaleString()}% + ? {round(props.validatorApy[validator.operator_address] * 100, 2).toLocaleString()}% : "-" : ( diff --git a/src/components/Validators.js b/src/components/Validators.js index c188968f6..c9e631b91 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import _ from 'lodash' import FuzzySearch from 'fuzzy-search' +import { round } from 'mathjs' import { format, add } from 'mathjs' @@ -221,7 +222,7 @@ function Validators(props) { {Object.keys(props.validatorApy).length > 0 ? props.validatorApy[validatorAddress] !== undefined - ? {Math.round(props.validatorApy[validatorAddress] * 100).toLocaleString() + "%"} + ? {round(props.validatorApy[validatorAddress] * 100, 1).toLocaleString() + "%"} : "-" : ( diff --git a/src/networks.json b/src/networks.json index 26da2cc9b..d20f3009a 100644 --- a/src/networks.json +++ b/src/networks.json @@ -171,7 +171,8 @@ "average": 0.025, "high": 0.04 }, - "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd" + "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd", + "authzHistorySupport": false }, { "name": "crescent", diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index ab35b3a19..c56a6a73e 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -6,6 +6,7 @@ const Chain = (data) => { const baseAsset = assets[0] const { cosmos_sdk_version } = data.versions || {} const sdkAuthzAminoSupport = validate(cosmos_sdk_version) && compareVersions(cosmos_sdk_version, '0.46') >= 0 + const authzSupport = data.authzSupport ?? data.params?.authz return { ...data, @@ -14,8 +15,9 @@ const Chain = (data) => { prefix: data.prefix || data.bech32_prefix, slip44: data.slip44 || 118, estimatedApr: data.params?.calculated_apr, - authzSupport: data.authzSupport ?? data.params?.authz, + authzSupport, authzAminoSupport: data.authzAminoSupport ?? sdkAuthzAminoSupport ?? false, + authzHistorySupport: data.authzHistorySupport ?? authzSupport, denom: data.denom || baseAsset?.base?.denom, display: data.display || baseAsset?.display?.denom, symbol: data.symbol || baseAsset?.symbol, diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 8ac822cdc..24fc6025f 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -25,6 +25,16 @@ export function overrideNetworks(networks, overrides){ }) } +export function lastRestakeClassname(operator, lastExec) { + if (!operator || lastExec == null) + return; + + const missedRuns = operator.missedRunCount(lastExec); + const warning = missedRuns > Math.min(10, operator.runsPerDay()); + const error = missedRuns > Math.min(15, operator.runsPerDay() * 2); + return error ? 'text-danger' : warning ? 'text-warning' : 'text-success'; +} + export function buildExecMessage(grantee, messages) { return { typeUrl: "/cosmos.authz.v1beta1.MsgExec", diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index af3a48749..520165c27 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -88,6 +88,7 @@ class Network { this.ledgerSupport = this.chain.ledgerSupport ?? true this.authzSupport = this.chain.authzSupport this.authzAminoSupport = this.chain.authzAminoSupport + this.authzHistorySupport = this.chain.authzHistorySupport this.defaultGasPrice = this.decimals && format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom this.gasPrice = this.data.gasPrice || this.defaultGasPrice if(this.gasPrice){ diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index fee21b260..574a3af5d 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -4,74 +4,84 @@ import parse from 'parse-duration' const Operator = (network, data) => { const { address } = data const botAddress = data.restake.address - const runTime = data.restake.run_time + let runTime = data.restake.run_time + runTime = runTime && Array.isArray(runTime) ? runTime : [runTime] const minimumReward = data.restake.minimum_reward - function runsPerDay(max) { - let runs = 0 - if(Array.isArray(runTime)){ - runs = runTime.length - }else{ - if(runTime.startsWith('every')){ - const interval = parse(runTime.replace('every ', '')) - runs = (1000 * 60 * 60 * 24) / interval + function missedRunCount(lastExec){ + if(lastExec === false) return 9999 + let count = 0 + let times = runTimes().reverse() + let date = times[0] + while(date && date.isAfter(lastExec)){ + times.forEach(time => { + if(time.isBefore() && time.isAfter(lastExec)){ + count++ + } + }) + times = runTimes(date.subtract(1, 'day').startOf('day')).reverse() + date = times[0] + } + return count + } + + function isInterval(){ + return runTime.length === 1 && runTime[0].startsWith('every') + } + + function runTimes(start) { + if (!runTime) return [] + start = start || moment().startOf('day') + + return runTime.map(time => { + if(time.startsWith('every')){ + let date = start.clone() + const interval = parse(time.replace('every ', '')) + return [...Array(Math.floor(parse('1d') / interval))].map((_, i) => { + current = date.clone() + date.add(interval, 'ms') + return current + }) }else{ - runs = 1 + return start.utc(time, 'HH:mm:ss') } - } - return max && runs > max ? max : runs + }).flat().sort((a, b) => a.valueOf() - b.valueOf()) } - function runTimes() { - if(Array.isArray(runTime)) return runTime - return [runTime] + function runsPerDay(max) { + let runs = runTimes().length + return max && runs > max ? max : runs } function runTimesString(){ - let string = '' - if (runTimes().length > 1 || !runTimes()[0].startsWith('every')) { - string = 'at ' + if (!isInterval()) { + if(runTime.length > 1){ + return `at ${runTime.join(', ')}` + }else{ + return `at ${runTime.join(', ')} every day` + } } - return string + runTimes().join(', ') + return runTime.join(', ') } function frequency() { - if(Array.isArray(runTime)){ + if(runTime.length > 1 && !isInterval()){ return runTime.length + 'x per day' }else{ - if(runTime.startsWith('every')){ - return runTime.replace('every ', '') + if(isInterval()){ + return runTime[0].replace('every ', '') } return 'daily' } } function nextRun() { - if (!runTime) return - - if (Array.isArray(runTime)) { - return runTime - .map(el => nextRunFromRuntime(el)) - .sort((a, b) => a.valueOf() - b.valueOf()) - .find(el => el.isAfter()); - } else { - if(runTime.startsWith('every')){ - return nextRunFromInterval(runTime) - } - return nextRunFromRuntime(runTime) - } - } - - function nextRunFromInterval(runTime){ - const interval = parse(runTime.replace('every ', '')) - const diff = moment().startOf('day').diff() - const ms = interval + diff % interval - return moment().add(ms, 'ms') - } - - function nextRunFromRuntime(runTime) { - const date = moment.utc(runTime, 'HH:mm:ss') - return date.isAfter() ? date : date.add(1, 'day') + const today = runTimes() + const tomorrow = runTimes(moment().startOf('day').add(1, 'day')) + return [ + ...today, + ...tomorrow + ].find(el => el.isAfter()) } return { @@ -86,7 +96,8 @@ const Operator = (network, data) => { frequency, runTimes, runTimesString, - runsPerDay + runsPerDay, + missedRunCount } } diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 813ac52c1..5ea5da4f9 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -1,4 +1,5 @@ import axios from "axios"; +import axiosRetry from 'axios-retry'; import _ from "lodash"; const QueryClient = async (chainId, restUrls, opts) => { @@ -200,6 +201,19 @@ const QueryClient = async (chainId, restUrls, opts) => { }); }; + const getTransactions = (params, _opts) => { + const { pageSize, order, retries, ...opts } = _opts + const searchParams = new URLSearchParams() + params.forEach(({key, value}) => { + searchParams.append(key, value) + }) + if(pageSize) searchParams.append('pagination.limit', pageSize) + if(order) searchParams.append('order_by', order) + const client = axios.create({ baseURL: restUrl }); + axiosRetry(client, { retries: retries || 0, shouldResetTimeout: true, retryCondition: (e) => true }); + return client.get("/cosmos/tx/v1beta1/txs?" + searchParams.toString(), opts).then((res) => res.data) + } + const getAllPages = async (getPage, pageCallback) => { let pages = []; let nextKey, error; @@ -266,7 +280,8 @@ const QueryClient = async (chainId, restUrls, opts) => { getGrants, getGranteeGrants, getGranterGrants, - getWithdrawAddress + getWithdrawAddress, + getTransactions }; }; From d49d91cb80c83924b6c8e5a92e12a9740619925a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 21 Oct 2022 13:36:12 +0100 Subject: [PATCH 704/862] Adjust wording --- src/components/ValidatorGrants.js | 2 +- src/components/ValidatorProfile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 55e294502..be190046f 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -211,7 +211,7 @@ function ValidatorGrants(props) { Last REStake
    - {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Never'} : } + {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Not recently'} : } } identifier={operator.address} tooltip="Based on the last REStake transaction sent by this validator for any of their users. Not every run generates a transaction." />
    diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js index c5f2c6f19..13c7e428b 100644 --- a/src/components/ValidatorProfile.js +++ b/src/components/ValidatorProfile.js @@ -140,7 +140,7 @@ function ValidatorProfile(props) { Last REStake
    - {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Never'} : } + {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Not recently'} : } } identifier={operator.address} tooltip="Based on the last REStake transaction sent by this validator for any of their users. Not every run generates a transaction." />
    From 6e029ad638d5132620da0cbe96d9a6bd71f4ddef Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 21 Oct 2022 13:43:17 +0100 Subject: [PATCH 705/862] Fix variable assignment --- src/utils/Operator.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index 574a3af5d..2adeff5ec 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -38,7 +38,7 @@ const Operator = (network, data) => { let date = start.clone() const interval = parse(time.replace('every ', '')) return [...Array(Math.floor(parse('1d') / interval))].map((_, i) => { - current = date.clone() + let current = date.clone() date.add(interval, 'ms') return current }) From f9464d4c087e3b4902b71279f49c2ef5d2961961 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 21 Oct 2022 14:39:18 +0100 Subject: [PATCH 706/862] Increase warning thresholds to 20/30 missed runs --- src/components/ValidatorGrants.js | 14 +++----------- src/components/ValidatorModal.js | 2 +- src/components/ValidatorProfile.js | 2 +- src/networks.json | 3 +-- src/utils/Chain.mjs | 1 - src/utils/Helpers.mjs | 12 ++++++++++-- src/utils/Network.mjs | 1 - 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index be190046f..5c6cec370 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -17,7 +17,7 @@ import { } from 'react-bootstrap-icons' import Coins from './Coins'; -import { buildExecMessage, coin, lastRestakeClassname } from '../utils/Helpers.mjs'; +import { buildExecMessage, coin, lastRestakeClassname, lastRestakeWarning } from '../utils/Helpers.mjs'; import RevokeGrant from './RevokeGrant'; import AlertMessage from './AlertMessage'; import TooltipIcon from './TooltipIcon' @@ -67,14 +67,6 @@ function ValidatorGrants(props) { return !maxTokensDenom() || larger(maxTokensDenom(), props.rewards) } - function lastRestakeWarning() { - if (!operator || lastExec == null) - return; - - const missedRuns = operator.missedRunCount(lastExec); - return missedRuns > Math.min(15, operator.runsPerDay() * 2); - } - function showLoading(isLoading) { setLoading(isLoading) props.setLoading && props.setLoading(isLoading) @@ -184,7 +176,7 @@ function ValidatorGrants(props) { You must delegate to {operator.moniker} before they can REStake for you.
    )} - {lastRestakeWarning() && ( + {lastRestakeWarning(operator, lastExec) && ( This validator has not REStaked recently. @@ -206,7 +198,7 @@ function ValidatorGrants(props) { {operator.runTimesString()} - {network.authzHistorySupport && ( + {network.authzSupport && ( Last REStake diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index 70d203afb..3c2aa52a9 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -110,7 +110,7 @@ function ValidatorModal(props) { } function getLastExec() { - if(!operator || !network.authzHistorySupport){ + if(!operator || !network.authzSupport){ setLastExec() return } diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js index 13c7e428b..61aa6d218 100644 --- a/src/components/ValidatorProfile.js +++ b/src/components/ValidatorProfile.js @@ -135,7 +135,7 @@ function ValidatorProfile(props) { Minimum rewards - {network.authzHistorySupport && ( + {network.authzSupport && ( Last REStake diff --git a/src/networks.json b/src/networks.json index d20f3009a..26da2cc9b 100644 --- a/src/networks.json +++ b/src/networks.json @@ -171,8 +171,7 @@ "average": 0.025, "high": 0.04 }, - "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd", - "authzHistorySupport": false + "ownerAddress": "mantlevaloper1fqs7gakxdmujtk0qufdzth5pfyspus3yx394zd" }, { "name": "crescent", diff --git a/src/utils/Chain.mjs b/src/utils/Chain.mjs index c56a6a73e..36bd978eb 100644 --- a/src/utils/Chain.mjs +++ b/src/utils/Chain.mjs @@ -17,7 +17,6 @@ const Chain = (data) => { estimatedApr: data.params?.calculated_apr, authzSupport, authzAminoSupport: data.authzAminoSupport ?? sdkAuthzAminoSupport ?? false, - authzHistorySupport: data.authzHistorySupport ?? authzSupport, denom: data.denom || baseAsset?.base?.denom, display: data.display || baseAsset?.display?.denom, symbol: data.symbol || baseAsset?.symbol, diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 24fc6025f..1c369bc99 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -30,11 +30,19 @@ export function lastRestakeClassname(operator, lastExec) { return; const missedRuns = operator.missedRunCount(lastExec); - const warning = missedRuns > Math.min(10, operator.runsPerDay()); - const error = missedRuns > Math.min(15, operator.runsPerDay() * 2); + const warning = missedRuns > Math.min(20, operator.runsPerDay()); + const error = missedRuns > Math.min(30, operator.runsPerDay() * 2); return error ? 'text-danger' : warning ? 'text-warning' : 'text-success'; } +export function lastRestakeWarning(operator, lastExec) { + if (!operator || lastExec == null) + return; + + const missedRuns = operator.missedRunCount(lastExec); + return missedRuns > Math.min(30, operator.runsPerDay() * 2); +} + export function buildExecMessage(grantee, messages) { return { typeUrl: "/cosmos.authz.v1beta1.MsgExec", diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 520165c27..af3a48749 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -88,7 +88,6 @@ class Network { this.ledgerSupport = this.chain.ledgerSupport ?? true this.authzSupport = this.chain.authzSupport this.authzAminoSupport = this.chain.authzAminoSupport - this.authzHistorySupport = this.chain.authzHistorySupport this.defaultGasPrice = this.decimals && format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom this.gasPrice = this.data.gasPrice || this.defaultGasPrice if(this.gasPrice){ From 32b0c5745562aac2fc9220c68a8df21d0eef46ed Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 21 Oct 2022 17:55:40 +0100 Subject: [PATCH 707/862] Decrease Juno autostake batchTxs --- src/networks.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 26da2cc9b..782560e0f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -9,7 +9,10 @@ { "name": "juno", "gasPrice": "0.0025ujuno", - "ownerAddress": "junovaloper19ur8r2l25qhsy9xvej5zgpuc5cpn6syydmwtrt" + "ownerAddress": "junovaloper19ur8r2l25qhsy9xvej5zgpuc5cpn6syydmwtrt", + "autostake": { + "batchTxs": 25 + } }, { "name": "cosmoshub", From 673e6890d5f35bd082709e58168f121038a94392 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 21 Oct 2022 17:55:46 +0100 Subject: [PATCH 708/862] Remove incorrect instruction from README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 879312655..f74045fff 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ npm --version ```bash git clone https://github.com/eco-stake/restake cd restake -npm install && npm run build +npm install cp .env.sample .env ``` From 62cd2e0cfe77ce2fea88502f7fb82a74aeb4c9be Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 21 Oct 2022 18:36:38 +0100 Subject: [PATCH 709/862] Fix table borders --- src/components/ValidatorProfile.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js index 61aa6d218..e540b083a 100644 --- a/src/components/ValidatorProfile.js +++ b/src/components/ValidatorProfile.js @@ -132,13 +132,13 @@ function ValidatorProfile(props) { {operator.runTimesString()} - Minimum rewards - + Minimum rewards + {network.authzSupport && ( - Last REStake - + Last REStake +
    {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Not recently'} : } } identifier={operator.address} tooltip="Based on the last REStake transaction sent by this validator for any of their users. Not every run generates a transaction." /> From cba25bd08baccc66704a39eaf063b404dca84a40 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 21 Oct 2022 19:08:38 +0100 Subject: [PATCH 710/862] Refactor --- src/components/OperatorLastRestake.js | 28 ++++++++++++++++++++++ src/components/OperatorLastRestakeAlert.js | 23 ++++++++++++++++++ src/components/ValidatorGrants.js | 18 ++++---------- src/components/ValidatorProfile.js | 8 ++----- src/utils/Helpers.mjs | 18 -------------- 5 files changed, 58 insertions(+), 37 deletions(-) create mode 100644 src/components/OperatorLastRestake.js create mode 100644 src/components/OperatorLastRestakeAlert.js diff --git a/src/components/OperatorLastRestake.js b/src/components/OperatorLastRestake.js new file mode 100644 index 000000000..bb685a54b --- /dev/null +++ b/src/components/OperatorLastRestake.js @@ -0,0 +1,28 @@ +import { + QuestionCircle +} from 'react-bootstrap-icons' + +import TooltipIcon from './TooltipIcon' + +function OperatorLastRestake(props) { + const { operator, lastExec } = props + + function classname() { + if (!operator || lastExec == null) + return; + + const missedRuns = operator.missedRunCount(lastExec); + const warning = missedRuns > Math.min(20, operator.runsPerDay()); + const error = missedRuns > Math.min(30, operator.runsPerDay() * 2); + return error ? 'text-danger' : warning ? 'text-warning' : 'text-success'; + } + + return ( +
    + {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Not recently'} : } + } identifier={operator.address} tooltip="Based on the last REStake transaction sent by this validator for any of their users. Not every run generates a transaction." /> +
    + ) +} + +export default OperatorLastRestake \ No newline at end of file diff --git a/src/components/OperatorLastRestakeAlert.js b/src/components/OperatorLastRestakeAlert.js new file mode 100644 index 000000000..641c17f32 --- /dev/null +++ b/src/components/OperatorLastRestakeAlert.js @@ -0,0 +1,23 @@ +import AlertMessage from './AlertMessage'; + +function OperatorLastRestakeAlert(props) { + const { operator, lastExec } = props + + function showWarning() { + if (!operator || lastExec == null) + return; + + const missedRuns = operator.missedRunCount(lastExec); + return missedRuns > Math.min(30, operator.runsPerDay() * 2); + } + + if (!showWarning()) return null + + return ( + + This validator has not REStaked recently. + + ) +} + +export default OperatorLastRestakeAlert \ No newline at end of file diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 5c6cec370..9cf987168 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -12,15 +12,14 @@ import { Form, Table } from 'react-bootstrap' -import { - QuestionCircle, -} from 'react-bootstrap-icons' import Coins from './Coins'; -import { buildExecMessage, coin, lastRestakeClassname, lastRestakeWarning } from '../utils/Helpers.mjs'; +import { buildExecMessage, coin } from '../utils/Helpers.mjs'; import RevokeGrant from './RevokeGrant'; import AlertMessage from './AlertMessage'; import TooltipIcon from './TooltipIcon' +import OperatorLastRestake from './OperatorLastRestake'; +import OperatorLastRestakeAlert from './OperatorLastRestakeAlert'; function ValidatorGrants(props) { const { grants, wallet, operator, address, network, lastExec } = props @@ -176,11 +175,7 @@ function ValidatorGrants(props) { You must delegate to {operator.moniker} before they can REStake for you. )} - {lastRestakeWarning(operator, lastExec) && ( - - This validator has not REStaked recently. - - )} + {error && {error} @@ -202,10 +197,7 @@ function ValidatorGrants(props) { Last REStake -
    - {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Not recently'} : } - } identifier={operator.address} tooltip="Based on the last REStake transaction sent by this validator for any of their users. Not every run generates a transaction." /> -
    + )} diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js index e540b083a..c35753099 100644 --- a/src/components/ValidatorProfile.js +++ b/src/components/ValidatorProfile.js @@ -12,12 +12,11 @@ import { } from 'react-bootstrap' import { - QuestionCircle, XCircle } from 'react-bootstrap-icons' import ValidatorServices from './ValidatorServices'; import ValidatorNetworks from './ValidatorNetworks'; -import { lastRestakeClassname } from '../utils/Helpers.mjs'; +import OperatorLastRestake from './OperatorLastRestake'; function ValidatorProfile(props) { const { validator, operator, network, networks, registryData, lastExec } = props @@ -139,10 +138,7 @@ function ValidatorProfile(props) { Last REStake -
    - {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Not recently'} : } - } identifier={operator.address} tooltip="Based on the last REStake transaction sent by this validator for any of their users. Not every run generates a transaction." /> -
    + )} diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 1c369bc99..8ac822cdc 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -25,24 +25,6 @@ export function overrideNetworks(networks, overrides){ }) } -export function lastRestakeClassname(operator, lastExec) { - if (!operator || lastExec == null) - return; - - const missedRuns = operator.missedRunCount(lastExec); - const warning = missedRuns > Math.min(20, operator.runsPerDay()); - const error = missedRuns > Math.min(30, operator.runsPerDay() * 2); - return error ? 'text-danger' : warning ? 'text-warning' : 'text-success'; -} - -export function lastRestakeWarning(operator, lastExec) { - if (!operator || lastExec == null) - return; - - const missedRuns = operator.missedRunCount(lastExec); - return missedRuns > Math.min(30, operator.runsPerDay() * 2); -} - export function buildExecMessage(grantee, messages) { return { typeUrl: "/cosmos.authz.v1beta1.MsgExec", From 8d6f7211680afc3809e60242a10a5bb32a795cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20K=C3=B6l=20Ti=C4=9Fin?= <102043225+koltigin@users.noreply.github.com> Date: Sat, 22 Oct 2022 19:50:48 +0300 Subject: [PATCH 711/862] Create README_TURKISH.md --- README_TURKISH.md | 433 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 README_TURKISH.md diff --git a/README_TURKISH.md b/README_TURKISH.md new file mode 100644 index 000000000..ef2530b6e --- /dev/null +++ b/README_TURKISH.md @@ -0,0 +1,433 @@ +# [REStake](https://restake.app) + +REStake, delegatorlerin validatorlerine stake ödüllerini yeniden stake etmesine izin vermelerine olanak tanır. REStake validatorlerin bir komut dosyasını çalıştırarak kendilerine verilen delegeleri bulmaları ve restake işlemlerini otomatik olarak yapmalarını sağlar. + +REStake, aynı zamanda kullanışlı bir stake etme aracıdır. Ödüllerinizi tek tek ya da toplu olarak telep etmenize ve yeniden stake etmenizi sağlar. Bu da işlem ücretlerinden ve zamandan tasarruf etmenizi sağlar ve daha birçok özellik de planlanmıştır. + +[![](./docs/screenshot.png)](https://restake.app) + +[restake.app](https://restake.app)'ı deneyin. + +## Nasıl çalışır / Authz (Yetkilendirme) + +Authz, Tendermint zincirleri için başka bir cüzdana sizin için belirli işlemleri gerçekleştirmesi için izin vermenizi sağlayan yeni bir özelliktir. + +Bu işlemler, stake eden adına stake alan tarafından gönderilir, yani valiatorler fee ücretini gönderecek ve ödeyecektir. Bu işlemler cüzdanınızı etkiler, ödül talep etme vb. yani siz restake işlemini kapatmadığınız sürece kazandığınız ödüller sürekli restake edilir. + +REStake, özellikle bir validatore `Delegate` işlemlerini yalnızca validatorleriniz yapmasına izin vermenizi sağlar. Validator başka herhangi bir işlem türü gönderemez ya da cüzdanınıza herhangi bir erişimi yoktur. Bunu normal olarak Keplr kullanarak yetkilendiriyorsunuz. REStake artık oto-stake için bir `çekme` izni gerektirmiyor. + +Doğrulayıcının delegelerini otomatik olarak aramasını sağlayan bir komut dosyası da sağlanmıştır, gerekli ödemeler için her birini kontrol edin ve varsa, talep ve işlemleri tek bir işlemde onlar adına gerçekleştirin. Bu script günlük olarak çalıştırılmalıdır ve çalıştıracağınız saat [operatörünüzü eklediğinizde](#become-an-operator) belirtilebilir. + + +## Kısıtlamalar + +Yazılı olarak, Ledger, Authz (Yetkilendirmeyi) etkinleştirmek için gerekli işlemleri gönderemiyor. Bu tamamen işlemlerin bir Ledger cihazına gönderilme şeklinden kaynaklanmaktadır ve yakında bir geçici çözüm mümkün olabilecektir. + +Authz da henüz tam olarak desteklenmemektedir. Birçok zincir henüz güncellenmedi. REStake UI, kullanışlı manuel birleştirme özelliklerine sahip bir manuel stake uygulaması olmaya geri dönecek. + +Şu anda REStake, Keplr'ın tarayıcı uzantısı sürümüne ihtiyaç duyuyor, ancak WalletConnect ve Keplr iOS işlevselliği en kısa sürede eklenecek. + + +## Operatör olun + +Bot cüzdanınızı kurun +Otomatik komut dosyasını ayarlayın +Komut dosyasını bir çizelgede çalıştırmak için bir cron veya zamanlayıcı kurun +Operatörünüzü Doğrulayıcı Kayıt Defterine (Validator Registry) gönderin. + +### Sıcak bir cüzdan kurun + +Stake işlemlerini otomatik olarak gerçekleştirmek için kullanacağınız yeni bir sıcak cüzdan oluşturun. Komut dosyasına anımsatıcının (mnemonic) sağlanması gerekecek, bu nedenle özel bir cüzdan kullanın ve yalnızca işlem ücretleri için yeterli parayı saklayın. Burada gerekli olan SADECE menemonic, sıcak cüzdan içindir, validatorünüze ait cüzdan mnemoniclerinizi hiçbir yere yazmayın. + +Birden çok Cosmos zinciri için yalnızca tek bir anımsatıcıya ihtiyacınız vardır ve komut dosyası, eşleşen bir bot adresi için [networks.json](./src/networks.json) dosyasındaki her ağı kontrol eder. + +#### Türetme Yolları (ÖNEMLİ) + +Şu anda, REStake otomatik stake komut dosyası varsayılan olarak standart 118 türetme yolunu kullanır. Bazı ağlar farklı bir yolu tercih eder ve Keplr gibi uygulamalar bunu izin verecektir. Otomatik stake komutunun kullandığı adres Keplr ile eşleşmeyebilir. + +118 yolunu kullanan mevcut operatörler olduğundan, operatörlerin yükseltme yapmak istediklerinde doğru yolu seçmeleri gerekecektir. Yeni operatörler, stake almadan önce doğru yolu kullanmalıdır. + +Doğru yol, bir [yapılandırma geçersiz kılma](#overriding-networks-config-locallyuse-your-own-node) dosyası kullanılarak iki yoldan biriyle ayarlanabilir. `"correctSlip44": true`, Zincir Kaydı'nda tanımlanan slip44'ü kullanır. Alternatif olarak `"slip44": 69` kullanarak belirli bir yol ayarlayın. Mümkünse `"correctSlip44": true` kullanmalısınız. + +```jsonc +{ + "desmos": { + "prettyName": "Desmos 852", + "autostake": { + "correctSlip44": true + } + } +} +``` + +Gelecekte, `correctSlip44` varsayılan olacak ve 118 yolunu kullanmak istiyorsanız `slip44`'ü açıkça ayarlamanız gerekecek. + +### Oto-stake komut dosyasını ayarlama + +Otomatik ayırma komut dosyasını `docker-compose` ya da doğrudan `npm` kullanarak çalıştırabilirsiniz. Her iki durumda da anımsatıcınızı bir `MNEMONIC` ortam değişkeninde sağlamanız gerekecektir. + +#### Docker Compose için Talimatlar + +##### Docker ve Docker Compose Yükleme + +En iyisi Docker resmi kılavuzlarını takip etmektir. Önce Docker'ı, ardından Docker Compose'u yükleyin. Son sürümlerde, Docker ve Docker Compose tek bir kurulumda birleştirilebilir. + +Docker: [docs.docker.com/get-docker](https://docs.docker.com/get-docker/) + +Docker Compose: [docs.docker.com/compose/install](https://docs.docker.com/compose/install/) + +##### Depoyu klonlama ve .env'yi kurma + +Depoyu klonlayın ve anımsatıcınız (menemonic kelimeleriniz) için hazır olan örnek .env dosyasını kopyalayın. + +```bash +git clone https://github.com/eco-stake/restake +cd restake +cp .env.sample .env +``` + +**Yeni .env dosyanıza anımsatıcı kelimelerinizi yazın.** + +#### NPM için Talimatlar + +##### nodejs@v17 yükleme + +```bash +curl -sL https://deb.nodesource.com/setup_17.x -o /tmp/nodesource_setup.sh +# read the script file and when you're sure it's safe run it +chmod +x /tmp/nodesource_setup.sh +/tmp/nodesource_setup.sh +apt install nodejs -y +node --version +> v17.9.0 +npm --version +> 8.5.5 +``` + +Depoyu klonlama ve yükleme + +```bash +git clone https://github.com/eco-stake/restake +cd restake +npm install && npm run build +cp .env.sample .env +``` +**Yeni .env dosyanıza anımsatıcı kelimelerinizi yazın.** + +#### Yerel versiyonunuzu güncelleme + +REStake MVP'dir. Çok MVP. Güncellemeler her zaman oluyor ve hala düzeltilmesi gereken hatalar var. Sık sık güncelleme yaptığınızdan emin olun. + +Yerel deponuzu güncelleyin ve aşağıdaki komutlarla Docker konteynırlarınızı önceden oluşturun: + +```bash +git pull +docker-compose run --rm app npm install +docker-compose build --no-cache +``` + +NPM için kodlar: + +```bash +git pull +npm install +``` + +Komut dosyasını çalıştırma + +Oto-stake komut dosyasını manuel olarak çalıştırmak basittir. + +Docker kullanıyorsanız, aşağıdaki komutları `docker-compose run ---rm app` ile kullanmalısınız. + +Not: Docker kurulumunuzda root yetkisine sahip değilseniz `sudo` kullanmanız gerekebilir ve bazı docker sürümleri `docker-compose` yerine `docker compose`'u kullanır. Sorunlarla karşılaşırsanız, `docker compose' kullanmayı deneyin. + +```bash +docker-compose run --rm app npm run autostake +``` + +Alternatif olarak, eğer NPM kullanıyorsanız, `docker-compose run --rm app` önekini göz ardı edebilirsiniz ve aağıdaki kodu kullanmanız yeterli olacaktır. + +```bash +npm run autostake +``` + +Komut dosyasını belirli ağlarla sınırlamak için ağ adlarını yazabilirsiniz. + +```bash +npm run autostake osmosis akash regen +``` + +Normal oto-stake komut dosyasını çalıştıran ancak gönderilen son TX'leri ve herhangi bir sağlık durumu kontrolü için pingleri atlayan bir Dry Run komut dosyası da kullanılabilir. + +```bash +npm run dryrun osmosis +``` + +**REStake operatör bilgilerinizi [Operatörünüzü kaydetme](#submitting-your-operator) bölümünde gösterileceği gibi Validator Regisrty kaydınızı yapana kadar 'operatör olmadığınıza dair bir uyarı görebilirsiniz.** + + +### REStake'i özelleştirin ve node'unuzu kullanın + +Muhtemelen ağların yapılandırılmasını özelleştirmek isteyeceksiniz, örneğin auto compounding (ödüllerinizi otomatik olarak yeniden stake havuzuna aktarmak) komut dosyanızın başarılı bir şekilde tamamlanmasını sağlamak için node'unuzun URL'lerinizi ayarlamak gibi. + +Bir `src/networks.local.json` dosyası oluşturun ve geçersiz kılmak istediğiniz ağları belirtin. Aşağıdaki sadece bir örnektir, **ihtiyaç gerekirse sadece bir yapılandırmayı geçersiz kılmalısınız.** + +```json +{ + "osmosis": { + "prettyName": "Osmosis", + "restUrl": [ + "https://rest.cosmos.directory/osmosis" + ], + "gasPrice": "0.0025uosmo", + "autostake": { + "retries": 3, + "batchPageSize": 100, + "batchQueries": 25, + "batchTxs": 50, + "delegationsTimeout": 20000, + "queryTimeout": 5000, + "queryThrottle": 100, + "gasModifier": 1.1 + }, + "healthCheck": { + "uuid": "XXXXX-XXX-XXXX" + } + }, + "desmos": { + "prettyName": "Desmos 118", + "autostake": { + "correctSlip44": true + } + }, + "cosmoshub": { + "enabled": false + } +} +``` + +Belirttiğiniz değerler `networks.json` dosyasını geçersiz kılacaktır. Bunlar örneklerdir, ihtiyacınız göre ayarlayabilirsiniz. + +Diziler değiştirilecek ve birleştirilmeyecektir. Dosya `.gitignore`'idir, böylece yukarı akış güncellemelerini etkilemez. + +REStake'in, indeksleme etkin ve `networks.json` gas fiyatı ile eşleşen minimum gas fiyatlarına sahip bir düğüm gerektirdiğini unutmayın. + +### Komut dosyasını bir program dahilinde çalıştırmak için cron/zamanlayıcıları ayarlamak + +Komut dosyasını her gün aynı saatte çalıştıracak şekilde ayarlamalısınız. 2 yöntem aşağıda açıklanmıştır; `crontab` ya da `systemd-timer` kullanma. + +Her iki durumda da, sistem zamanınızın doğru olduğundan emin olun ve komut dosyasının UTC'de ne zaman çalışacağını biliyor olmalısınız, çünkü bu daha sonra gerekli olacak. Her iki örnek de saat 21:00'e göre verilmiştir. + +[Sık sık güncellemeyi](#updating-your-local-version) unutmayın! + +#### `crontab` Kullanma + +NOT: REStake zamanlayıcınızı `crontab`'a göre belirlemek için faydalı bir hesap makinesini buradan ulaşabilirsiniz: https://crontab.guru/. + +Güncellenmiş sürümler, `docker-compose` yerine `docker compose` kullanır. Sorunlarla karşılaşırsanız, `docker compose` yerine bunu kullanmayı deneyin. + +```bash +crontab -e +0 21 * * * /bin/bash -c "cd restake && docker compose run --rm app npm run autostake" > ./restake.log 2>&1 +``` + +ya da NPM için bunu kullanabilirsiniz: + +```bash +crontab -e +0 21 * * * /bin/bash -c "cd restake && npm run autostake" > ./restake.log 2>&1 +``` + +#### `systemd-timer` Kullanma + +Systemd-timer, belirtilen kurallarla bir kerelik hizmetin çalıştırılmasına izin verir. Bu yöntem tartışmasız Cron'a tercih edilir. + +##### systemd birimi dosyası oluşturma + +The unit file describe the application to run. We define a dependency with the timer with the `Wants` statement. + +Birim dosyası çalıştırılacak uygulamayı tanımlar. `Wants` ve zamanlayıcı ifadesi ile bir bağımlılık tanımlıyoruz. + +```bash +sudo vim /etc/systemd/system/restake.service +``` + +```bash +[Unit] +Description=restake service with docker compose +Requires=docker.service +After=docker.service +Wants=restake.timer +[Service] +Type=oneshot +WorkingDirectory=/path/to/restake +ExecStart=/usr/bin/docker-compose run --rm app npm run autostake +[Install] +WantedBy=multi-user.target +``` +NPM kurulumu için `Requires` ve `After` direktiflerini kaldırın ve` `ExecStart`'ı` `ExecStart=/usr/bin/npm run autostake` olarak değiştirin. + +Not: Sorun yaşarsanız `WorkingDirectory=/path/to/restake` bölümünü `WorkingDirectory=/root/restake` olarak değiştiriniz. Eğer yine sorun yaşarsanız `chmod 777 /root/restake` komutu ile dosyaya okuma, yazma ve çalıştırma izni veriniz. Daha sonra `systemctl daemon-reload` yaptıktan sonra sistemi yeniden başlatınız. + +Eğer aşağıdaki gibi bir hata alırsanız yine `chmod 777 /usr/bin/docker-compose` komutu ile dosyaya okuma, yazma ve çalıştırma izni veriniz. + +Çözüm için değerli arkadaşım [Odyseus'a]() teşekkür ederim. + +##### systemd timer dosyası oluşturma + +Zamanlayıcı dosyası, yeniden düzenleme hizmetini her gün çalıştırma kurallarını tanımlar. Tüm kurallar [systemd dokümanlarında] (https://www.freedesktop.org/software/systemd/man/systemd.timer.html) açıklanmaktadır. + +Not: `OnCalendar` için restake sürelerini belirlemek için yararlı hesap makinesi https://crontab.guru/ adresinde bulunabilir. + +```bash +sudo vim /etc/systemd/system/restake.timer +``` + +```bash +[Unit] +Description=Restake bot timer +[Timer] +AccuracySec=1min +OnCalendar=*-*-* 21:00:00 +[Install] +WantedBy=timers.target +``` + +##### Servisleri Etkinleştirme ve Başlatma + +```bash +systemctl enable restake.service +systemctl enable restake.timer +systemctl start restake.timer +``` + +##### Zamanlayıcınızı kontrol etme + +`systemctl status restake.timer` +
     restake.timer - Restake bot timer
    +     Loaded: loaded (/etc/systemd/system/restake.timer; enabled; vendor preset: enabled)
    +     Active: active (waiting) since Sun 2022-03-06 22:29:48 UTC; 2 days ago
    +    Trigger: Wed 2022-03-09 21:00:00 UTC; 7h left
    +   Triggers: ● restake.service
    +
    + +`systemctl status restake.service` +
    ● restake.service - stakebot service with docker compose
    +     Loaded: loaded (/etc/systemd/system/restake.service; enabled; vendor preset: enabled)
    +     Active: inactive (dead) since Tue 2022-03-08 21:00:22 UTC; 16h ago
    +TriggeredBy:  restake.timer
    +    Process: 86925 ExecStart=/usr/bin/docker-compose run --rm app npm run autostake (code=exited, status=0/SUCCESS)
    +   Main PID: 86925 (code=exited, status=0/SUCCESS)
    +
    + +### İzleme + +Her ağ için komut dosyası durumunu bildirmek için REStake oto-stake betiği [healthchecks.io] (https://healthchecks.io/) ile entegre olabilir. [HealthChecks.io] (https://healthchecks.io/) daha sonra, herhangi bir arızayı bildiğinizden emin olmak için e -posta, Discord ve Slack gibi birçok bildirim platformuyla entegre edilebilir. + +Yapılandırıldıktan sonra, komut dosyası başladığında, başarılı ya da başarısız olduğunda REStake [healthchecks.io](https://healthchecks.io/)'a ping atacaktır. Kontrol günlüğü ilgili hata bilgilerini içerecektir ve yapılandırılması basittir. + +Komut dosyasını çalıştırdığınız her ağ için bir kontrol ayarlayın ve beklenen programı yapılandırın. Örneğin, her 12 saatte bir Osmosis kontrolü ekleyin, Akash için her 1 saatte bir vb. + +Kontrol UUID numaranızı aşağıdaki gibi `networks.local.json` yapılandırma dosyanızda ilgili ağa ekleyin. İsteğe bağlı olarak [HealthChecks.io platformunu kendi hostinginizde barındırmak](https://healthchecks.io/docs/self_hosted/) istiyorsanız. `address` özniteliğini de ayarlayabilirsiniz. + +```JSON +{ + "osmosis": { + "healthCheck": { + "uuid": "77f02efd-c521-46cb-70g8-fa5v275au873" + } + } +} +``` + +### Operatörünüzü kaydetme + +#### REStake Operatörünüzü Kurma + +Artık operatör bilgilerinizi oto-sake'i aktif etmek istediğiniz ağları eklemek için [Doğrulayıcı Kayıt Defteri](https://github.com/eco-stake/validator-registry)'ni güncellemeniz gerekiyor. Örnekler için README ve mevcut doğrulayıcıları kontrol edebilirsiniz, ancak bir ağ için yapılandırma şuna benziyor: + + +You now need to update the [Validator Registry](https://github.com/eco-stake/validator-registry) to add your operator information to any networks you want to auto-compound for. Check the README and existing validators for examples, but the config for a network looks like this: + +```json +{ + "name": "akash", + "address": "akashvaloper1xgnd8aach3vawsl38snpydkng2nv8a4kqgs8hf", + "restake": { + "address": "akash1yxsmtnxdt6gxnaqrg0j0nudg7et2gqczud2r2v", + "run_time": [ + "09:00", + "21:00" + ], + "minimum_reward": 1000 + } +}, +``` + +`address` doğrulayıcınızın adresidir ve `restake.address` ise fee ödemeleri için oluşturduğunuz yeni sıcak cüzdanınızın adresidir. + +`restake.run_time` *UTC zaman diliminde* botunuzu çalıştırmayı düşündüğünüz zamandır ve orada birkaç seçenek vardır. Belli bir saat ayarlamak için, ör. `09:00`, UTC zaman diliminde 9am (sabah dokuzda) scripti çalıştırdığınızı belirtir. Birden fazla zaman için bir dizi de kullanabilirsiniz, örneğin `["09:00", "21:00"]`. Saatte/günde birden çok kez için bir aralık dizesi kullanabilirsiniz, örneğin, `"every 15 minutes"`. + +`restake.minimum_reward`, otomatik stake'i tetiklemek için asgari ödüldür, aksi takdirde adres atlanır. Bu, daha sık yeniden düzenleme için daha yüksek ayarlanabilir. Bunun temel nominal değer olduğunu unutmayın, Örneğin, `uosmo`. + +`restake.minimum_reward` is the minimum reward to trigger autostaking, otherwise the address be skipped. This could be set higher for more frequent restaking. Note this is in the base denomination, e.g. `uosmo`. + +REStake yapmak istediğiniz tüm ağlar için bu yapılandırmayı tekrarlayın. + +`restake.address`'in kullanıcı ara yüzünde delegator'ün restake işlemlerini gerçekleştirmek için vermiş olduğu adrese stake işleminde fee ücretinin lınacağı adres olduğunu unutmayın. + + +#### Operatörünüzü Validator Kayıt Defterine kaydetme + +Artık [Validator Kayıt Defteri] (https://github.com/eco-stake/validator-registry) güncellemenizi mümkün olan en kısa sürede merge edilmek üzere pull request isteğinde bulunabilirsiniz. REStake, değişikliklerin birleştirilmesinden sonraki 15 dakika içinde otomatik olarak güncellenir. + +## Katkıda Bulunma + +### Bir Ağ Ekleme/Güncelleme + +Ağ bilgileri [Zincir Kayıt Defteri] (https://github.com/cosmos/chain-registry) [registry.cosmos.directory] (https://registry.cosmos.directory) API üzerinden alınır. Yeterli temel bilgilerin sağlandığı varsayılarak, REStake'e ana daldaki zincirler otomatik olarak eklenir. + +'networks.json' dosyası, REStake'de 'desteklendiği' gibi hangi zincirlerin göründüğünü tanımlar; zincir adı Zincir Kayıt Defterinden dizin adıyla eşleştiği sürece, tüm zincir bilgileri otomatik olarak sağlanacaktır. Alternatif olarak zincirler, tek başına `networks.json`'da _desteklenebilir_, ancak bu belgelenmiş bir özellik değildir. + +Bir zinciri yeniden eklemek veya geçersiz kılmak için gerekli bilgileri aşağıdaki gibi `networks.json`'a ekleyin: + +```json +{ + "name": "osmosis", + "prettyName": "Osmosis", + "gasPrice": "0.025uosmo", + "authzSupport": true +} +``` + +`networks.json`'daki CamelCase sürümünü tanımlayarak zincir kayıt defterinin çoğunun geçersiz kılınabileceğini unutmayın. + +### Kullanıcı Arayüzünü (UI) Çalıştırma + +Docker'ı kullanarak kullanıcı arayüzünü bir satırla çalıştırın: + +```bash +docker run -p 80:80 -t ghcr.io/eco-stake/restake +``` + +`docker-compose up` ya da `npm start` kullanarak kaynaktan alternatif çalıştırma; + +## Etik + +REStake kullanıcı arayüzü hem validator hem de ağ için agnostiktir. Herhangi bir delegator bir operatör olarak eklenebilir ve delegatorlerine otomatik birleştirme hizmeti sağlamak için bu aracı çalıştırabilir ancak markayı kendilerine uyacak şekilde seçip ayarlarlarsa kendi kullanıcı arayüzlerini de çalıştırabilirler. + +Bunun çalışması için ortak bir zincir bilgisi kaynağına ve ortak bir 'operator' bilgisi kaynağına ihtiyacımız var. Zincir bilgileri, [Cosmos.Directory](https://github.com/eco-stake/cosmos-directory) tarafından sağlanan bir API aracılığıyla [Zincir Kayıt Defteri](https://github.com/cosmos/chain-registry)'nden temin edilir. Operatör bilgileri [Validator Kayıt Defteri](https://github.com/eco-stake/validator-registry)'nde bulunur. + +Artık ortak bir operatör bilgisi kaynağımız var, uygulamalar verileri doğrudan GitHub'dan ya da [cosmos.directory](https://github.com/eco-stake/cosmos-directory) projesi aracılığıyla yeniden kullanabilir. + +## Feragatname + +REStake ilk sürümü yeni authz özelliklerinden yararlanmak için hızlı bir şekilde oluşturuldu. + +Ben şahsen bir React veya JavaScript geliştiricisi değilim ve bu proje [CosmJS projesi](https://github.com/cosmos/cosmjs) ve [Keplr Wallet](https://github.com/chainapsis/keplr-wallet) ve [Osmosis Zone frontend](https://github.com/osmosis-labs/osmosis-frontend) gibi diğer fantastik kod tabanlarına son derece eğiliyor. + +## ECO Stake 🌱 + +ECO Stake iklim pozitif bir validatordur, ancak Cosmos ekosistemini de önemsiyoruz. Tüm validatorlerin Authz ile bir otomatik stake çalıştırmasını kolaylaştırmak için REStake'i inşa ettik ve ekosistemde üzerinde çalıştığımız birçok projeden biridir. + +Bunun gibi daha fazla projeyi desteklemek için bizimle delege edin [bizimle delege edin](https://ecostake.com). From d5d146b32c2e55bf06ec382094022186d291c703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20K=C3=B6l=20Ti=C4=9Fin?= <102043225+koltigin@users.noreply.github.com> Date: Sat, 22 Oct 2022 20:35:34 +0300 Subject: [PATCH 712/862] Update README_TURKISH.md --- README_TURKISH.md | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/README_TURKISH.md b/README_TURKISH.md index ef2530b6e..7996a2f50 100644 --- a/README_TURKISH.md +++ b/README_TURKISH.md @@ -18,7 +18,6 @@ REStake, özellikle bir validatore `Delegate` işlemlerini yalnızca validatorle Doğrulayıcının delegelerini otomatik olarak aramasını sağlayan bir komut dosyası da sağlanmıştır, gerekli ödemeler için her birini kontrol edin ve varsa, talep ve işlemleri tek bir işlemde onlar adına gerçekleştirin. Bu script günlük olarak çalıştırılmalıdır ve çalıştıracağınız saat [operatörünüzü eklediğinizde](#become-an-operator) belirtilebilir. - ## Kısıtlamalar Yazılı olarak, Ledger, Authz (Yetkilendirmeyi) etkinleştirmek için gerekli işlemleri gönderemiyor. Bu tamamen işlemlerin bir Ledger cihazına gönderilme şeklinden kaynaklanmaktadır ve yakında bir geçici çözüm mümkün olabilecektir. @@ -27,7 +26,6 @@ Authz da henüz tam olarak desteklenmemektedir. Birçok zincir henüz güncellen Şu anda REStake, Keplr'ın tarayıcı uzantısı sürümüne ihtiyaç duyuyor, ancak WalletConnect ve Keplr iOS işlevselliği en kısa sürede eklenecek. - ## Operatör olun Bot cüzdanınızı kurun @@ -244,8 +242,6 @@ Systemd-timer, belirtilen kurallarla bir kerelik hizmetin çalıştırılmasına ##### systemd birimi dosyası oluşturma -The unit file describe the application to run. We define a dependency with the timer with the `Wants` statement. - Birim dosyası çalıştırılacak uygulamayı tanımlar. `Wants` ve zamanlayıcı ifadesi ile bir bağımlılık tanımlıyoruz. ```bash @@ -271,7 +267,7 @@ Not: Sorun yaşarsanız `WorkingDirectory=/path/to/restake` bölümünü `Workin Eğer aşağıdaki gibi bir hata alırsanız yine `chmod 777 /usr/bin/docker-compose` komutu ile dosyaya okuma, yazma ve çalıştırma izni veriniz. -Çözüm için değerli arkadaşım [Odyseus'a]() teşekkür ederim. +Çözüm için değerli arkadaşım [Odyseus](https://github.com/odyseus8)'a teşekkür ederim. ##### systemd timer dosyası oluşturma @@ -344,10 +340,8 @@ Kontrol UUID numaranızı aşağıdaki gibi `networks.local.json` yapılandırma #### REStake Operatörünüzü Kurma -Artık operatör bilgilerinizi oto-sake'i aktif etmek istediğiniz ağları eklemek için [Doğrulayıcı Kayıt Defteri](https://github.com/eco-stake/validator-registry)'ni güncellemeniz gerekiyor. Örnekler için README ve mevcut doğrulayıcıları kontrol edebilirsiniz, ancak bir ağ için yapılandırma şuna benziyor: - +Artık operatör bilgilerinizi oto-sake'i aktif etmek istediğiniz ağları eklemek için [Validator Kayıt Defteri](https://github.com/eco-stake/validator-registry)'ni güncellemeniz gerekiyor. Örnekler için README ve mevcut doğrulayıcıları kontrol edebilirsiniz, ancak bir ağ için yapılandırma şuna benziyor: -You now need to update the [Validator Registry](https://github.com/eco-stake/validator-registry) to add your operator information to any networks you want to auto-compound for. Check the README and existing validators for examples, but the config for a network looks like this: ```json { @@ -370,13 +364,10 @@ You now need to update the [Validator Registry](https://github.com/eco-stake/val `restake.minimum_reward`, otomatik stake'i tetiklemek için asgari ödüldür, aksi takdirde adres atlanır. Bu, daha sık yeniden düzenleme için daha yüksek ayarlanabilir. Bunun temel nominal değer olduğunu unutmayın, Örneğin, `uosmo`. -`restake.minimum_reward` is the minimum reward to trigger autostaking, otherwise the address be skipped. This could be set higher for more frequent restaking. Note this is in the base denomination, e.g. `uosmo`. - REStake yapmak istediğiniz tüm ağlar için bu yapılandırmayı tekrarlayın. `restake.address`'in kullanıcı ara yüzünde delegator'ün restake işlemlerini gerçekleştirmek için vermiş olduğu adrese stake işleminde fee ücretinin lınacağı adres olduğunu unutmayın. - #### Operatörünüzü Validator Kayıt Defterine kaydetme Artık [Validator Kayıt Defteri] (https://github.com/eco-stake/validator-registry) güncellemenizi mümkün olan en kısa sürede merge edilmek üzere pull request isteğinde bulunabilirsiniz. REStake, değişikliklerin birleştirilmesinden sonraki 15 dakika içinde otomatik olarak güncellenir. @@ -422,9 +413,7 @@ Artık ortak bir operatör bilgisi kaynağımız var, uygulamalar verileri doğr ## Feragatname -REStake ilk sürümü yeni authz özelliklerinden yararlanmak için hızlı bir şekilde oluşturuldu. - -Ben şahsen bir React veya JavaScript geliştiricisi değilim ve bu proje [CosmJS projesi](https://github.com/cosmos/cosmjs) ve [Keplr Wallet](https://github.com/chainapsis/keplr-wallet) ve [Osmosis Zone frontend](https://github.com/osmosis-labs/osmosis-frontend) gibi diğer fantastik kod tabanlarına son derece eğiliyor. +REStake ilk sürümü yeni authz özelliklerinden yararlanmak için hızlı bir şekilde oluşturuldu. Ben şahsen bir React veya JavaScript geliştiricisi değilim ve bu proje [CosmJS projesi](https://github.com/cosmos/cosmjs) ve [Keplr Wallet](https://github.com/chainapsis/keplr-wallet) ve [Osmosis Zone frontend](https://github.com/osmosis-labs/osmosis-frontend) gibi diğer fantastik kod tabanlarına son derece eğiliyor. ## ECO Stake 🌱 From 93ed94a2c9d5bb0a8352c5288b295f06a4f69347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20K=C3=B6l=20Ti=C4=9Fin?= <102043225+koltigin@users.noreply.github.com> Date: Sat, 22 Oct 2022 20:54:09 +0300 Subject: [PATCH 713/862] Update README_TURKISH.md --- README_TURKISH.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README_TURKISH.md b/README_TURKISH.md index 7996a2f50..fc8f4f9fc 100644 --- a/README_TURKISH.md +++ b/README_TURKISH.md @@ -16,7 +16,7 @@ Bu işlemler, stake eden adına stake alan tarafından gönderilir, yani valiato REStake, özellikle bir validatore `Delegate` işlemlerini yalnızca validatorleriniz yapmasına izin vermenizi sağlar. Validator başka herhangi bir işlem türü gönderemez ya da cüzdanınıza herhangi bir erişimi yoktur. Bunu normal olarak Keplr kullanarak yetkilendiriyorsunuz. REStake artık oto-stake için bir `çekme` izni gerektirmiyor. -Doğrulayıcının delegelerini otomatik olarak aramasını sağlayan bir komut dosyası da sağlanmıştır, gerekli ödemeler için her birini kontrol edin ve varsa, talep ve işlemleri tek bir işlemde onlar adına gerçekleştirin. Bu script günlük olarak çalıştırılmalıdır ve çalıştıracağınız saat [operatörünüzü eklediğinizde](#become-an-operator) belirtilebilir. +Doğrulayıcının delegelerini otomatik olarak aramasını sağlayan bir komut dosyası da sağlanmıştır, gerekli ödemeler için her birini kontrol edin ve varsa, talep ve işlemleri tek bir işlemde onlar adına gerçekleştirin. Bu script günlük olarak çalıştırılmalıdır ve çalıştıracağınız saat [operatörünüzü eklediğinizde]([#become-an-operator](#operat%C3%B6r-olun)) belirtilebilir. ## Kısıtlamalar @@ -161,12 +161,12 @@ Normal oto-stake komut dosyasını çalıştıran ancak gönderilen son TX'leri npm run dryrun osmosis ``` -**REStake operatör bilgilerinizi [Operatörünüzü kaydetme](#submitting-your-operator) bölümünde gösterileceği gibi Validator Regisrty kaydınızı yapana kadar 'operatör olmadığınıza dair bir uyarı görebilirsiniz.** +**REStake operatör bilgilerinizi [Operatörünüzü kaydetme](#operat%C3%B6r%C3%BCn%C3%BCz%C3%BC-kaydetme) bölümünde gösterileceği gibi [Validator Kayıt Defteri](https://github.com/eco-stake/validator-registry)'ne kaydınızı yapana kadar 'operatör olmadığınıza dair bir uyarı görebilirsiniz.** ### REStake'i özelleştirin ve node'unuzu kullanın -Muhtemelen ağların yapılandırılmasını özelleştirmek isteyeceksiniz, örneğin auto compounding (ödüllerinizi otomatik olarak yeniden stake havuzuna aktarmak) komut dosyanızın başarılı bir şekilde tamamlanmasını sağlamak için node'unuzun URL'lerinizi ayarlamak gibi. +Muhtemelen ağların yapılandırılmasını özelleştirmek isteyeceksiniz, örneğin auto compounding (ödüllerinizi otomatik olarak yeniden stake etmek) komut dosyanızın başarılı bir şekilde tamamlanmasını sağlamak için node'unuzun URL'lerinizi ayarlamak gibi. Bir `src/networks.local.json` dosyası oluşturun ve geçersiz kılmak istediğiniz ağları belirtin. Aşağıdaki sadece bir örnektir, **ihtiyaç gerekirse sadece bir yapılandırmayı geçersiz kılmalısınız.** @@ -216,7 +216,7 @@ Komut dosyasını her gün aynı saatte çalıştıracak şekilde ayarlamalısı Her iki durumda da, sistem zamanınızın doğru olduğundan emin olun ve komut dosyasının UTC'de ne zaman çalışacağını biliyor olmalısınız, çünkü bu daha sonra gerekli olacak. Her iki örnek de saat 21:00'e göre verilmiştir. -[Sık sık güncellemeyi](#updating-your-local-version) unutmayın! +[Sık sık güncellemeyi](#yerel-versiyonunuzu-g%C3%BCncelleme) unutmayın! #### `crontab` Kullanma From 5a9e79483a8e3fba886ba1dd0c01e5651a63cbbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20K=C3=B6l=20Ti=C4=9Fin?= <102043225+koltigin@users.noreply.github.com> Date: Sat, 22 Oct 2022 20:55:42 +0300 Subject: [PATCH 714/862] Update README_TURKISH.md --- README_TURKISH.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_TURKISH.md b/README_TURKISH.md index fc8f4f9fc..f89aae083 100644 --- a/README_TURKISH.md +++ b/README_TURKISH.md @@ -16,7 +16,7 @@ Bu işlemler, stake eden adına stake alan tarafından gönderilir, yani valiato REStake, özellikle bir validatore `Delegate` işlemlerini yalnızca validatorleriniz yapmasına izin vermenizi sağlar. Validator başka herhangi bir işlem türü gönderemez ya da cüzdanınıza herhangi bir erişimi yoktur. Bunu normal olarak Keplr kullanarak yetkilendiriyorsunuz. REStake artık oto-stake için bir `çekme` izni gerektirmiyor. -Doğrulayıcının delegelerini otomatik olarak aramasını sağlayan bir komut dosyası da sağlanmıştır, gerekli ödemeler için her birini kontrol edin ve varsa, talep ve işlemleri tek bir işlemde onlar adına gerçekleştirin. Bu script günlük olarak çalıştırılmalıdır ve çalıştıracağınız saat [operatörünüzü eklediğinizde]([#become-an-operator](#operat%C3%B6r-olun)) belirtilebilir. +Doğrulayıcının delegelerini otomatik olarak aramasını sağlayan bir komut dosyası da sağlanmıştır, gerekli ödemeler için her birini kontrol edin ve varsa, talep ve işlemleri tek bir işlemde onlar adına gerçekleştirin. Bu script günlük olarak çalıştırılmalıdır ve çalıştıracağınız saat [operatörünüzü eklediğinizde](#operat%C3%B6r-olun) belirtilebilir. ## Kısıtlamalar From 8c9ef3184b3ae821493a60b36dcb583307080a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20K=C3=B6l=20Ti=C4=9Fin?= <102043225+koltigin@users.noreply.github.com> Date: Sat, 22 Oct 2022 21:00:01 +0300 Subject: [PATCH 715/862] Update README_TURKISH.md --- README_TURKISH.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_TURKISH.md b/README_TURKISH.md index f89aae083..b63e9b5d4 100644 --- a/README_TURKISH.md +++ b/README_TURKISH.md @@ -45,7 +45,7 @@ Birden çok Cosmos zinciri için yalnızca tek bir anımsatıcıya ihtiyacınız 118 yolunu kullanan mevcut operatörler olduğundan, operatörlerin yükseltme yapmak istediklerinde doğru yolu seçmeleri gerekecektir. Yeni operatörler, stake almadan önce doğru yolu kullanmalıdır. -Doğru yol, bir [yapılandırma geçersiz kılma](#overriding-networks-config-locallyuse-your-own-node) dosyası kullanılarak iki yoldan biriyle ayarlanabilir. `"correctSlip44": true`, Zincir Kaydı'nda tanımlanan slip44'ü kullanır. Alternatif olarak `"slip44": 69` kullanarak belirli bir yol ayarlayın. Mümkünse `"correctSlip44": true` kullanmalısınız. +Doğru yol, bir [yapılandırma geçersiz kılma](https://github.com/AnatolianTeam/restake/edit/master/README_TURKISH.md#restakei-%C3%B6zelle%C5%9Ftirin-ve-nodeunuzu-kullan%C4%B1n) dosyası kullanılarak iki yoldan biriyle ayarlanabilir. `"correctSlip44": true`, Zincir Kaydı'nda tanımlanan slip44'ü kullanır. Alternatif olarak `"slip44": 69` kullanarak belirli bir yol ayarlayın. Mümkünse `"correctSlip44": true` kullanmalısınız. ```jsonc { From f429c5f72ab14a5383f88f077e72ea048ab9f3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20K=C3=B6l=20Ti=C4=9Fin?= <102043225+koltigin@users.noreply.github.com> Date: Sat, 22 Oct 2022 21:09:57 +0300 Subject: [PATCH 716/862] Update README.md The link given to the title Updating Your Local Version from the Derivation Paths (IMPORTANT) title was corrected. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 879312655..22a6bd306 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Right now, the REStake autostaking script uses the standard 118 derivation path As there are existing operators using the 118 path, operators will need to opt in to the correct path when they want to upgrade. **New operators should use the correct path before they get grants**. -The correct path can be set in one of two ways using a [config override](#overriding-networks-config-locallyuse-your-own-node) file. `"correctSlip44": true` will use the slip44 defined in the Chain Registry. Alternatively set a specific path using `"slip44": 69`. You should use `"correctSlip44": true` if possible. +The correct path can be set in one of two ways using a [config override](#customise-restake-and-use-your-own-node) file. `"correctSlip44": true` will use the slip44 defined in the Chain Registry. Alternatively set a specific path using `"slip44": 69`. You should use `"correctSlip44": true` if possible. ```jsonc { From 985be0c9087a58acedd047ad8705b26fec7a161a Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 23 Oct 2022 18:51:41 +0100 Subject: [PATCH 717/862] Refactor validators and improve mobile UI --- src/components/App.js | 4 +- src/components/ClaimRewards.js | 40 +++- src/components/Coins.js | 1 + src/components/CountdownRestake.js | 27 ++- src/components/DelegateForm.js | 170 ++++++++-------- src/components/Delegations.js | 126 ++++++++---- src/components/ManageRestake.js | 4 +- src/components/ValidatorDelegate.js | 301 +++++++++++++++++++++------- src/components/ValidatorGrants.js | 92 +++++---- src/components/ValidatorModal.js | 176 +++++----------- src/components/ValidatorNetworks.js | 2 +- src/components/Validators.js | 292 +++++++++------------------ src/utils/Helpers.mjs | 10 +- 13 files changed, 670 insertions(+), 575 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index d832aa096..c120e95a7 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -531,7 +531,7 @@ class App extends React.Component { render() { return ( - +
    @@ -641,7 +641,7 @@ class App extends React.Component { {this.otherFavouriteAddresses().length < 1 && this.state.wallet ? ( this.setState({ showAddressModal: true })} className="small d-none d-lg-inline ms-2">{this.state.wallet.name || this.state.wallet.address} ) : ( - this.setState({ address: e.target.value })} style={{maxWidth: 200}}> {this.state.wallet ? ( diff --git a/src/components/ClaimRewards.js b/src/components/ClaimRewards.js index bea779758..b465a051c 100644 --- a/src/components/ClaimRewards.js +++ b/src/components/ClaimRewards.js @@ -1,19 +1,22 @@ import { MsgWithdrawDelegatorReward, MsgWithdrawValidatorCommission } from "cosmjs-types/cosmos/distribution/v1beta1/tx"; import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx"; -import { buildExecableMessage, buildExecMessage, coin } from "../utils/Helpers.mjs"; +import { buildExecableMessage, buildExecMessage, coin, rewardAmount } from "../utils/Helpers.mjs"; import { - Dropdown + Dropdown, + Button } from 'react-bootstrap' import { add, subtract, multiply, divide, bignumber } from 'mathjs' function ClaimRewards(props) { - const { address, wallet, signingClient, validatorRewards } = props + const { network, address, wallet, signingClient, rewards } = props async function claim(){ + props.setError() props.setLoading(true) + const validatorRewards = mapRewards() const gasSimMessages = buildMessages(validatorRewards) let gas @@ -65,6 +68,21 @@ function ClaimRewards(props) { }) } + function mapRewards() { + if (!rewards) return []; + + const validatorRewards = rewards + .map(reward => { + return { + validatorAddress: reward.validator_address, + reward: rewardAmount(reward, network.denom), + } + }) + .filter(validatorReward => validatorReward.reward ); + + return validatorRewards; + } + // Expects a map of string -> string (validator -> reward) function buildMessages(validatorRewards){ return validatorRewards.map(validatorReward => { @@ -74,7 +92,7 @@ function ClaimRewards(props) { valMessages.push(buildExecableMessage(MsgDelegate, "/cosmos.staking.v1beta1.MsgDelegate", { delegatorAddress: address, validatorAddress: validatorReward.validatorAddress, - amount: coin(validatorReward.reward, props.network.denom) + amount: coin(validatorReward.reward, network.denom) }, wallet?.address !== address)) }else{ valMessages.push(buildExecableMessage(MsgWithdrawDelegatorReward, "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", { @@ -106,6 +124,8 @@ function ClaimRewards(props) { } function buttonText() { + if(props.buttonText) return props.buttonText + if(props.restake){ return 'Manual Compound' }else if(props.commission){ @@ -117,9 +137,15 @@ function ClaimRewards(props) { return ( <> - claim()}> - {buttonText()} - + {props.button ? ( + + ) : ( + claim()}> + {buttonText()} + + )} ) } diff --git a/src/components/Coins.js b/src/components/Coins.js index 9509e482d..53171728d 100644 --- a/src/components/Coins.js +++ b/src/components/Coins.js @@ -23,6 +23,7 @@ function Coins(props) { function precision(coins){ if(fullPrecision) return decimals; + if(props.precision) return props.precision; if(coins.amount >= (1000 * Math.pow(10, decimals))) return 2 if(coins.amount >= (100 * Math.pow(10, decimals))) return 3 return 6 diff --git a/src/components/CountdownRestake.js b/src/components/CountdownRestake.js index 3ec00fe10..113915254 100644 --- a/src/components/CountdownRestake.js +++ b/src/components/CountdownRestake.js @@ -10,14 +10,13 @@ import { import Coins from './Coins'; function CountdownRestake(props) { - const { operator, network, maxAmount } = props + const { operator, network, maxAmount, icon } = props const nextRun = () => { if(!operator) return null return operator.nextRun() + (60 * 1000) } - const icon = maxAmount ? : const countdownRenderer = ({ hours, minutes, seconds, completed }) => { if (completed) { @@ -28,23 +27,37 @@ function CountdownRestake(props) { if(minutes > 0) string = string.concat(minutes + (hours > 0 ? 'm ' : ' minutes ')) if(props.showSeconds) string = string.concat(seconds + 's') if(string === '') return

    Validator is restaking now

    + if(props.renderText) return props.renderText(string) + return ( -

    Validator will REStake in
    {string}

    +
    in {string}
    ) } } return ( - -
    + icon ? ( + +
    + + {maxAmount && ( +

    Grant remaining:

    + )} +
    +
    + ): ( +
    {maxAmount && ( -

    Grant remaining:

    +
    Grant remaining:
    )}
    - + ) ) } diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js index 3cf3fdb09..db56af9d7 100644 --- a/src/components/DelegateForm.js +++ b/src/components/DelegateForm.js @@ -1,6 +1,4 @@ -import React from 'react' -import Coins from './Coins' -import { buildExecMessage, coin } from '../utils/Helpers.mjs' +import React, { useState, useReducer } from 'react'; import { MsgDelegate, MsgUndelegate, MsgBeginRedelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx"; import { @@ -11,77 +9,75 @@ import { import { pow, multiply, divide, subtract, bignumber } from 'mathjs' -class DelegateForm extends React.Component { - constructor(props) { - super(props); - this.state = {amount: '', memo: ''} +import Coins from './Coins' +import { buildExecMessage, coin } from '../utils/Helpers.mjs' - this.handleInputChange = this.handleInputChange.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - } +function DelegateForm(props) { + const { network, wallet, address, validator, selectedValidator, action } = props + const [state, setState] = useReducer( + (state, newState) => ({ ...state, ...newState }), + { amount: '', memo: '' } + ) - handleInputChange(event) { + function handleInputChange(event) { const target = event.target; const value = target.value; const name = target.name; - this.setState({ + setState({ [name]: value }); } - async handleSubmit(event) { + async function handleSubmit(event) { event.preventDefault(); - this.setState({loading: true, error: null}) + setState({ loading: true, error: null }) - const wallet = this.props.wallet - const amount = this.state.amount - const memo = this.state.memo - const client = this.props.signingClient + const amount = state.amount + const memo = state.memo + const client = props.signingClient - const decimals = pow(10, this.props.network.decimals) + const decimals = pow(10, network.decimals) const denomAmount = bignumber(multiply(amount, decimals)) - let messages = this.buildMessages(denomAmount) + let messages = buildMessages(denomAmount) let gas try { - gas = await client.simulate(wallet.address, messages) + gas = await client.simulate(wallet.address, messages) } catch (error) { - this.setState({ loading: false, error: error.message }) + setState({ loading: false, error: error.message }) return } client.signAndBroadcast(wallet.address, messages, gas, memo).then((result) => { console.log("Successfully broadcasted:", result); - this.setState({loading: false, error: null}) - this.props.onDelegate() + setState({ loading: false, error: null }) + props.onDelegate() }, (error) => { console.log('Failed to broadcast:', error) - this.setState({ loading: false, error: `Failed to broadcast: ${error.message}` }) + setState({ loading: false, error: `Failed to broadcast: ${error.message}` }) }) } - buildMessages(amount){ - const { wallet, address } = this.props - const validatorAddress = this.props.selectedValidator.operator_address + function buildMessages(amount) { let message, type, typeUrl, value - if(this.props.redelegate){ + if (action === 'redelegate') { type = MsgBeginRedelegate typeUrl = "/cosmos.staking.v1beta1.MsgBeginRedelegate" value = { delegatorAddress: address, - validatorSrcAddress: this.props.validator.operator_address, - validatorDstAddress: validatorAddress, - amount: coin(amount, this.props.network.denom) + validatorSrcAddress: validator.operator_address, + validatorDstAddress: selectedValidator.operator_address, + amount: coin(amount, network.denom) } - }else{ - type = this.props.undelegate ? MsgUndelegate : MsgDelegate - typeUrl = "/cosmos.staking.v1beta1.Msg" + (this.props.undelegate ? 'Undelegate' : 'Delegate') + } else { + type = action === 'undelegate' ? MsgUndelegate : MsgDelegate + typeUrl = "/cosmos.staking.v1beta1.Msg" + (action === 'undelegate' ? 'Undelegate' : 'Delegate') value = { delegatorAddress: address, - validatorAddress: validatorAddress, - amount: coin(amount, this.props.network.denom) + validatorAddress: validator.operator_address, + amount: coin(amount, network.denom) } } if (wallet?.address !== address) { @@ -98,87 +94,99 @@ class DelegateForm extends React.Component { return [message] } - hasPermission(){ - const permission = this.props.redelegate ? 'BeginRedelegate' : this.props.undelegate ? 'Undelegate' : 'Delegate' - return this.props.wallet?.hasPermission(this.props.address, permission) + function hasPermission() { + const permission = action === 'redelegate' ? 'BeginRedelegate' : action === 'undelegate' ? 'Undelegate' : 'Delegate' + return wallet?.hasPermission(address, permission) } - async setAvailableAmount(){ - if(!this.props.wallet) return + async function setAvailableAmount() { + if (!wallet) return - this.setState({error: undefined}) - const messages = this.buildMessages(multiply(this.props.availableBalance.amount, 0.95)) - const decimals = pow(10, this.props.network.decimals) - const balance = bignumber(this.props.availableBalance.amount) - if(this.props.redelegate || this.props.undelegate){ - return this.setState({amount: divide(balance, decimals)}) + setState({ error: undefined }) + const messages = buildMessages(multiply(availableBalance().amount, 0.95)) + const decimals = pow(10, network.decimals) + const balance = bignumber(availableBalance().amount) + if (['redelegate', 'undelegate'].includes(action)) { + return setState({ amount: divide(balance, decimals) }) } - this.props.signingClient.simulate(this.props.wallet.address, messages).then(gas => { - const gasPrice = this.props.signingClient.getFee(gas).amount[0].amount + props.signingClient.simulate(wallet.address, messages).then(gas => { + const gasPrice = props.signingClient.getFee(gas).amount[0].amount const saveTxFeeNum = 10 const amount = divide(subtract(balance, multiply(gasPrice, saveTxFeeNum)), decimals) - this.setState({amount: amount > 0 ? amount : 0}) + setState({ amount: amount > 0 ? amount : 0 }) }, error => { - this.setState({error: error.message}) + setState({ error: error.message }) }) } - actionText(){ - if(this.props.redelegate) return 'Redelegate' - if(this.props.undelegate) return 'Undelegate' + function availableBalance() { + if (['redelegate', 'undelegate'].includes(action)) { + return (props.delegation || {}).balance; + } else { + return props.balance; + } + } + + function actionText() { + if (action === 'redelegate') return 'Redelegate' + if (action === 'undelegate') return 'Undelegate' return 'Delegate' } - denom(){ - return this.props.network.symbol + function denom() { + return network.symbol } - step(){ - return 1 / pow(10, this.props.network.decimals) + function step() { + return 1 / pow(10, network.decimals) } - render() { - return ( - <> - {this.state.error && + return ( + <> + {state.error && - {this.state.error} + {state.error} - } -
    -
    + } + +
    Amount
    - - {this.denom()} + + {denom()}
    - {this.props.availableBalance && -
    this.setAvailableAmount()}> - Available: + {availableBalance() && +
    setAvailableAmount()}> + Available:
    }
    Memo - + -

    - {!this.state.loading - ? - : + )} + + + ) : } -

    +
    - - ) - } + + ) } export default DelegateForm diff --git a/src/components/Delegations.js b/src/components/Delegations.js index 7f2e4a01b..f07c79f4c 100644 --- a/src/components/Delegations.js +++ b/src/components/Delegations.js @@ -6,9 +6,15 @@ import ClaimRewards from "./ClaimRewards"; import ValidatorModal from "./ValidatorModal"; import AboutLedger from "./AboutLedger"; -import { Button, Dropdown, Spinner } from "react-bootstrap"; - -import { parseGrants } from "../utils/Helpers.mjs"; +import { + Button, + Dropdown, + Spinner, + Dropdown, + } from "react-bootstrap"; +import { Gear } from "react-bootstrap-icons"; + +import { parseGrants, rewardAmount } from "../utils/Helpers.mjs"; import Validators from "./Validators"; class Delegations extends React.Component { @@ -21,7 +27,6 @@ class Delegations extends React.Component { this.onClaimRewards = this.onClaimRewards.bind(this); this.onGrant = this.onGrant.bind(this); this.onRevoke = this.onRevoke.bind(this); - this.validatorRewards = this.validatorRewards.bind(this); this.showValidatorModal = this.showValidatorModal.bind(this); this.setValidatorLoading = this.setValidatorLoading.bind(this); this.hideValidatorModal = this.hideValidatorModal.bind(this); @@ -288,7 +293,7 @@ class Delegations extends React.Component { grantsValid: !!( grant.stakeGrant && (!grant.validators || grant.validators.includes(operator.address)) && - (grant.maxTokens === null || larger(grant.maxTokens, this.validatorReward(operator.address))) + (grant.maxTokens === null || larger(grant.maxTokens, rewardAmount(this.state.rewards, this.props.network.denom))) ), grantsExist: !!(grant.claimGrant || grant.stakeGrant), } @@ -321,31 +326,6 @@ class Delegations extends React.Component { }; } - validatorReward(validatorAddress) { - if (!this.state.rewards) return 0; - const denom = this.props.network.denom; - const validatorReward = this.state.rewards[validatorAddress]; - const reward = validatorReward && validatorReward.reward.find((el) => el.denom === denom) - return reward ? bignumber(reward.amount) : 0 - } - - validatorRewards(validators) { - if (!this.state.rewards) return []; - - const validatorRewards = Object.keys(this.state.rewards) - .map(validator => { - return { - validatorAddress: validator, - reward: this.validatorReward(validator), - } - }) - .filter(validatorReward => { - return validatorReward.reward && (validators === undefined || validators.includes(validatorReward.validatorAddress)) - }); - - return validatorRewards; - } - showValidatorModal(validator, opts) { opts = opts || {} this.setState({ validatorModal: { show: true, validator: validator, ...opts } }) @@ -367,8 +347,6 @@ class Delegations extends React.Component { theme={this.props.theme} validator={validatorModal.validator} activeTab={validatorModal.activeTab} - redelegate={validatorModal.redelegate} - undelegate={validatorModal.undelegate} network={this.props.network} networks={this.props.networks} address={this.props.address} @@ -388,6 +366,7 @@ class Delegations extends React.Component { onDelegate={this.onClaimRewards} onGrant={this.onGrant} onRevoke={this.onRevoke} + onClaimRewards={this.onClaimRewards} setError={this.setError} /> ) @@ -461,12 +440,87 @@ class Delegations extends React.Component { operatorGrants={this.operatorGrants()} authzSupport={this.authzSupport()} restakePossible={this.restakePossible()} - validatorRewards={this.validatorRewards} showValidator={this.showValidatorModal} setValidatorLoading={this.setValidatorLoading} setError={this.setError} onClaimRewards={this.onClaimRewards} - onRevoke={this.onRevoke} /> + onRevoke={this.onRevoke} + manageControl={({validator, operator, delegation, rewards, grants, filter}) => { + const { network, wallet, address } = this.props + const validatorAddress = validator.operator_address + const validatorOperator = validator.isValidatorOperator(address) + return ( + !this.state.validatorLoading[validatorAddress] ? ( + filter.group === 'delegated' && delegation ? ( + + + + + + this.showValidatorModal(validator, { activeTab: 'profile' })}> + View {validator.moniker} + +
    + this.setValidatorLoading(validatorAddress, loading)} + setError={this.setError} + /> + this.setValidatorLoading(validatorAddress, loading)} + setError={this.setError} + /> + {validatorOperator && ( + <> +
    + this.setValidatorLoading(validatorAddress, loading)} + setError={this.setError} + /> + + )} +
    +
    + ) : ( + + ) + ) : ( + + ) + ) + }} />
    @@ -494,7 +548,7 @@ class Delegations extends React.Component { network={this.props.network} address={this.props.address} wallet={this.props.wallet} - validatorRewards={this.validatorRewards()} + rewards={Object.values(this.state.rewards || {})} signingClient={this.props.signingClient} onClaimRewards={this.onClaimRewards} setLoading={this.setClaimLoading} @@ -505,7 +559,7 @@ class Delegations extends React.Component { network={this.props.network} address={this.props.address} wallet={this.props.wallet} - validatorRewards={this.validatorRewards()} + rewards={Object.values(this.state.rewards || {})} signingClient={this.props.signingClient} onClaimRewards={this.onClaimRewards} setLoading={this.setClaimLoading} diff --git a/src/components/ManageRestake.js b/src/components/ManageRestake.js index 379c46ee7..3a7da3d2e 100644 --- a/src/components/ManageRestake.js +++ b/src/components/ManageRestake.js @@ -5,7 +5,7 @@ import TooltipIcon from "./TooltipIcon"; import { Button, OverlayTrigger, Tooltip } from "react-bootstrap"; -import { CheckCircle, XCircle, WrenchAdjustable, Magic } from "react-bootstrap-icons"; +import { CheckCircle, XCircle, WrenchAdjustable, Magic, ClockHistory, Clock } from "react-bootstrap-icons"; function ManageRestake(props) { const { validator, operator, delegation, grants, network, authzSupport, restakePossible } = props @@ -20,6 +20,8 @@ function ManageRestake(props) { network={network} operator={operator} maxAmount={grants.maxTokens} + icon={grants.maxTokens ? : } + renderText={(string) =>

    Validator will REStake in
    {string}

    } /> ) : restakePossible && (delegation || grants.grantsExist) ? ( diff --git a/src/components/ValidatorDelegate.js b/src/components/ValidatorDelegate.js index 2ee5bae8b..2173c31b2 100644 --- a/src/components/ValidatorDelegate.js +++ b/src/components/ValidatorDelegate.js @@ -1,93 +1,242 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { round } from 'mathjs' +import { + Table, + Spinner, + Dropdown, + Button, +} from 'react-bootstrap' +import { ChevronLeft } from "react-bootstrap-icons"; + import Coins from './Coins'; import TooltipIcon from './TooltipIcon' import DelegateForm from './DelegateForm' -import ValidatorLink from './ValidatorLink' - -import { - Table, - Spinner -} from 'react-bootstrap' +import { rewardAmount } from '../utils/Helpers.mjs'; +import ClaimRewards from './ClaimRewards'; +import Validators from './Validators' +import AlertMessage from './AlertMessage' function ValidatorDelegate(props) { - const {redelegate, undelegate, network, validator, selectedValidator, onDelegate, availableBalance } = props + const { network, validator, balance, wallet, address } = props + const [action, setAction] = useState(props.action) + const [selectedValidator, setSelectedValidator] = useState(); + const [loading, setLoading] = useState(false) + const [error, setError] = useState() + + const delegation = props.delegations && props.delegations[validator.address] + const validatorRewards = props.rewards && props.rewards[validator.address] + const reward = rewardAmount(validatorRewards, network.denom) + const validatorCommission = props.commission && props.commission[validator.address] + const commission = rewardAmount(validatorCommission, network.denom, 'commission') + + useEffect(() => { + setAction(props.action) + }, [props.action]) + + useEffect(() => { + setError() + props.onChangeAction && props.onChangeAction(action) + }, [action]) - const actionText = () => { - if(redelegate) return 'Redelegate' - if(undelegate) return 'Undelegate' - return 'Delegate' + function actionText() { + if (action === 'redelegate') { + if (selectedValidator) { + return `Redelegate to ${selectedValidator.moniker}` + } + return 'Redelegate to...' + } + if (action === 'undelegate') return `Undelegate from ${validator.moniker}` + return `Delegate to ${validator.moniker}` + } + + function onDelegate(){ + setAction() + props.onDelegate() } return ( <> - - - {network.apyEnabled && ( - - - - - )} - - - - - - - - - {!!props.commission && ( - - - - + {error && + + {error} + + } + {action ? ( + <> +
    + {action && ( + { + action === 'redelegate' && selectedValidator ? setSelectedValidator() : setAction() + }} /> + )} +
    {actionText()}
    +
    + {action === 'redelegate' && !selectedValidator ? ( + + ) : ( + setAction()} + onDelegate={onDelegate} /> )} - -
    - APY} - identifier="delegations-apy" - > -
    -

    Based on commission, compounding frequency and estimated block times.

    -

    This is an estimate and best case scenario.

    -
    -
    -
    - {Object.keys(props.validatorApy).length > 0 - ? props.validatorApy[selectedValidator.operator_address] - ? {round(props.validatorApy[selectedValidator.operator_address] * 100, 2).toLocaleString()}% - : "-" - : ( - - Loading... - - )} -
    Current Delegation
    Current Rewards - -
    Current Commission - -
    -
    - {redelegate - ? Redelegate from - : actionText() - } -
    - + + ) : ( + <> + + + {network.apyEnabled && ( + + + + + )} + + + + + {delegation?.balance?.amount && ( + + + + + )} + {!!commission && ( + + + + + )} + +
    + APY} + identifier="delegations-apy" + > +
    +

    Based on commission, compounding frequency and estimated block times.

    +

    This is an estimate and best case scenario.

    +
    +
    +
    + {Object.keys(props.validatorApy).length > 0 + ? props.validatorApy[validator.operator_address] + ? {round(props.validatorApy[validator.operator_address] * 100, 2).toLocaleString()}% + : "-" + : ( + + Loading... + + )} +
    Current Delegation
    Current Rewards + +
    Current Commission + +
    +
    + {delegation?.balance?.amount && ( + !loading ? ( + + + Manage + + + + setLoading(loading) + } + setError={setError} + /> + + setLoading(loading) + } + setError={setError} + /> + {!!commission && ( + + setLoading(loading) + } + setError={setError} + /> + )} +
    + { + setSelectedValidator() + setAction('redelegate') + }}> + Redelegate + + setAction('undelegate')}> + Undelegate + +
    +
    + ) : ( + + ) + )} + +
    + + )} ) } diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js index 9cf987168..498c747ea 100644 --- a/src/components/ValidatorGrants.js +++ b/src/components/ValidatorGrants.js @@ -14,12 +14,13 @@ import { } from 'react-bootstrap' import Coins from './Coins'; -import { buildExecMessage, coin } from '../utils/Helpers.mjs'; +import { buildExecMessage, coin, rewardAmount } from '../utils/Helpers.mjs'; import RevokeGrant from './RevokeGrant'; import AlertMessage from './AlertMessage'; import TooltipIcon from './TooltipIcon' import OperatorLastRestake from './OperatorLastRestake'; import OperatorLastRestakeAlert from './OperatorLastRestakeAlert'; +import CountdownRestake from './CountdownRestake'; function ValidatorGrants(props) { const { grants, wallet, operator, address, network, lastExec } = props @@ -29,6 +30,8 @@ function ValidatorGrants(props) { const [error, setError] = useState() const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry.format('YYYY-MM-DD') }); + const reward = rewardAmount(props.rewards, network.denom) + useEffect(() => { setState({ ...state, @@ -63,7 +66,7 @@ function ValidatorGrants(props) { } function maxTokensValid() { - return !maxTokensDenom() || larger(maxTokensDenom(), props.rewards) + return !maxTokensDenom() || larger(maxTokensDenom(), reward) } function showLoading(isLoading) { @@ -187,26 +190,6 @@ function ValidatorGrants(props) { REStake Address {operator.botAddress} - - Frequency - - {operator.runTimesString()} - - - {network.authzSupport && ( - - Last REStake - - - - - )} - - Minimum Reward - - - - {network.apyEnabled && ( @@ -234,31 +217,66 @@ function ValidatorGrants(props) { )} - Current Rewards + Frequency - + {operator.runTimesString()} - {state.maxTokens && ( - - Grant Remaining - - - - + {network.authzSupport && ( + <> + + Next REStake + + + + + + Last REStake + + + + + )} - Grant status + Minimum Reward + + + + + + Current Rewards + + + + + + Grant Status {grantsValid - ? Active + ? Active : grantsExist - ? state.maxTokens && smaller(state.maxTokens, props.rewards) - ? Not enough grant remaining - : Invalid / total delegation reached - : Inactive} + ? state.maxTokens && smaller(state.maxTokens, reward) + ? Not enough grant remaining + : Invalid / total delegation reached + : Inactive} + {grantsExist && ( + + Grant Remaining + + {state.maxTokens ? ( + + ) : ( + 'Unlimited' + )} + + + )} {grantsExist && !props.restakePossible && ( diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index 3c2aa52a9..26c50d1ec 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -1,9 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useParams, useNavigate } from "react-router-dom"; -import { bignumber } from 'mathjs' import moment from 'moment' -import Validators from './Validators' import ValidatorImage from './ValidatorImage' import ValidatorLink from './ValidatorLink' import AboutLedger from './AboutLedger'; @@ -19,17 +17,17 @@ import ValidatorProfile from './ValidatorProfile'; import ValidatorGrants from './ValidatorGrants'; function ValidatorModal(props) { - const { redelegate, undelegate, validator, delegations, operators, network, validators, grants } = props + const { validator, delegations, operators, network, validators, grants } = props const navigate = useNavigate() const params = useParams(); - const [selectedValidator, setSelectedValidator] = useState(!redelegate && validator); - const [activeTab, setActiveTab] = useState(params.section); + const [activeTab, setActiveTab] = useState(params.section || 'profile'); + const [activeAction, setActiveAction] = useState() const [registryData, setRegistryData] = useState({}) const [lastExec, setLastExec] = useState() let operator - if (operators && selectedValidator){ - operator = network.getOperator(selectedValidator.operator_address); + if (operators && validator){ + operator = network.getOperator(validator.operator_address); } useEffect(() => { @@ -38,7 +36,7 @@ function ValidatorModal(props) { getRegistryData() }, 15_000); return () => clearInterval(interval); - }, [selectedValidator]); + }, [validator]); useEffect(() => { getLastExec() @@ -51,47 +49,34 @@ function ValidatorModal(props) { useEffect(() => { if(params.network !== network.name) return - const shouldShow = props.show && selectedValidator && validator?.operator_address === selectedValidator.operator_address - const shouldChangeValidator = params.validator !== selectedValidator?.operator_address + const shouldShow = props.show && validator + const shouldChangeValidator = params.validator !== validator?.operator_address const shouldChangeTab = activeTab === 'profile' ? !!params.section : params.section !== activeTab const shouldChangeUrl = shouldShow && (shouldChangeValidator || shouldChangeTab) if (shouldChangeUrl) { - navigate(`/${network.name}/${selectedValidator.operator_address}${activeTab === 'profile' ? '' : `/${activeTab}`}`) + navigate(`/${network.name}/${validator.operator_address}${activeTab === 'profile' ? '' : `/${activeTab}`}`) } else if (params.validator && props.show === false) { navigate(`/${network.name}`) } - }, [props.show, params.validator, params.section, activeTab, selectedValidator]) + }, [props.show, params.validator, params.section, activeTab, validator]) useEffect(() => { if (props.activeTab && props.activeTab != activeTab) { - setActiveTab(props.activeTab) + setTab(props.activeTab) } else if (params.section && params.section != activeTab) { - setActiveTab(params.section) - } else if (redelegate || undelegate) { - setActiveTab('delegate') + setTab(params.section) } else if (!activeTab) { - setActiveTab('profile') - } - }, [props.activeTab, redelegate, undelegate, selectedValidator]) - - useEffect(() => { - if (props.show) { - setSelectedValidator(!redelegate && validator) - } - }, [validator, redelegate, props.show]) - - function handleClose() { - if (selectedValidator && (!validator || redelegate) && selectedValidator !== validator) { - setSelectedValidator(null); - } else { - props.hideModal(); - setSelectedValidator(null); + setTab('profile') } + }, [props.activeTab, validator]) + + function setTab(tab){ + setActiveAction() + setActiveTab(tab) } - function selectValidator(selectedValidator, opts) { - setSelectedValidator(selectedValidator); - setActiveTab(opts.activeTab || 'profile'); + function handleClose() { + props.hideModal(); } function onDelegate() { @@ -100,8 +85,8 @@ function ValidatorModal(props) { } function getRegistryData(){ - if(selectedValidator?.path && network.directory){ - network.directory.getRegistryValidator(selectedValidator.path).then(data => { + if(validator?.path && network.directory){ + network.directory.getRegistryValidator(validator.path).then(data => { setRegistryData(data) }) }else{ @@ -132,89 +117,21 @@ function ValidatorModal(props) { }) } - function excludeValidators() { - if (redelegate) { - return [validator.operator_address]; - } else if (delegations) { - return [...Object.keys(delegations)]; - } - } - - function availableBalance() { - if (redelegate || undelegate) { - return (props.delegations[validator.address] || {}).balance; - } else { - return props.balance; - } - } - - function rewards() { - if (!props.rewards) - return 0; - const denom = network.denom; - const validatorReward = props.rewards[selectedValidator.address]; - const reward = validatorReward && validatorReward.reward.find((el) => el.denom === denom); - return reward ? bignumber(reward.amount) : 0; - } - - function commission() { - if (!props.commission) - return 0; - const denom = network.denom; - const validatorCommission = props.commission[selectedValidator.address]; - const commission = validatorCommission && validatorCommission.commission.find((el) => el.denom === denom); - return commission ? bignumber(commission.amount) : 0; - } - - function actionText() { - if (redelegate) - return Redelegate from ; - if (undelegate) - return 'Undelegate'; - if (validator) { - return 'Delegate'; - } else { - return 'Add Validator'; - } - } - return ( <> - + - {selectedValidator - ? ( - <> - - - - ) : actionText() - } + <> + + + - {!selectedValidator && - } - {selectedValidator && ( - setActiveTab(k)} id="validator-tabs"> -
    +
    ) } diff --git a/src/components/ValidatorServices.js b/src/components/ValidatorServices.js index f07fd6288..eeba42f07 100644 --- a/src/components/ValidatorServices.js +++ b/src/components/ValidatorServices.js @@ -1,7 +1,11 @@ +import { Link } from 'react-router-dom' import { OverlayTrigger, Tooltip } from 'react-bootstrap' +import { + HeartPulse +} from 'react-bootstrap-icons' import StakingRewardsIcon from '../assets/staking-rewards.svg' import StakingRewardsWhiteIcon from '../assets/staking-rewards-white.svg' @@ -16,6 +20,20 @@ function ValidatorServices(props) { const services = [] + if(Object.entries(validator.public_nodes || {}).length){ + services.push({ + key: 'nodes', + tooltip: 'Provides public nodes used by REStake', + render: () => { + return ( + + + + ) + } + }) + } + if(validator.services?.staking_rewards){ let verified = validator.services.staking_rewards.verified let icon = verified ? StakingRewardsVerifiedIcon : StakingRewardsIcon @@ -58,7 +76,11 @@ function ValidatorServices(props) { }) } - const showServices = services.filter(el => !props.show || props.show.includes(el.key)) + const showServices = services.filter(el => { + if(props.show && !props.show.includes(el.key)) return false + if(props.exclude && props.exclude.includes(el.key)) return false + return true + }) if(!showServices.length) return null @@ -68,15 +90,16 @@ function ValidatorServices(props) { return ( {service.tooltip} } > - - - + {service.render ? service.render() : ( + + + + )} ) })} diff --git a/src/components/Validators.js b/src/components/Validators.js index 69c03809b..ac69e6972 100644 --- a/src/components/Validators.js +++ b/src/components/Validators.js @@ -156,18 +156,18 @@ function Validators(props) { return ( - {validator.rank || '-'} - + - - props.showValidator(validator, { activeTab: 'profile' })}> + +
    props.showValidator(validator, { activeTab: 'profile' })} style={{maxWidth: 200}}> - +
    #{validator.rank}
    +
    {!props.isLoading ? ( @@ -229,10 +229,10 @@ function Validators(props) { {format(validator.commission.commission_rates.rate * 100, 2)}% - {filter.group === 'delegated' && ( - <> - - {delegations ? ( + + {delegations ? ( + delegationBalance && ( +
    props.showValidator(validator, { activeTab: 'delegate' })}> - ) : address && ( - - Loading... - - )} - - {filter.group === 'delegated' && !props.modal && ( +
    + ) + ) : address && ( + + Loading... + + )} + + {filter.group === 'delegated' && ( + <> + {!props.modal && ( {props.rewards ? denomRewards && ( - - - +
    props.showValidator(validator, { activeTab: 'delegate' })}> + + + +
    ) : address && ( Loading... @@ -269,14 +275,16 @@ function Validators(props) { {!props.modal && showCommission && ( {denomCommission ? ( - - - +
    props.showValidator(validator, { activeTab: 'delegate' })}> + + + +
    ) : validatorOperator && ( Loading... @@ -285,13 +293,13 @@ function Validators(props) { )} {!props.modal && ( - +
    - +
    )} - +
    {props.buttonText ? (
    - - - )} - - - - ); -} - -export default AddressModal diff --git a/src/components/App.css b/src/components/App.css index 0c02ffcce..23dfe3ba5 100644 --- a/src/components/App.css +++ b/src/components/App.css @@ -61,10 +61,6 @@ code p{ display: none !important; } -.hover-show .hover-target{ - visibility: hidden; -} - @media (hover: hover) { .favourite-on:hover .off, .favourite-off:hover .on{ @@ -75,8 +71,4 @@ code p{ .favourite-off:hover .off { display: none !important; } - - .hover-show:hover .hover-target{ - visibility: visible; - } } \ No newline at end of file diff --git a/src/components/App.js b/src/components/App.js index 7ef1f4438..3f11b1cb6 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -51,7 +51,7 @@ import Governance from './Governance'; import Networks from './Networks'; import Grants from './Grants'; import Favourite from './Favourite'; -import AddressModal from './AddressModal'; +import WalletModal from './WalletModal'; import Wallet from '../utils/Wallet.mjs'; import SendModal from './SendModal'; import KeplrSignerProvider from '../utils/KeplrSignerProvider.mjs'; @@ -315,11 +315,15 @@ class App extends React.Component { async getBalance() { if (!this.state.address) return - this.props.queryClient.getBalance(this.state.address, this.props.network.denom) + this.props.queryClient.getBalance(this.state.address) .then( - (balance) => { + (balances) => { + const balance = balances?.find( + (element) => element.denom === this.props.network.denom + ) || { denom: this.props.network.denom, amount: 0 }; this.setState({ - balance: balance + balance, + balances }) } ) @@ -462,6 +466,20 @@ class App extends React.Component { } } + showWalletModal(opts) { + opts = opts || {} + this.setState((state) => { + return { walletModal: { ...state.validatorModal, show: true, ...opts } } + }) + } + + hideWalletModal(opts) { + opts = opts || {} + this.setState((state) => { + return { walletModal: { ...state.walletModal, show: false } } + }) + } + setCopied() { this.setState({ copied: true }) setTimeout(() => { @@ -614,14 +632,14 @@ class App extends React.Component {
    {this.viewingWallet() ? ( - - this.setState({ showAddressModal: true })}> + + this.showWalletModal({ activeTab: 'wallet' })}> ) : ( - - this.setState({ showAddressModal: true })}> + + this.showWalletModal({ activeTab: 'saved' })}> @@ -630,7 +648,7 @@ class App extends React.Component { )} {this.otherFavouriteAddresses().length < 1 && this.state.wallet ? ( - this.setState({ showAddressModal: true })} className="small d-none d-lg-inline ms-2">{this.state.wallet.name || truncateAddress(this.state.wallet.address)} + this.showWalletModal({ activeTab: 'wallet' })} className="small d-none d-lg-inline ms-2">{this.state.wallet.name || truncateAddress(this.state.wallet.address)} ) : ( )} - + this.setCopied()}> @@ -661,17 +679,19 @@ class App extends React.Component { )} {this.state.address && (
  16. - {this.state.balance ? ( - - ) : ( - - Loading... - - )} +
    this.showWalletModal({activeTab: this.state.wallet ? 'wallet' : 'saved'})}> + {this.state.balance ? ( + + ) : ( + + Loading... + + )} +
  17. )}
  18. @@ -687,15 +707,12 @@ class App extends React.Component { {this.state.address && (
    {this.addressName()} - - this.setCopied()}> - - + this.showWalletModal({activeTab: this.state.wallet ? 'wallet' : 'saved'})}> +
    @@ -715,11 +732,11 @@ class App extends React.Component { return this.connect(provider.key, true)} disabled={!provider.available()}>Connect {provider.label} }) )} - this.setState({ showAddressModal: true })}>Saved Addresses + this.showWalletModal({ activeTab: 'saved' })}>Saved Addresses {this.state.address && ( <> - Disconnect {this.state.signerProvider?.label} + {this.state.wallet ? `Disconnect ${this.state.signerProvider?.label}` : 'Close'} )} @@ -789,7 +806,7 @@ class App extends React.Component { operators={this.props.operators} validators={this.props.validators} favouriteAddresses={this.favouriteAddresses()} - showFavouriteAddresses={() => this.setState({ showAddressModal: true })} + showFavouriteAddresses={() => this.showWalletModal({ activeTab: 'saved' })} toggleFavouriteAddress={this.toggleFavouriteAddress} onGrant={this.onGrant} onRevoke={this.onRevoke} @@ -827,15 +844,22 @@ class App extends React.Component {

    this.setState({ showAbout: false })} /> - this.setState({ showAddressModal: false })} + this.hideWalletModal()} + activeTab={this.state.walletModal?.activeTab} network={this.props.network} networks={Object.values(this.props.networks)} address={this.state.address} wallet={this.state.wallet} + signerProvider={this.state.signerProvider} + balances={this.state.balances} favouriteAddresses={this.state.favouriteAddresses} updateFavouriteAddresses={this.updateFavouriteAddresses} - setAddress={(value) => this.setState({address: value, showAddressModal: false})} + toggleFavouriteAddress={this.toggleFavouriteAddress} + setAddress={(value) => { + this.setState({address: value}) + this.hideWalletModal() + }} /> { + setSelectedNetwork(network) + }, [network]) + + function setAddress(address) { + if (selectedNetwork.path === network.path) { + props.setAddress(address) + } + } + + function updateAddress(address, label) { + updateFavouriteAddresses({ + ...favouriteAddresses, [network.path]: networkAddresses.map(data => { + if (data.address === address) { + return { ...data, label } + } + return data + }) + }) + } + + function removeAddress(address) { + updateFavouriteAddresses({ ...favouriteAddresses, [network.path]: networkAddresses.filter(data => data.address !== address) }) + } + + function addAddress(event) { + event.preventDefault() + if (!validNewAddress()) return + + updateFavouriteAddresses({ ...favouriteAddresses, [network.path]: [...networkAddresses, { address: newAddress, label: newLabel }] }) + setNewAddress('') + setNewLabel('') + } + + function validNewAddress() { + return newAddress !== '' && !networkAddresses.some(el => el.address === newAddress) + } + + return ( + <> +

    Save addresses here to easily find and view them, and carry out any granted actions in the REStake UI on their behalf.

    +

    Select 'View address' for any saved address to enter View mode and see REStake as they would - useful for monitoring other addresses quickly!

    +

    If a saved address has granted you any permissions, REStake will detect them and enable the relevant features. This can make it very easy to manage staking and governance for multiple wallets.

    + + {selectedNetwork && ( + <> +
    + + + + + + + + + + {networkAddresses.map(({ label, address }) => { + return ( + + + + + + ) + })} + + + + + + +
    AddressLabel
    +
    props.address !== address && setAddress(address)} as={props.address === address && 'strong'} /> +
    updateAddress(address, e.target.value)} /> +
    + {props.wallet?.address === address && ( + } + identifier={`${address}-wallet`} + tooltip="This is your connected wallet" + /> + )} + {props.address === address && ( + } + identifier={`${address}-view`} + tooltip="This is the address you are viewing" + /> + )} + + + + + + setAddress(address)}> + {props.address === address ? 'Viewing address' : 'View address'} + + removeAddress(address)}> + Remove saved address + + + +
    +
    setNewAddress(e.target.value)} /> setNewLabel(e.target.value)} /> +
    + +
    +
    +
    + + )} + + ); +} + +export default SavedAddresses diff --git a/src/components/ValidatorGrants.js b/src/components/ValidatorGrants.js deleted file mode 100644 index 768e76747..000000000 --- a/src/components/ValidatorGrants.js +++ /dev/null @@ -1,347 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import moment from 'moment' -import { pow, multiply, divide, larger, smaller, bignumber, round } from 'mathjs' - -import { MsgGrant } from "cosmjs-types/cosmos/authz/v1beta1/tx"; -import { StakeAuthorization } from "cosmjs-types/cosmos/staking/v1beta1/authz"; -import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; - -import { - Spinner, - Button, - Form, - Table -} from 'react-bootstrap' - -import Coins from './Coins'; -import { buildExecMessage, coin, rewardAmount } from '../utils/Helpers.mjs'; -import RevokeGrant from './RevokeGrant'; -import AlertMessage from './AlertMessage'; -import TooltipIcon from './TooltipIcon' -import OperatorLastRestake from './OperatorLastRestake'; -import OperatorLastRestakeAlert from './OperatorLastRestakeAlert'; -import CountdownRestake from './CountdownRestake'; -import Address from './Address'; - -function ValidatorGrants(props) { - const { grants, wallet, operator, address, network, lastExec } = props - const { stakeGrant, maxTokens, validators, grantsValid, grantsExist } = grants || {} - const defaultExpiry = moment().add(1, 'year') - const [loading, setLoading] = useState(false); - const [error, setError] = useState() - const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry.format('YYYY-MM-DD') }); - - const reward = rewardAmount(props.rewards, network.denom) - - useEffect(() => { - setState({ - ...state, - validators: validators || (!stakeGrant && [operator.address]), - maxTokens, - expiryDate: expiryDate(), - }) - }, [grants, operator]) - - useEffect(() => { - setState({ - ...state, - expiryDateValue: (expiryDate() || defaultExpiry).format('YYYY-MM-DD'), - maxTokensValue: maxTokens && state.maxTokensValue === '' ? divide(bignumber(maxTokens), pow(10, network.decimals)) : maxTokens ? state.maxTokensValue : '', - }) - }, [operator]) - - function handleInputChange(e) { - setState({ ...state, [e.target.name]: e.target.value }); - } - - function expiryDate() { - const stakeExpiry = stakeGrant && stakeGrant.expiration && moment(stakeGrant.expiration) - return stakeExpiry - } - - function maxTokensDenom() { - if (state.maxTokensValue === '') return - - const decimals = pow(10, network.decimals) - return bignumber(multiply(state.maxTokensValue, decimals)) - } - - function maxTokensValid() { - return !maxTokensDenom() || larger(maxTokensDenom(), reward) - } - - function showLoading(isLoading) { - setLoading(isLoading) - props.setLoading && props.setLoading(isLoading) - } - - function handleSubmit(event) { - event.preventDefault() - showLoading(true) - const expiry = moment(state.expiryDateValue) - let maxTokens - if (state.maxTokensValue !== '') { - maxTokens = coin(maxTokensDenom(), network.denom) - } - - const messages = [ - buildGrantMsg("/cosmos.staking.v1beta1.StakeAuthorization", - StakeAuthorization.encode(StakeAuthorization.fromPartial({ - allowList: { address: [operator.address] }, - maxTokens: maxTokens, - authorizationType: 1 - })).finish(), - expiry - ) - ] - console.log(messages) - - props.signingClient.signAndBroadcast(wallet.address, messages).then((result) => { - console.log("Successfully broadcasted:", result); - showLoading(false) - props.onGrant(operator.botAddress, { - grantee: operator.botAddress, - granter: address, - expiration: expiry, - authorization: { - '@type': "/cosmos.staking.v1beta1.StakeAuthorization", - max_tokens: maxTokens, - allow_list: { address: [operator.address] } - } - }); - }, (error) => { - console.log('Failed to broadcast:', error) - showLoading(false) - setError('Failed to broadcast: ' + error.message) - }) - } - - function buildGrantMsg(type, authValue, expiryDate) { - const value = { - granter: address, - grantee: operator.botAddress, - grant: { - authorization: { - typeUrl: type, - value: authValue - }, - expiration: Timestamp.fromPartial({ - seconds: expiryDate.unix(), - nanos: 0 - }) - } - } - if(wallet?.address !== address){ - return buildExecMessage(wallet.address, [{ - typeUrl: "/cosmos.authz.v1beta1.MsgGrant", - value: MsgGrant.encode(MsgGrant.fromPartial(value)).finish() - }]) - }else{ - return { - typeUrl: "/cosmos.authz.v1beta1.MsgGrant", - value: value - } - } - } - - function grantInformation() { - return ( - <> -

    {operator.moniker} will be able to carry out the following transactions on your behalf.

    -

    Delegate - allowed to delegate {maxTokensDenom() ? : 'any amount'} to {!state.validators ? 'any validator' : !state.validators.length || (state.validators.length === 1 && state.validators.includes(operator.address)) ? 'only their own validator' : 'validators ' + state.validators.join(', ')}.

    -

    This grant will expire automatically on {state.expiryDateValue}.

    -

    REStake only re-delegates {operator.moniker}'s accrued rewards and tries not to touch your balance.

    -

    REStake previously required a Withdraw grant but this is no longer necessary.

    - - ) - } - - const minimumReward = () => { - return { - amount: operator.minimumReward, - denom: network.denom - } - } - - const step = () => { - return 1 / pow(10, network.decimals) - } - - return ( - <> - {!props.authzSupport && ( - - {props.network.prettyName} doesn't support Authz just yet. - - )} - {props.restakePossible && !props.delegation && ( - - You must delegate to {operator.moniker} before they can REStake for you. - - )} - - {error && - - {error} - - } - - - - - - - {network.apyEnabled && ( - - - - - )} - - - - - {network.authzSupport && ( - <> - - - - - - - - - - )} - - - - - - - - - - - - - {grantsExist && ( - - - - - )} - -
    REStake Address
    - APY} - identifier="delegations-apy" - > -
    -

    Based on commission, compounding frequency and estimated block times.

    -

    This is an estimate and best case scenario.

    -
    -
    -
    - {Object.keys(props.validatorApy).length > 0 - ? props.validatorApy[operator.address] - ? {round(props.validatorApy[operator.address] * 100, 2).toLocaleString()}% - : "-" - : ( - - Loading... - - )} -
    Frequency - {operator.runTimesString()} -
    Next REStake - -
    Last REStake - -
    Minimum Reward - -
    Current Rewards - -
    Grant Status - {grantsValid - ? Active - : grantsExist - ? state.maxTokens && smaller(state.maxTokens, reward) - ? Not enough grant remaining - : Invalid / total delegation reached - : Inactive} -
    Grant Remaining - {state.maxTokens ? ( - - ) : ( - 'Unlimited' - )} -
    - {grantsExist && !props.restakePossible && ( - <>{grantInformation()} - )} - {props.restakePossible && ( -
    -
    -
    -
    - - Max amount -
    -
    - - {network.symbol} -
    -
    - Reduces with every delegation made by the validator
    Leave empty for unlimited -
    -
    -
    - - Expiry date - -
    Date the grant will expire. After this date you will need to re-grant
    -
    -

    - {!loading - ? ( - <> - {grants.grantsExist && ( - showLoading(loading)} - setError={setError} - /> - )} - - - ) - : - } -

    -
    -
    -
    -
    -

    Grant details

    - {grantInformation()} -
    -
    - )} - - ) -} - -export default ValidatorGrants; diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js index 3e330f7c1..7a2b5178f 100644 --- a/src/components/ValidatorProfile.js +++ b/src/components/ValidatorProfile.js @@ -50,7 +50,7 @@ function ValidatorProfile(props) { return ( <>
    -
    +
    @@ -139,7 +139,7 @@ function ValidatorProfile(props) {
    -
    +
    diff --git a/src/components/ValidatorStake.js b/src/components/ValidatorStake.js index 9c0fdab28..416bf3246 100644 --- a/src/components/ValidatorStake.js +++ b/src/components/ValidatorStake.js @@ -161,7 +161,7 @@ function ValidatorStake(props) { ) : ( <>
    -
    +
    @@ -231,7 +231,7 @@ function ValidatorStake(props) {
    -
    +
    @@ -320,7 +320,7 @@ function ValidatorStake(props) {
    - {delegation?.balance?.amount && ( + {delegation?.balance?.amount && wallet && ( !loading ? ( { + const balances = _.sortBy((props.balances || []).filter(el => el.denom === network.denom).map(balance => { + const asset = network.assets.find(a => a.base?.denom === balance.denom) + return { + ...balance, + asset + } + }), ({ asset }) => { + if(!asset) return 0 + if(network.denom === asset.base?.denom) return -2 + return -1 + }) + + useEffect(() => { + if (props.activeTab && props.activeTab != activeTab) { + setActiveTab(props.activeTab) + }else if (!props.show){ + setActiveTab() + } + }, [props.show]) + + function onHide() { + props.onHide() + } + + return ( + <> + onHide()}> + + Wallet + + + setActiveTab(k)} id="wallet-tabs"> + + + + {wallet && ( + <> +
    + + + + + + {wallet.name && ( + + + + + )} + + + + + + + + + +
    Wallet Provider +
    + {props.signerProvider?.label || 'None'} +
    +
    Wallet Name +
    + {wallet.name} +
    +
    Address +
    +
    + +
    +
    Balance + {balances.map(balance => { + return ( +
    + +
    + ) + })} +
    +
    + + +
    + + )} + + + + + + + + + + ); +} + +export default WalletModal diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index 5ea5da4f9..55d6f631c 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -73,6 +73,8 @@ const QueryClient = async (chainId, restUrls, opts) => { .get(restUrl + "/cosmos/bank/v1beta1/balances/" + address, opts) .then((res) => res.data) .then((result) => { + if(!denom) return result.balances + const balance = result.balances?.find( (element) => element.denom === denom ) || { denom: denom, amount: 0 }; From ae4e9e8089803ab674e4a1b4aa988612ec19445f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 25 Oct 2022 02:29:43 +0100 Subject: [PATCH 725/862] Improve grant lookup when changing wallet --- src/components/App.js | 55 +++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index 3f11b1cb6..cbdee06f1 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -126,11 +126,6 @@ class App extends React.Component { this.refreshInterval(); }) } - if(this.state.grants?.grantee !== prevState.grants?.grantee){ - if(this.state.wallet && this.state.wallet.address === this.state.address){ - this.state.wallet.grants = this.state.grants?.grantee || [] - } - } } componentWillUnmount() { @@ -336,28 +331,9 @@ class App extends React.Component { try { granterGrants = await this.props.queryClient.getGranterGrants(address) - if (address !== this.state.address) return - this.setState((state) => { - return { - grantQuerySupport: true, - grants: { - ...state.grants, - granter: granterGrants, - } - } - }) + this.setGrants(address, granterGrants, 'granter', true) granteeGrants = await this.props.queryClient.getGranteeGrants(address) - if (address !== this.state.address) return - this.setState((state) => { - if (address !== state.address) return {} - return { - grants: { - ...state.grants, - grantee: granteeGrants - } - } - }) - return + return this.setGrants(address, granteeGrants, 'grantee') } catch (error) { console.log('Failed to get all grants in batch', error.message) grantQuerySupport = error.response?.status !== 501 @@ -374,17 +350,11 @@ class App extends React.Component { granterGrants = await this.getGrantsIndividually(addresses.map(el => { return { grantee: el, granter: address } })) - if (address !== this.state.address) return - this.setState((state) => { - return { grantQuerySupport, grants: { ...state.grants, granter: granterGrants } } - }) + this.setGrants(address, granterGrants, 'granter', grantQuerySupport) granteeGrants = await this.getGrantsIndividually(favourites.map(el => { return { granter: el.address, grantee: address } })) - if (address !== this.state.address) return - this.setState((state) => { - return { grantQuerySupport, grants: { ...state.grants, grantee: granteeGrants } } - }) + this.setGrants(address, granteeGrants, 'grantee', grantQuerySupport) } async getGrantsIndividually(grants){ @@ -417,6 +387,23 @@ class App extends React.Component { return allGrants } + setGrants(address, grants, type, grantQuerySupport){ + if(type === 'grantee' && this.state.wallet?.address === address){ + this.state.wallet.grants = grants || [] + } + if (address !== this.state.address) return + this.setState((state) => { + if (address !== state.address) return {} + return { + grants: { + ...state.grants, + [type]: grants, + grantQuerySupport: grantQuerySupport ?? state.grantQuerySupport + } + } + }) + } + onSend(recipient, amount){ this.setState({showSendModal: false}) setTimeout(() => { From fda7940bdf5c045b593ebb513b672cd4121777af Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 26 Oct 2022 00:08:24 +0100 Subject: [PATCH 726/862] Move ledger support check --- src/components/App.js | 29 +++++++++++++++++------------ src/utils/SignerProvider.mjs | 3 --- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/components/App.js b/src/components/App.js index cbdee06f1..06b64c6f2 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -201,6 +201,9 @@ class App extends React.Component { let key try { key = await signerProvider.connect(network); + if (!network.ledgerSupport && (key.isNanoLedger || key.isHardware)) { + throw new Error('Ledger support is coming soon') + } } catch (e) { return this.setState({ error: `Failed to connect to ${signerProvider?.label || 'signer'}: ${e.message}`, @@ -398,8 +401,8 @@ class App extends React.Component { grants: { ...state.grants, [type]: grants, - grantQuerySupport: grantQuerySupport ?? state.grantQuerySupport - } + }, + grantQuerySupport: grantQuerySupport ?? state.grantQuerySupport } }) } @@ -652,16 +655,18 @@ class App extends React.Component { )} - - - - this.setCopied()}> - {this.state.copied ? : } - - - - + {this.state.address && ( + + + + this.setCopied()}> + {this.state.copied ? : } + + + + + )}
  19. )} {this.state.address && ( diff --git a/src/utils/SignerProvider.mjs b/src/utils/SignerProvider.mjs index 7609d712a..9a7e109cb 100644 --- a/src/utils/SignerProvider.mjs +++ b/src/utils/SignerProvider.mjs @@ -41,9 +41,6 @@ export default class SignerProvider { async getKey(network){ const { chainId } = network const key = await this.provider.getKey(chainId) - if(!network.ledgerSupport && (key.isNanoLedger || key.isHardware)){ - throw new Error('Ledger support is coming soon') - } return key } From b26ca37fadf4ad42d47fb641faad496d174fb0b8 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 26 Oct 2022 00:08:53 +0100 Subject: [PATCH 727/862] Fix coin separators --- src/components/Coins.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/Coins.js b/src/components/Coins.js index 59d7299b2..36166bef8 100644 --- a/src/components/Coins.js +++ b/src/components/Coins.js @@ -12,13 +12,19 @@ function Coins(props) { if(inBaseDenom) return coins.amount const prec = precision(coins) - return format(round(divide(bignumber(coins.amount), Math.pow(10, decimals)), prec), {notation: 'fixed'}).toLocaleString(undefined, { maximumFractionDigits: prec }) + return separator(format(round(divide(bignumber(coins.amount), Math.pow(10, decimals)), prec), {notation: 'fixed'})) } function value(coins){ return (coins.amount / Math.pow(10, decimals) * coingecko.usd).toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 }) } + function separator(stringNum) { + var str = stringNum.split("."); + str[0] = str[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); + return str.join("."); +} + if(!coins || !coins.denom){ return null } From 896bfa79ff82c21da076a9a218b1240d14aeae5d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 26 Oct 2022 00:10:10 +0100 Subject: [PATCH 728/862] UI and network fixes --- src/components/Address.js | 7 +- src/components/GrantStakeForm.js | 6 +- src/components/SavedAddresses.js | 35 +++++---- src/components/ValidatorLink.js | 4 +- src/components/ValidatorModal.js | 19 ++--- src/components/ValidatorName.js | 33 +------- src/components/ValidatorProfile.js | 4 +- src/components/ValidatorStake.js | 31 ++++++-- src/components/Validators.js | 122 +++++++++++++++++------------ src/components/WalletModal.js | 35 ++++----- 10 files changed, 147 insertions(+), 149 deletions(-) diff --git a/src/components/Address.js b/src/components/Address.js index 87d4d4893..fc0f7db41 100644 --- a/src/components/Address.js +++ b/src/components/Address.js @@ -22,10 +22,11 @@ function Address(props) { const Element = props.as ? props.as : 'span' const className = [`address-container d-flex align-items-center hover-show`, props.className].join(' ') + const addressClassName = [`address`, props.addressClassName].join(' ') return ( - - {truncateAddress(address)} +
    + {truncateAddress(address)} {props.children} - +
    ); } diff --git a/src/components/GrantStakeForm.js b/src/components/GrantStakeForm.js index 080db81e4..d7b4f07c5 100644 --- a/src/components/GrantStakeForm.js +++ b/src/components/GrantStakeForm.js @@ -164,7 +164,7 @@ function GrantStakeForm(props) { }
    -
    +
    @@ -181,7 +181,7 @@ function GrantStakeForm(props) { Expiry date - +
    Date the grant will expire. After this date you will need to re-grant
    @@ -217,7 +217,7 @@ function GrantStakeForm(props) {
    -
    +
    {grantInformation()}
    diff --git a/src/components/SavedAddresses.js b/src/components/SavedAddresses.js index 517baddbd..46a5eb1c7 100644 --- a/src/components/SavedAddresses.js +++ b/src/components/SavedAddresses.js @@ -1,7 +1,6 @@ import React, { useState, useEffect } from 'react'; import { - Modal, Table, Form, Button, @@ -18,7 +17,7 @@ import Address from './Address.js'; import TooltipIcon from './TooltipIcon.js'; function SavedAddresses(props) { - const { network, networks, wallet, favouriteAddresses, updateFavouriteAddresses } = props + const { network, networks, favouriteAddresses, updateFavouriteAddresses } = props const [newAddress, setNewAddress] = useState('') const [newLabel, setNewLabel] = useState('') const [selectedNetwork, setSelectedNetwork] = useState(network) @@ -89,25 +88,27 @@ function SavedAddresses(props) { return ( -
    props.address !== address && setAddress(address)} as={props.address === address && 'strong'} /> +
    props.address !== address && setAddress(address)} as={props.address === address && 'strong'} addressClassName="flex-fill" /> updateAddress(address, e.target.value)} />
    - {props.wallet?.address === address && ( - } - identifier={`${address}-wallet`} - tooltip="This is your connected wallet" - /> - )} - {props.address === address && ( - } - identifier={`${address}-view`} - tooltip="This is the address you are viewing" - /> - )} +
    + {props.wallet?.address === address && ( + } + identifier={`${address}-wallet`} + tooltip="This is your connected wallet" + /> + )} + {props.address === address && ( + } + identifier={`${address}-view`} + tooltip="This is the address you are viewing" + /> + )} +
    + return props.children || } return ( - {props.children || } + {props.children || } ); } diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js index afb3a85ae..71db8f81a 100644 --- a/src/components/ValidatorModal.js +++ b/src/components/ValidatorModal.js @@ -129,16 +129,16 @@ function ValidatorModal(props) { - <> - - - +
    + + +
    {validator && ( setTab(k)} id="validator-tabs"> - -
    -
    -
    - {this.props.network?.experimental && this.props.active !== 'networks' && ( - - This network was added to REStake automatically and has not been thoroughly tested yet. Raise an issue if you have any problems. - - )} - - {!this.state.providerError === 'keplr' && ( - this.setState({ providerError: false })}> - Please install the Keplr browser extension using desktop Google Chrome.
    WalletConnect and mobile support is coming soon. -
    - )} - {this.props.active === 'networks' && ( - - )} - {this.props.active === 'delegations' && - <> - - - } - {this.props.active === 'voting' && ( - - )} - {this.props.active === 'grants' && this.state.address && this.props.network.authzSupport && ( - this.showWalletModal({ activeTab: 'saved' })} - toggleFavouriteAddress={this.toggleFavouriteAddress} - onGrant={this.onGrant} - onRevoke={this.onRevoke} - queryClient={this.props.queryClient} - grantQuerySupport={this.state.grantQuerySupport} - signingClient={this.state.signingClient} /> - )} -
    - - this.setState({ showAbout: false })} /> - this.hideWalletModal()} - activeTab={this.state.walletModal?.activeTab} - network={this.props.network} - networks={Object.values(this.props.networks)} - address={this.state.address} - wallet={this.state.wallet} - signerProvider={this.state.signerProvider} - balances={this.state.balances} - favouriteAddresses={this.state.favouriteAddresses} - updateFavouriteAddresses={this.updateFavouriteAddresses} - toggleFavouriteAddress={this.toggleFavouriteAddress} - setAddress={(value) => { - this.setState({address: value}) - this.hideWalletModal() - }} - /> - this.setState({connectWallet: false})} - /> - {this.props.network && ( - this.setState({ showSendModal: false })} - onSend={this.onSend} - /> - )} - - ) - } -} - -export default App; diff --git a/src/components/App.test.js b/src/components/App.test.js deleted file mode 100644 index 1f03afeec..000000000 --- a/src/components/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/components/ClaimRewards.js b/src/components/ClaimRewards.js deleted file mode 100644 index b465a051c..000000000 --- a/src/components/ClaimRewards.js +++ /dev/null @@ -1,153 +0,0 @@ -import { MsgWithdrawDelegatorReward, MsgWithdrawValidatorCommission } from "cosmjs-types/cosmos/distribution/v1beta1/tx"; -import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx"; -import { buildExecableMessage, buildExecMessage, coin, rewardAmount } from "../utils/Helpers.mjs"; - -import { - Dropdown, - Button -} from 'react-bootstrap' - -import { add, subtract, multiply, divide, bignumber } from 'mathjs' - -function ClaimRewards(props) { - const { network, address, wallet, signingClient, rewards } = props - - async function claim(){ - props.setError() - props.setLoading(true) - - const validatorRewards = mapRewards() - const gasSimMessages = buildMessages(validatorRewards) - - let gas - try { - gas = await signingClient.simulate(wallet.address, gasSimMessages) - } catch (error) { - props.setLoading(false) - props.setError('Failed to broadcast: ' + error.message) - return - } - - const fee = signingClient.getFee(gas) - const feeAmount = fee.amount[0].amount - - const totalReward = validatorRewards.reduce((sum, validatorReward) => add(sum, bignumber(validatorReward.reward)), 0); - const adjustedValidatorRewards = validatorRewards.map(validatorReward => { - const shareOfFee = multiply(divide(bignumber(validatorReward.reward), totalReward), feeAmount); // To take a proportional amount from each validator relative to total reward - return { - validatorAddress: validatorReward.validatorAddress, - reward: subtract(validatorReward.reward, shareOfFee), - } - }) - - if(!props.commission && (adjustedValidatorRewards.length < 1 || adjustedValidatorRewards.some(validatorReward => validatorReward.reward <= 0))) { - props.setLoading(false) - props.setError('Reward is too low') - return - } - - let messages = buildMessages(adjustedValidatorRewards) - try { - gas = gas || await signingClient.simulate(wallet.address, messages) - } catch (error) { - props.setLoading(false) - props.setError('Failed to broadcast: ' + error.message) - return - } - console.log(messages, gas) - - const signAndBroadcast = signingClient.signAndBroadcastWithoutBalanceCheck - signAndBroadcast(wallet.address, messages, gas).then((result) => { - console.log("Successfully broadcasted:", result); - props.setLoading(false) - props.onClaimRewards(result) - }, (error) => { - console.log('Failed to broadcast:', error) - props.setLoading(false) - props.setError('Failed to broadcast: ' + error.message) - }) - } - - function mapRewards() { - if (!rewards) return []; - - const validatorRewards = rewards - .map(reward => { - return { - validatorAddress: reward.validator_address, - reward: rewardAmount(reward, network.denom), - } - }) - .filter(validatorReward => validatorReward.reward ); - - return validatorRewards; - } - - // Expects a map of string -> string (validator -> reward) - function buildMessages(validatorRewards){ - return validatorRewards.map(validatorReward => { - let valMessages = [] - - if(props.restake){ - valMessages.push(buildExecableMessage(MsgDelegate, "/cosmos.staking.v1beta1.MsgDelegate", { - delegatorAddress: address, - validatorAddress: validatorReward.validatorAddress, - amount: coin(validatorReward.reward, network.denom) - }, wallet?.address !== address)) - }else{ - valMessages.push(buildExecableMessage(MsgWithdrawDelegatorReward, "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward", { - delegatorAddress: address, - validatorAddress: validatorReward.validatorAddress - }, wallet?.address !== address)) - } - - if (props.commission) { - valMessages.push(buildExecableMessage(MsgWithdrawValidatorCommission, "/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission", { - validatorAddress: validatorReward.validatorAddress - }, wallet?.address !== address)) - } - - if (wallet?.address !== address) { - return [buildExecMessage(wallet.address, valMessages)] - }else{ - return valMessages - } - }).flat() - } - - function hasPermission(){ - const permissions = [] - if(props.restake) permissions.push('Delegate') - if(!props.restake) permissions.push('WithdrawDelegatorReward') - if(props.commission) permissions.push('WithdrawValidatorCommission') - return permissions.every(type => wallet?.hasPermission(address, type)) - } - - function buttonText() { - if(props.buttonText) return props.buttonText - - if(props.restake){ - return 'Manual Compound' - }else if(props.commission){ - return 'Claim Commission' - }else{ - return 'Claim Rewards' - } - } - - return ( - <> - {props.button ? ( - - ) : ( - claim()}> - {buttonText()} - - )} - - ) -} - -export default ClaimRewards; diff --git a/src/components/Coins.js b/src/components/Coins.js deleted file mode 100644 index 36166bef8..000000000 --- a/src/components/Coins.js +++ /dev/null @@ -1,58 +0,0 @@ -import _ from 'lodash' -import { divide, bignumber, round, format } from 'mathjs' - -function Coins(props) { - const { asset, coins, fullPrecision, inBaseDenom, hideValue, className } = props - let { decimals, symbol, prices } = asset || {} - const { coingecko } = prices || {} - decimals = decimals ?? 6 - symbol = symbol || coins?.denom?.toUpperCase() - - function amount(coins){ - if(inBaseDenom) return coins.amount - - const prec = precision(coins) - return separator(format(round(divide(bignumber(coins.amount), Math.pow(10, decimals)), prec), {notation: 'fixed'})) - } - - function value(coins){ - return (coins.amount / Math.pow(10, decimals) * coingecko.usd).toLocaleString(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 }) - } - - function separator(stringNum) { - var str = stringNum.split("."); - str[0] = str[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); - return str.join("."); -} - - if(!coins || !coins.denom){ - return null - } - - function precision(coins){ - if(fullPrecision) return decimals; - if(props.precision) return props.precision; - if(coins.amount >= (1000 * Math.pow(10, decimals))) return 2 - if(coins.amount >= (100 * Math.pow(10, decimals))) return 3 - return 6 - } - - return ( - - - {amount(coins)}  - {symbol} - - {!!coingecko?.usd && !hideValue && !!coins.amount && ( - <> -
    - - ${value(coins)} - - - )} -
    - ) -} - -export default Coins; diff --git a/src/components/ConnectWalletModal.js b/src/components/ConnectWalletModal.js deleted file mode 100644 index ba74657af..000000000 --- a/src/components/ConnectWalletModal.js +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useState, useMemo } from 'react'; - -import { - Modal, - Button, - Spinner -} from 'react-bootstrap' - -import { - isMobile, - isAndroid -} from "@walletconnect/browser-utils"; -import QRCode from "qrcode.react"; - -function ConnectWalletModal(props) { - const { show, signerProvider, uri, callback, onClose } = props - const [checkMobile] = useState(() => isMobile()); - const [checkAndroid] = useState(() => isAndroid()); - const [showQrCode, setShowQrCode] = useState(!checkMobile); - - const navigateToAppURL = useMemo(() => { - if (checkMobile) { - const uriStr = uri ? `?${uri}` : '' - if (checkAndroid) { - return `intent://wcV1${uriStr}#Intent;package=com.chainapsis.keplr;scheme=keplrwallet;end;` - } else { - return `keplrwallet://wcV1${uriStr}` - } - } - }, [checkAndroid, checkMobile, uri]); - - function handleClose() { - onClose(); - callback && callback(); - } - - function forceDisconnect() { - signerProvider?.forceDisconnect() - handleClose() - } - - const walletName = signerProvider?.label || 'Wallet' - const title = !checkMobile && uri ? 'Scan QR Code' : `Connect ${walletName}` - - return ( - <> - - <> - - - {title} - - - -
    -

    {`Open your ${walletName} app to continue...`}

    - {checkMobile && ( - <> - - {uri && ( -
    - -
    - )} - - )} -
    - {uri ? ( - showQrCode && ( - - ) - ) : ( - - Loading... - - )} -
    - {!uri && ( -

    - -

    - )} -
    -
    - -
    - - ); -} - -export default ConnectWalletModal diff --git a/src/components/CountdownRestake.js b/src/components/CountdownRestake.js deleted file mode 100644 index d4e7c1e66..000000000 --- a/src/components/CountdownRestake.js +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; - -import TooltipIcon from './TooltipIcon'; - -import Countdown from 'react-countdown'; - -import Coins from './Coins'; - -function CountdownRestake(props) { - const { operator, network, maxAmount, icon } = props - - const nextRun = () => { - if(!operator) return null - return operator.nextRun() - } - - - const countdownRenderer = ({ hours, minutes, seconds, completed }) => { - let string = nextRun()?.fromNow(false) - if (props.renderText) return props.renderText(string) - - return ( -
    {string}
    - ) - } - - if(!nextRun()) return null - - return ( - icon ? ( - -
    - - {maxAmount && ( -

    Grant remaining:

    - )} -
    -
    - ): ( -
    - - {maxAmount && ( -
    Grant remaining:
    - )} -
    - ) - ) -} - -export default CountdownRestake; - diff --git a/src/components/DelegateForm.js b/src/components/DelegateForm.js deleted file mode 100644 index db56af9d7..000000000 --- a/src/components/DelegateForm.js +++ /dev/null @@ -1,192 +0,0 @@ -import React, { useState, useReducer } from 'react'; -import { MsgDelegate, MsgUndelegate, MsgBeginRedelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx"; - -import { - Button, - Form, - Alert -} from 'react-bootstrap' - -import { pow, multiply, divide, subtract, bignumber } from 'mathjs' - -import Coins from './Coins' -import { buildExecMessage, coin } from '../utils/Helpers.mjs' - -function DelegateForm(props) { - const { network, wallet, address, validator, selectedValidator, action } = props - const [state, setState] = useReducer( - (state, newState) => ({ ...state, ...newState }), - { amount: '', memo: '' } - ) - - function handleInputChange(event) { - const target = event.target; - const value = target.value; - const name = target.name; - - setState({ - [name]: value - }); - } - - async function handleSubmit(event) { - event.preventDefault(); - - setState({ loading: true, error: null }) - - const amount = state.amount - const memo = state.memo - const client = props.signingClient - - const decimals = pow(10, network.decimals) - const denomAmount = bignumber(multiply(amount, decimals)) - - let messages = buildMessages(denomAmount) - let gas - try { - gas = await client.simulate(wallet.address, messages) - } catch (error) { - setState({ loading: false, error: error.message }) - return - } - - client.signAndBroadcast(wallet.address, messages, gas, memo).then((result) => { - console.log("Successfully broadcasted:", result); - setState({ loading: false, error: null }) - props.onDelegate() - }, (error) => { - console.log('Failed to broadcast:', error) - setState({ loading: false, error: `Failed to broadcast: ${error.message}` }) - }) - } - - function buildMessages(amount) { - let message, type, typeUrl, value - if (action === 'redelegate') { - type = MsgBeginRedelegate - typeUrl = "/cosmos.staking.v1beta1.MsgBeginRedelegate" - value = { - delegatorAddress: address, - validatorSrcAddress: validator.operator_address, - validatorDstAddress: selectedValidator.operator_address, - amount: coin(amount, network.denom) - } - } else { - type = action === 'undelegate' ? MsgUndelegate : MsgDelegate - typeUrl = "/cosmos.staking.v1beta1.Msg" + (action === 'undelegate' ? 'Undelegate' : 'Delegate') - value = { - delegatorAddress: address, - validatorAddress: validator.operator_address, - amount: coin(amount, network.denom) - } - } - if (wallet?.address !== address) { - message = buildExecMessage(wallet.address, [{ - typeUrl: typeUrl, - value: type.encode(type.fromPartial(value)).finish() - }]) - } else { - message = { - typeUrl: typeUrl, - value: value - } - } - return [message] - } - - function hasPermission() { - const permission = action === 'redelegate' ? 'BeginRedelegate' : action === 'undelegate' ? 'Undelegate' : 'Delegate' - return wallet?.hasPermission(address, permission) - } - - async function setAvailableAmount() { - if (!wallet) return - - setState({ error: undefined }) - const messages = buildMessages(multiply(availableBalance().amount, 0.95)) - const decimals = pow(10, network.decimals) - const balance = bignumber(availableBalance().amount) - if (['redelegate', 'undelegate'].includes(action)) { - return setState({ amount: divide(balance, decimals) }) - } - props.signingClient.simulate(wallet.address, messages).then(gas => { - const gasPrice = props.signingClient.getFee(gas).amount[0].amount - const saveTxFeeNum = 10 - const amount = divide(subtract(balance, multiply(gasPrice, saveTxFeeNum)), decimals) - - setState({ amount: amount > 0 ? amount : 0 }) - }, error => { - setState({ error: error.message }) - }) - } - - function availableBalance() { - if (['redelegate', 'undelegate'].includes(action)) { - return (props.delegation || {}).balance; - } else { - return props.balance; - } - } - - function actionText() { - if (action === 'redelegate') return 'Redelegate' - if (action === 'undelegate') return 'Undelegate' - return 'Delegate' - } - - function denom() { - return network.symbol - } - - function step() { - return 1 / pow(10, network.decimals) - } - - return ( - <> - {state.error && - - {state.error} - - } -
    -
    - - Amount -
    -
    - - {denom()} -
    - {availableBalance() && -
    setAvailableAmount()}> - Available: -
    - } -
    -
    - - Memo - - -
    - {!state.loading - ? ( - <> - {props.closeForm && ( - - )} - - - ) : - } -
    -
    -
    - - ) -} - -export default DelegateForm diff --git a/src/components/Delegations.js b/src/components/Delegations.js deleted file mode 100644 index 4dbd9269a..000000000 --- a/src/components/Delegations.js +++ /dev/null @@ -1,619 +0,0 @@ -import React from "react"; -import _ from "lodash"; -import { larger, bignumber } from 'mathjs' -import AlertMessage from "./AlertMessage"; -import ClaimRewards from "./ClaimRewards"; -import ValidatorModal from "./ValidatorModal"; -import AboutLedger from "./AboutLedger"; - -import { - Button, - Dropdown, - Spinner, - Dropdown, - } from "react-bootstrap"; -import { Gear } from "react-bootstrap-icons"; - -import { parseGrants, rewardAmount } from "../utils/Helpers.mjs"; -import Validators from "./Validators"; - -class Delegations extends React.Component { - constructor(props) { - super(props); - this.state = { operatorGrants: {}, validatorLoading: {}, validatorApy: {}, validatorModal: {}, commission: {} }; - - this.setError = this.setError.bind(this); - this.setClaimLoading = this.setClaimLoading.bind(this); - this.onClaimRewards = this.onClaimRewards.bind(this); - this.onGrant = this.onGrant.bind(this); - this.onRevoke = this.onRevoke.bind(this); - this.isLoading = this.isLoading.bind(this); - this.showValidatorModal = this.showValidatorModal.bind(this); - this.setValidatorLoading = this.setValidatorLoading.bind(this); - this.hideValidatorModal = this.hideValidatorModal.bind(this); - this.defaultGrant = { - claimGrant: null, - stakeGrant: null, - validators: [], - grantsValid: false, - grantsExist: false, - } - } - - async componentDidMount() { - const walletAuthzSupport = this.props.wallet?.authzSupport(); - this.setState({ walletAuthzSupport }); - this.refresh(true); - - if (this.props.validator) { - this.showValidatorModal(this.props.validator) - } - } - - async componentDidUpdate(prevProps, prevState) { - if (prevProps.validator !== this.props.validator && this.props.validator) { - this.showValidatorModal(this.props.validator) - } - - if ((this.props.network !== prevProps.network && !this.props.address) - || (this.props.address !== prevProps.address)) { - this.clearRefreshInterval() - const walletAuthzSupport = this.props.wallet?.authzSupport(); - this.setState({ - walletAuthzSupport: walletAuthzSupport, - delegations: undefined, - rewards: undefined, - commission: {}, - validatorApy: {}, - operatorGrants: {}, - error: null, - }); - this.refresh(false); - } - - if (this.props.grants !== prevProps.grants){ - this.getGrants() - } - } - - componentWillUnmount() { - this.clearRefreshInterval() - } - - async refresh(getGrants) { - this.calculateApy(); - await this.getDelegations() - if (getGrants){ - this.getGrants() - } - this.getWithdrawAddress(); - this.getRewards(); - this.refreshInterval(); - } - - refreshInterval() { - const refreshInterval = setInterval(() => { - this.props.getBalance(); - this.getRewards(true); - }, 15_000); - const delegateInterval = setInterval(() => { - this.getDelegations(true) - }, 30_000) - this.setState({ refreshInterval, delegateInterval }); - } - - clearRefreshInterval(){ - clearInterval(this.state.refreshInterval); - clearInterval(this.state.delegateInterval); - } - - async getDelegations(hideError) { - if(!this.props.address) return - const address = this.props.address - - return this.props.queryClient.getDelegations(address) - .then( - (delegations) => { - const orderedAddresses = Object.keys(this.props.validators) - delegations = orderedAddresses.reduce((sum, address) => { - if(delegations[address] && delegations[address].balance.amount !== '0'){ - sum[address] = delegations[address] - } - return sum - }, {}) - if(address === this.props.address){ - this.setState({ - delegations: delegations, - error: null - }); - } - }, - (error) => { - if(address !== this.props.address) return - - if([404, 500].includes(error.response && error.response.status) && !this.state.delegations){ - this.setState({ - delegations: {}, - error: null - }); - }else if(!hideError){ - this.setState({ - error: 'Failed to load delegations.', - }); - } - } - ) - } - - async getWithdrawAddress() { - if(!this.props.address) return - const address = this.props.address - - return this.props.queryClient.getWithdrawAddress(address).then(withdraw => { - if (withdraw !== address) { - this.setState({ error: 'You have a different withdraw address set. REStake WILL NOT WORK!' }) - } - }, error => { - console.log('Failed to get withdraw address', error) - }) - } - - getRewards(hideError) { - if(!this.props.address) return - - this.props.queryClient - .getRewards(this.props.address, this.props.network.denom) - .then( - (rewards) => { - this.setState({ rewards: rewards }); - }, - (error) => { - if ([404, 500].includes(error.response && error.response.status) && !this.state.rewards) { - this.setState({ rewards: {} }); - } else { - if (!hideError) - this.setState({ error: "Failed to get rewards." }); - } - } - ); - - Object.values(this.props.validators).forEach(validator => { - if(validator.isValidatorOperator(this.props.address)){ - this.props.queryClient.getCommission(validator.address).then((commission) => { - this.setState((state, props) => ({ - commission: _.set( - state.commission, - validator.address, - commission - ), - })); - }) - } - }) - } - - async calculateApy() { - if (!this.props.network.apyEnabled || !this.props.network.getApy) return - - this.props.network.getApy( - this.props.validators, - this.props.operators - ).then(validatorApy => { - this.setState({ validatorApy }); - }, error => { - console.log(error) - this.setState({ error: "Failed to get APY." }); - }) - } - - async getGrants() { - if (!this.props.grants?.granter) return - - const operatorGrants = this.props.operators.reduce((sum, operator) => { - const grantee = operator.botAddress - sum[grantee] = this.buildGrants(this.props.grants.granter, grantee, this.props.address) - return sum - }, {}) - this.setState({operatorGrants: operatorGrants}) - } - - buildGrants(grants, grantee, granter){ - const { claimGrant, stakeGrant } = parseGrants(grants, grantee, granter) - let grantValidators, maxTokens; - if (stakeGrant) { - const { allow_list, deny_list, max_tokens } = stakeGrant.authorization - if (allow_list?.address) { - grantValidators = allow_list.address - } else if (deny_list?.address) { - grantValidators = deny_list.address.includes('') ? [] : this.props.validators.map(el => el.address).filter(address => !deny_list.address.includes(address)) - } - maxTokens = max_tokens - } - return { - claimGrant: claimGrant, - stakeGrant: stakeGrant, - validators: grantValidators, - maxTokens: maxTokens ? bignumber(maxTokens.amount) : null - }; - } - - isValidatorOperator(){ - if(!this.props.address) return false - - return Object.values(this.props.validators).some(validator => validator.isValidatorOperator(this.props.address)) - } - - isLoading(type){ - if(!this.props.address) return false - const loaders = { - 'delegations': () => !this.state.delegations, - 'grants': () => this.props.network?.authzSupport && !this.props.grants?.granter, - 'rewards': () => !this.state.rewards, - 'commission': () => this.isValidatorOperator() && !this.state.commission - } - if(!type) return Object.values(loaders).some((value) => value()) - - return loaders[type] ? loaders[type]() : false - } - - onGrant(grantAddress, grant) { - const operator = this.props.operators.find(el => el.botAddress === grantAddress) - if(operator){ - this.setState((state, props) => ({ - error: null, - validatorLoading: _.set(state.validatorLoading, operator.address, false), - })); - } - this.props.onGrant(grantAddress, grant) - } - - onRevoke(grantAddress, msgTypes) { - const operator = this.props.operators.find(el => el.botAddress === grantAddress) - if(operator){ - this.setState((state, props) => ({ - error: null, - validatorLoading: _.set(state.validatorLoading, operator.address, false), - })); - }else{ - this.setState({ error: null }); - } - this.props.onRevoke(grantAddress, msgTypes) - } - - onClaimRewards() { - this.setState({ claimLoading: false, validatorLoading: {}, error: null }); - setTimeout(() => { - this.props.getBalance(); - this.getDelegations(); - this.getRewards(); - }, 3_000); - } - - setClaimLoading(value) { - if (value) this.setState({ error: null }); - this.setState({ claimLoading: !!value }); - } - - setValidatorLoading(validatorAddress, value) { - if (value) this.setState({ error: null }); - this.setState((state, props) => ({ - validatorLoading: _.set(state.validatorLoading, validatorAddress, value), - })); - } - - setError(error) { - this.setState({ error: error }); - } - - authzSupport() { - return this.props.network.authzSupport - } - - operatorGrants() { - if (!this.state.operatorGrants) return {} - return this.props.operators.reduce((sum, operator) => { - let grant = this.state.operatorGrants[operator.botAddress] - if (!grant) grant = this.defaultGrant; - sum[operator.botAddress] = { - ...grant, - grantsValid: !!( - grant.stakeGrant && - (!grant.validators || grant.validators.includes(operator.address)) && - (grant.maxTokens === null || larger(grant.maxTokens, rewardAmount(this.state.rewards, this.props.network.denom))) - ), - grantsExist: !!(grant.claimGrant || grant.stakeGrant), - } - return sum - }, {}) - } - - restakePossible() { - return this.props.address && this.state.walletAuthzSupport && this.authzSupport(); - } - - totalRewards(validators) { - if (!this.state.rewards) return; - - const denom = this.props.network.denom; - const total = Object.values(this.state.rewards).reduce((sum, item) => { - const reward = item.reward.find((el) => el.denom === denom); - if ( - reward && - (validators === undefined || - validators.includes(item.validator_address)) - ) { - return sum + parseInt(reward.amount); - } - return sum; - }, 0); - return { - amount: total, - denom: denom, - }; - } - - showValidatorModal(validator, opts) { - opts = opts || {} - this.setState({ validatorModal: { show: true, validator: validator, ...opts } }) - } - - hideValidatorModal(opts) { - opts = opts || {} - this.setState((state, props) => { - return { validatorModal: { ...state.validatorModal, show: false } } - }) - } - - renderValidatorModal() { - const validatorModal = this.state.validatorModal - - return ( - - ) - } - - render() { - if (!this.props.validators) { - return ( -
    - - Loading... - -
    - ); - } - - const alerts = ( - <> - {!this.authzSupport() && ( - - {this.props.network.prettyName} doesn't support Authz just yet. You can stake and compound manually, REStake will update automatically when support is added. - - )} - {this.authzSupport() && - this.props.operators.length > 0 && - this.props.wallet && - !this.state.walletAuthzSupport && ( - <> - - {this.props.wallet.getIsNanoLedger() ? ( - <> -

    Ledger devices can't send Authz transactions on {this.props.network.prettyName} just yet. All other features are supported and Authz is coming soon.

    -

    this.setState({ showAboutLedger: true })} role="button" className="text-reset text-decoration-underline">A manual workaround is possible using the CLI.

    - - ) : ( - <> -

    This wallet can't send Authz transactions on {this.props.network.prettyName} just yet. All other features are supported, and Keplr Extension likely has support if required.

    - - )} -
    - - )} - - {this.props.network && ( - this.setState({ showAboutLedger: false })} network={this.props.network} /> - )} - - ); - - return ( - <> - {alerts} -
    - { - const { network, wallet, address } = this.props - const validatorAddress = validator.operator_address - const validatorOperator = validator.isValidatorOperator(address) - return ( - !this.state.validatorLoading[validatorAddress] ? ( - filter.group === 'delegated' && delegation ? ( - - - - - - this.showValidatorModal(validator, { activeTab: 'profile' })}> - View {validator.moniker} - -
    - this.setValidatorLoading(validatorAddress, loading)} - setError={this.setError} - /> - this.setValidatorLoading(validatorAddress, loading)} - setError={this.setError} - /> - {validatorOperator && ( - <> -
    - this.setValidatorLoading(validatorAddress, loading)} - setError={this.setError} - /> - - )} -
    -
    - ) : ( - - ) - ) : ( - - ) - ) - }} /> -
    -
    -
    -
    -
    -
    - {this.state.rewards && - (!this.state.claimLoading && !this.isLoading('rewards') ? ( - - - All Rewards - - - - - - - - ) : ( - - ))} -
    -
    -
    -
    -

    - Enabling REStake will authorize the validator to send Delegate transactions on your behalf for 1 year using Authz.
    - They will only be authorized to delegate to their own validator. You can revoke the authorization at any time and everything is open source. -

    -

    - The validators will pay the transaction fees for you. -

    -

    - -

    - {this.renderValidatorModal()} - - ); - } -} - -export default Delegations; diff --git a/src/components/Favourite.js b/src/components/Favourite.js deleted file mode 100644 index 12c29bf2a..000000000 --- a/src/components/Favourite.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react' -import { - BookmarkPlus, - BookmarkPlusFill -} from 'react-bootstrap-icons' -import TooltipIcon from './TooltipIcon' - -function Favourite(props) { - const { favourites, value, toggle, label } = props - const key = props.key || 'address' - const favourited = favourites.some(el => el[key] === value) - const className = `${props.className} favourite-${favourited ? 'on' : 'off'}` - return ( - -
    toggle(value, label)} className={className}> - - -
    -
    - ) -} - -export default Favourite \ No newline at end of file diff --git a/src/components/GrantModal.js b/src/components/GrantModal.js deleted file mode 100644 index 14790c9bf..000000000 --- a/src/components/GrantModal.js +++ /dev/null @@ -1,256 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import _ from 'lodash' -import moment from 'moment' - -import { MsgGrant } from "cosmjs-types/cosmos/authz/v1beta1/tx"; -import { GenericAuthorization } from "cosmjs-types/cosmos/authz/v1beta1/authz"; -import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; - -import { - Modal, - Button, - Form, - Collapse -} from 'react-bootstrap' - -import AlertMessage from './AlertMessage'; -import { messageTypes } from '../utils/Wallet.mjs'; -import { buildExecMessage, truncateAddress } from '../utils/Helpers.mjs'; - -function GrantModal(props) { - const { show, network, address, wallet } = props - const walletAuthzSupport = wallet?.authzSupport() - const defaultExpiry = moment().add(1, 'year') - const [loading, setLoading] = useState(false); - const [error, setError] = useState() - const [state, setState] = useState({ maxTokensValue: '', expiryDateValue: defaultExpiry.format('YYYY-MM-DD') }); - const [showLedger, setShowLedger] = useState(!walletAuthzSupport) - - const { daemon_name, chain_id } = network.chain - - useEffect(() => { - setState({ - ...state, - granteeValue: '', - customGranteeValue: '', - expiryDateValue: defaultExpiry.format('YYYY-MM-DD'), - grantTypeValue: '/cosmos.authz.v1beta1.GenericAuthorization', - messageTypeValue: messageTypes[0], - customMessageTypeValue: '', - }) - setShowLedger(!walletAuthzSupport) - setError(null) - }, [address]) - - function handleInputChange(e) { - setState({ ...state, [e.target.name]: e.target.value }); - } - - function showLoading(isLoading) { - setLoading(isLoading) - props.setLoading && props.setLoading(isLoading) - } - - function messageType(){ - return state.messageTypeValue === 'Custom' ? state.customMessageTypeValue : state.messageTypeValue - } - - function handleSubmit(event) { - event.preventDefault() - if(!address || !walletAuthzSupport || !valid()) return - - showLoading(true) - const expiry = moment(state.expiryDateValue) - - const messages = [ - buildGrantMsg(state.grantTypeValue, - GenericAuthorization.encode(GenericAuthorization.fromPartial({ - msg: messageType() - })).finish(), - expiry - ) - ] - console.log(messages) - - props.signingClient.signAndBroadcast(wallet.address, messages).then((result) => { - console.log("Successfully broadcasted:", result); - showLoading(false) - props.onGrant(grantee(), { - grantee: grantee(), - granter: address, - expiration: expiry, - authorization: { - '@type': state.grantTypeValue, - msg: messageType() - } - }); - }, (error) => { - console.log('Failed to broadcast:', error) - showLoading(false) - setError('Failed to broadcast: ' + error.message) - }) - } - - function handleClose() { - setError(null) - props.onHide(); - } - - function buildGrantMsg(type, authValue, expiryDate) { - const value = { - granter: address, - grantee: grantee(), - grant: { - authorization: { - typeUrl: type, - value: authValue - }, - expiration: Timestamp.fromPartial({ - seconds: expiryDate.unix(), - nanos: 0 - }) - } - } - if(wallet?.address !== address){ - return buildExecMessage(wallet.address, [{ - typeUrl: "/cosmos.authz.v1beta1.MsgGrant", - value: MsgGrant.encode(MsgGrant.fromPartial(value)).finish() - }]) - }else{ - return { - typeUrl: "/cosmos.authz.v1beta1.MsgGrant", - value: value - } - } - } - - function grantee(){ - return state.granteeValue === 'custom' ? state.customGranteeValue : state.granteeValue - } - - function valid(){ - return state.granteeValue && validGrantee() && !!messageType() && wallet?.hasPermission(address, 'Grant') - } - - function validGrantee(){ - const value = grantee() - if(!value) return true; - - return !network.prefix || value.startsWith(network.prefix) - } - - function favourites(){ - return props.favouriteAddresses.filter(el => el.address !== props.address) - } - - return ( - <> - - - - {address && !!walletAuthzSupport ? 'New Grant' : 'CLI/Ledger instructions'} - - - - {error && - - {error} - - } - {!address || !walletAuthzSupport && ( - <> -

    Enter your grant details to generate the relevant CLI command.

    - - )} -
    - - Grantee - - {state.granteeValue === 'custom' && ( - - )} - - - Expiry date - - - - Grant type - - - {state.grantTypeValue === '/cosmos.authz.v1beta1.GenericAuthorization' && ( - <> - - Message type - - - {state.messageTypeValue === 'Custom' && ( - - Type URL - - - )} - - )} -

    Incorrect use of Authz grants can be as dangerous as giving away your mnemonic. Make sure you trust the Grantee address and understand the permissions you are granting.

    - {address && !!walletAuthzSupport && ( -

    - - {!loading - ? ( - - ) - : - } -

    - )} - -
    
    -                

    {daemon_name ? daemon_name : {''}} tx authz grant \
    - {grantee() || ''} generic \
    - --msg-type {messageType()} \
    - --expiration {moment(state.expiryDateValue).unix()} \
    - --chain-id {chain_id} \
    - --node https://rpc.cosmos.directory:443/{network.name} \
    - --gas auto --gas-prices {network.gasPrice} \
    - --gas-adjustment 1.5 \
    - --from my-key

    -
    -
    -
    -
    -
    - - ); -} - -export default GrantModal diff --git a/src/components/Grants.js b/src/components/Grants.js deleted file mode 100644 index 59fe13df2..000000000 --- a/src/components/Grants.js +++ /dev/null @@ -1,309 +0,0 @@ -import React, { useState, useEffect, useReducer } from 'react'; -import _ from 'lodash' -import FuzzySearch from 'fuzzy-search' -import Moment from 'react-moment'; - -import { - Spinner, - Table, - Nav, - Button -} from 'react-bootstrap' -import { XCircle } from "react-bootstrap-icons"; - -import AlertMessage from './AlertMessage'; -import RevokeGrant from './RevokeGrant'; -import Coins from './Coins'; -import GrantModal from './GrantModal'; -import Favourite from './Favourite'; -import Address from './Address' - -function Grants(props) { - const { address, wallet, network, operators, validators, grants } = props - const [error, setError] = useState() - const [showModal, setShowModal] = useState() - const [grantsLoading, setGrantLoading] = useReducer( - (grantsLoading, newGrantsLoading) => ({...grantsLoading, ...newGrantsLoading}), - {} - ) - - const [filter, setFilter] = useState({keywords: '', group: 'granter'}) - const [results, setResults] = useState([]) - - const walletAuthzSupport = props.wallet?.authzSupport() - - useEffect(() => { - if(!grants) return - - let filtered = filteredGrants(grants, filter) - let group = filter.group - while(filtered.length < 1 && group !== 'grantee'){ - group = 'grantee' - filtered = filteredGrants(grants, {...filter, group}) - if(filtered.length > 0 || group === 'all'){ - return setFilter({ ...filter, group }) - } - } - setResults(filtered) - }, [grants, filter]); - - function sortGrants(grants){ - return _.sortBy(grants, ({ expiration }) => { - return new Date(expiration) - }); - } - - function filterGrants(event){ - setFilter({...filter, keywords: event.target.value}) - } - - function filteredGrants(grants, filter){ - let searchResults = grants - const { keywords, group } = filter - - searchResults = filterByGroup(searchResults, group) - - if (!keywords || keywords === '') return sortGrants(searchResults) - - const searcher = new FuzzySearch( - searchResults, ['grantee', 'granter', 'authorization.@type', 'authorization.msg'], - { sort: true } - ) - - return searcher.search(keywords) - } - - function filterByGroup(grants, group){ - switch (group) { - case 'grantee': - grants = grants.grantee - break; - case 'granter': - grants = grants.granter - break; - } - return grants - } - - function validatorForGrantAddress(grantAddress){ - const operator = operators.find(el => el.botAddress === grantAddress) - if(!operator) return null - - return validators[operator.address] - } - - function renderGrantData(grant){ - const maxTokens = grant.authorization.max_tokens - switch (grant.authorization['@type']) { - case "/cosmos.staking.v1beta1.StakeAuthorization": - const restrictionType = grant.authorization.allow_list ? 'Validators' : 'Denied Validators' - const restrictionList = grant.authorization.allow_list || grant.authorization.deny_list - return ( - - Maximum: {maxTokens ? : 'unlimited'}
    - {restrictionType}: {restrictionList.address.map(address => { - const validator = validators[address] - return address ?
    {validator?.moniker ||
    }
    : null - })} -
    - ) - case "/cosmos.authz.v1beta1.GenericAuthorization": - return ( - - Message: {grant.authorization.msg.split('.').slice(-1)[0]} - - ) - } - } - - function closeModal(){ - setShowModal(false) - } - - function onGrant(grantee, grant){ - setError(false) - closeModal() - props.onGrant(grantee, grant) - } - - function onRevoke(grantee, msgTypes){ - setError(false) - props.onRevoke(grantee, msgTypes) - } - - function renderGrant(grant) { - const { granter, grantee, authorization, expiration } = grant - - const validator = filter.group === 'granter' && validatorForGrantAddress(grant.grantee) - const favourite = props.favouriteAddresses && props.favouriteAddresses.find(el => el.address === (filter.group === 'granter' ? grantee : granter)) - const grantId = `${granter}-${grantee}-${authorization['@type']}-${authorization.msg}` - return ( - - - {filter.group === 'grantee' ? ( -
    - - {favourite?.label ||
    } -
    - ) : ( - validator ? validator.moniker : favourite?.label ||
    - )} - - - {authorization['@type'].split('.').slice(-1)[0]} - - - {renderGrantData(grant)} - - - - {expiration} - - - {filter.group === 'granter' && ( - -
    - {!grantsLoading[grantId] ? ( - - setGrantLoading({ [grantId]: loading }) - } - setError={setError} - /> - ) : ( - - )} -
    - - )} - - ); - } - - if (!grants) { - return ( -
    - - Loading... - -
    - ); - } - - const alerts = ( - <> - {!props.grantQuerySupport && ( - Grants cannot be queried on this network yet. Save addresses first to see their grants. - )} - {props.grantQuerySupport && !walletAuthzSupport && ( - -

    Ledger devices can't send Authz transactions just yet. Full support will be enabled as soon as it is possible.

    -

    setShowModal(true)} role="button" className="text-reset text-decoration-underline">A manual workaround is possible using the CLI.

    -
    - )} - - - ); - - return ( - <> - {alerts} -
    -
    -
    -
    - - - - -
    -
    -
    -
    - - - - -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - {results.length > 0 && - - - - - - - - {filter.group === 'granter' && ( - - )} - - - - {results.map(item => renderGrant(item))} - -
    {filter.group === 'grantee' ? 'Granter' : 'Grantee'}TypeDataExpiration
    - } - {results.length < 1 && -

    No grants found

    - } -
    - - - ); -} - -export default Grants; \ No newline at end of file diff --git a/src/components/NetworkChecks.js b/src/components/NetworkChecks.js deleted file mode 100644 index c9ee90f57..000000000 --- a/src/components/NetworkChecks.js +++ /dev/null @@ -1,105 +0,0 @@ -import React from 'react'; -import _ from 'lodash' -import { CheckCircle, XCircle, InfoCircle } from "react-bootstrap-icons"; -import { round } from 'mathjs' -import TooltipIcon from './TooltipIcon.js'; -import Coingecko from '../assets/coingecko.png' - -function NetworkChecks(props) { - const { network, error, skipConnected } = props - const baseAsset = network?.baseAsset - const price = baseAsset?.prices?.coingecko - - function renderCheck({ title, failTitle, description, failDescription, state, successClass, failClass, identifier, icon }) { - const className = state ? (successClass || 'success') : (failClass || 'warning') - - const content = ( -
    - {icon ? icon : state ? ( - - ) : ( - failClass === 'danger' ? : - )}{state ? title : (failTitle || title)} -
    - ) - - return ( -
  20. - -
  21. - ) - } - - const className = ['list-group', props.className].join(' ') - - const operatorCheck = { - title: {network.operatorCount} REStake operators, - failTitle: "No REStake operators", - failDescription: "There are no operators for this network yet. You can stake and compound manually in the meantime." - } - if(!network.authzSupport && network.operatorCount > 0){ - operatorCheck.failTitle = operatorCheck.title, - operatorCheck.failDescription = "Authz is disabled but there are operators ready when support is added." - } - - const testedCheck = { - title: 'Tested with REStake', - failTitle: 'Experimental support', - failDescription: "This network was added to REStake automatically and has not been thoroughly tested yet.", - } - if(!network.authzSupport && !network.experimental){ - testedCheck.description = "Authz is disabled but all other features have been fully tested." - } - - return ( -
      - {([ - renderCheck({ - title: {`$${price && price.usd.toLocaleString(undefined, { maximumFractionDigits: 8, minimumFractionDigits: 2 })}`}, - failTitle: 'Price unknown', - state: price?.usd, - identifier: 'price', - icon: , - failClass: 'success', - }), - renderCheck({ - title: {`${round(network.estimatedApr * 100, 2).toLocaleString()}% APR`}, - failTitle: 'APR unknown', - state: network.estimatedApr, - identifier: 'apr', - }), - !skipConnected && renderCheck({ - title: 'API connected', - failTitle: 'API offline', - failDescription: error, - state: network.connected && !error, - failClass: 'danger', - identifier: 'network' - }), - renderCheck({ - title: network.authzAminoSupport ? Full Authz support : 'Authz support', - failTitle: 'Authz not supported', - failDescription: "This network doesn't support Authz just yet. You can stake and compound manually, REStake will update automatically when support is added.", - state: network.authzSupport, - identifier: 'authz' - }), - renderCheck({ - ...operatorCheck, - state: network.authzSupport && network.operatorCount > 0, - identifier: 'operators' - }), - renderCheck({ - ...testedCheck, - state: !network.experimental, - identifier: 'experimental' - }), - ])} -
    - ) -} - -export default NetworkChecks; diff --git a/src/components/NetworkFinder.js b/src/components/NetworkFinder.js deleted file mode 100644 index ad618238e..000000000 --- a/src/components/NetworkFinder.js +++ /dev/null @@ -1,239 +0,0 @@ -import _ from 'lodash' -import React, { useEffect, useState, useReducer } from 'react'; -import { useParams, useNavigate, useMatch } from "react-router-dom"; -import Network from '../utils/Network.mjs' -import CosmosDirectory from '../utils/CosmosDirectory.mjs' -import App from './App'; -import AlertMessage from './AlertMessage' - -import { - Spinner -} from 'react-bootstrap'; - -import networksData from '../networks.json'; - -const LIGHT_THEME = 'cosmo' -const DARK_THEME = 'superhero' - -function NetworkFinder() { - const params = useParams(); - const navigate = useNavigate() - const voteMatch = useMatch("/:network/vote/*"); - const grantMatch = useMatch("/:network/grants"); - - const networkMode = process.env.TESTNET_MODE === '1' ? 'testnet' : 'mainnet' - const directory = getDirectory() - - const LS_THEME_KEY = "restake-theme"; - const LS_THEME = localStorage.getItem(LS_THEME_KEY) - - const [theme, setTheme] = useState() - const [themeChoice, setThemeChoice] = useState(LS_THEME || 'auto') - const [themeDefault, setThemeDefault] = useState('light') - const [state, setState] = useReducer( - (state, newState) => ({...state, ...newState}), - {loading: true, networks: {}, operators: [], validators: {}, networkMode, directory} - ) - - function getDirectory() { - let testnet = networkMode === 'testnet'; - if (params.network && !testnet) { - const data = networksData.find(el => el.name === params.network); - if (data && data.testnet) { - testnet = true; - } - } - return CosmosDirectory(testnet); - } - - async function getNetworks() { - let registryNetworks, operatorAddresses; - try { - registryNetworks = await state.directory.getChains(); - operatorAddresses = await state.directory.getOperatorAddresses(); - } catch (error) { - setState({ error: error.message, loading: false }); - return {}; - } - - const networks = Object.values(registryNetworks).map(data => { - const networkData = networksData.find(el => el.name === data.path); - if (networkData && networkData.enabled === false) - return; - if (!data.image || data.status === 'killed') - return; - - if (!networkData) - data.experimental = true; - - return new Network({ ...data, ...networkData }, operatorAddresses[data.path]); - }); - return _.compact(networks).reduce((a, v) => ({ ...a, [v.path]: v }), {}); - } - - function changeNetworkMode(networkMode) { - let domain = networkMode === 'testnet' ? process.env.TESTNET_DOMAIN : process.env.MAINNET_DOMAIN; - if (domain) { - window.location.replace('https://' + domain); - } else { - const directory = CosmosDirectory(networkMode === 'testnet'); - setState({ networkMode, directory, active: 'networks', network: null, queryClient: null, networks: {}, validators: {}, operators: [] }); - } - } - - function changeNetwork(network) { - if(!network) return - - setState({ - network: network, - queryClient: network.queryClient, - validators: network.getValidators(), - operators: network.getOperators(), - loading: false - }); - if (voteMatch) { - setActive('voting', network); - } else if(grantMatch && network.authzSupport) { - setActive('grants', network); - } else { - setActive('delegations', network); - } - } - - function setActive(active, network) { - network = network || state.network; - switch (active) { - case 'grants': - navigate("/" + network.path + '/grants'); - break; - case 'voting': - navigate("/" + network.path + '/vote'); - break; - case 'delegations': - navigate("/" + network.path); - break; - default: - navigate("/"); - break; - } - setState({ active }); - - const body = document.querySelector('#root'); - body.scrollIntoView({}, 500); - } - - - useEffect(() => { - const setThemeEvent = (event) => { - setTheme(event.matches ? "dark" : "light") - setThemeDefault(event.matches ? "dark" : "light") - } - const matchMedia = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)') - if (themeChoice !== 'auto') { - setTheme(themeChoice) - } else if (matchMedia) { - matchMedia.addEventListener('change', setThemeEvent); - setThemeEvent(matchMedia) - } else { - setTheme('light') - } - - if (localStorage.getItem(LS_THEME_KEY) !== themeChoice) { - localStorage.setItem(LS_THEME_KEY, themeChoice) - } - - return () => { - matchMedia && matchMedia.removeEventListener('change', setThemeEvent) - } - }, [themeChoice]) - - useEffect(() => { - if (theme) { - const themeLink = document.getElementById("theme-style"); - const themeName = theme === 'dark' ? DARK_THEME : LIGHT_THEME - themeLink.setAttribute("href", `https://cdn.jsdelivr.net/npm/bootswatch@5.1.3/dist/${themeName}/bootstrap.min.css`); - document.body.classList.toggle('dark-theme', theme === 'dark'); - } - }, [theme]) - - useEffect(() => { - if (state.error) return - if (!Object.keys(state.networks).length) { - setState({ loading: true }) - getNetworks().then(networks => { - setState({ networks: networks }) - }) - } - }, [state.networks]) - - useEffect(() => { - if (!params.network) { - setState({ active: 'networks' }) - } - }, [voteMatch, grantMatch, params.network]) - - useEffect(() => { - if (Object.keys(state.networks).length && (!state.network || state.network.path !== params.network)) { - let networkName = params.network - const network = state.networks[networkName] - if (!network) { - navigate("/"); - setState({ loading: false }) - return - } - if (params.network != networkName) { - navigate("/" + networkName); - } - network.load().then(() => { - return network.connect().then(() => { - if (network.connected) { - setState({ - active: grantMatch ? 'grants' : voteMatch ? 'voting' : 'delegations', - network: network, - queryClient: network.queryClient, - validators: network.getValidators(), - operators: network.getOperators(), - loading: false - }) - } else { - throw false - } - }) - }).catch(error => { - console.log(error) - changeNetwork(network) - }) - } - }, [state.networks, state.network, params.network, navigate]) - - useEffect(() => { - if (params.validator && state.validators[params.validator]) { - setState({ validator: state.validators[params.validator] }) - } else if (state.validator) { - setState({ validator: null }) - } - }, [state.validators, params.validator]) - - if (state.error) { - return - } - - if (state.loading) { - return ( -
    - - Loading... - -
    - ) - } - - return changeNetwork(network, validators)} setActive={setActive} - theme={theme} themeChoice={themeChoice} themeDefault={themeDefault} setThemeChoice={setThemeChoice} - />; -} - -export default NetworkFinder diff --git a/src/components/NetworkImage.js b/src/components/NetworkImage.js deleted file mode 100644 index de4d1df5b..000000000 --- a/src/components/NetworkImage.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react' - -function NetworkImage(props) { - let { network, assetlist, width, height, alt } = props - height = height || 30 - width = width || 30 - let fallbackImage = `https://craftypixels.com/placeholder-image/${width}x${height}/ffffff/a6a6a6&text=missing` - let image - - if(assetlist){ - const baseAsset = assetlist.assets && assetlist.assets[0]; - image = baseAsset?.logo_URIs?.svg || baseAsset?.logo_URIs?.png - } - - image = image || network.image - - return ( - {alt} { - currentTarget.onerror = null; // prevents looping - currentTarget.src = fallbackImage; - }} /> - ) -} - -export default NetworkImage \ No newline at end of file diff --git a/src/components/NetworkSelect.js b/src/components/NetworkSelect.js deleted file mode 100644 index f06f89214..000000000 --- a/src/components/NetworkSelect.js +++ /dev/null @@ -1,220 +0,0 @@ -import React, { useState, useReducer, useEffect } from 'react'; - -import { - Button, - Modal, - Form, - Badge -} from 'react-bootstrap' - -import Select from 'react-select'; -import NetworkChecks from './NetworkChecks.js'; -import NetworkImage from './NetworkImage.js'; - -function NetworkSelect(props) { - const [show, setShow] = useState(props.show); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(false); - const [selectedNetwork, setSelectedNetwork] = useState(); - const [options, setOptions] = useReducer( - (state, newState) => ({ ...state, ...newState }), - { networks: [], operators: [], network: { value: '' } } - ) - - function handleOpen() { - setSelectedNetwork(props.network); - setShow(true); - } - - function handleClose() { - setSelectedNetwork(); - setShow(false); - props.onHide(); - } - - function handleSubmit(event) { - event.preventDefault(); - - props.changeNetwork(selectedNetwork); - handleClose(); - } - - function selectNetwork(newValue) { - const network = props.networks[newValue.value.replace(/^(favourite-)/,'')]; - if (network) { - setLoading(true); - setError(false); - network.load().then(() => { - setSelectedNetwork(network); - if (network.usingDirectory && !network.connectedDirectory()) { - throw false; - } - return network.connect().then(() => { - if (network.connected) { - setLoading(false); - } else { - throw false; - } - }); - }).catch(error => { - console.log(error); - setError('Could not connect to this network. Try again later'); - setLoading(false); - }); - } - } - - function selectedOption(){ - let networks = options.networks - if(options.grouped){ - networks = options.grouped.map(el => el.options).flat() - } - return networks.find(el => el.value === options.network) - } - - useEffect(() => { - if (props.show && !show) { - handleOpen() - } else if (!props.show && show) { - handleClose() - } - }, [props.show]) - - useEffect(() => { - const networks = Object.values(props.networks).sort((a, b) => a.name > b.name ? 1 : -1) - const networkOptions = networks.map(network => { - return { - value: network.path, - label: network.prettyName, - image: network.image, - operatorCount: network.operatorCount, - authz: network.authzSupport, - online: network.online, - experimental: network.experimental - } - }) - const isFavourite = selectedNetwork && props.favourites && props.favourites.includes(selectedNetwork.path) - setOptions({ - networks: networkOptions, - network: isFavourite ? `favourite-${selectedNetwork.path}` : selectedNetwork && selectedNetwork.path, - grouped: props.favourites && props.favourites.length > 0 && [ - { - label: 'Favourites', - options: networkOptions.filter(el => props.favourites.includes(el.value)).map(el => { - return {...el, value: `favourite-${el.value}`} - }) - }, - { - label: 'All Networks', - options: networkOptions - } - ] - }) - }, [props.networks, selectedNetwork]) - - const price = props.network?.baseAsset?.prices?.coingecko - - return ( - <> - - - - Change Network - - -
    - {props.networks && -
    -
    - - - - -
    -
    -
    - -
    -
    - -
    -
    -
    -
    - {renderNetworks(results)} - - ); -} - -export default Networks; \ No newline at end of file diff --git a/src/components/OperatorLastRestake.js b/src/components/OperatorLastRestake.js deleted file mode 100644 index dc2f27e61..000000000 --- a/src/components/OperatorLastRestake.js +++ /dev/null @@ -1,28 +0,0 @@ -import { - QuestionCircle -} from 'react-bootstrap-icons' - -import TooltipIcon from './TooltipIcon' - -function OperatorLastRestake(props) { - const { operator, lastExec } = props - - function classname() { - if (!operator || lastExec == null) - return; - - const missedRuns = operator.missedRunCount(lastExec); - const warning = missedRuns > Math.min(20, operator.runsPerDay()); - const error = missedRuns > Math.min(30, operator.runsPerDay() * 2); - return error ? 'text-danger' : warning ? 'text-warning' : 'text-success'; - } - - return ( -
    - {lastExec != null ? {lastExec ? lastExec?.fromNow() : 'Not recently'} : } - } identifier={operator.address} tooltip="Based on the last REStake transaction sent by this validator for any of their users. Not every run generates a transaction." /> -
    - ) -} - -export default OperatorLastRestake \ No newline at end of file diff --git a/src/components/OperatorLastRestakeAlert.js b/src/components/OperatorLastRestakeAlert.js deleted file mode 100644 index 641c17f32..000000000 --- a/src/components/OperatorLastRestakeAlert.js +++ /dev/null @@ -1,23 +0,0 @@ -import AlertMessage from './AlertMessage'; - -function OperatorLastRestakeAlert(props) { - const { operator, lastExec } = props - - function showWarning() { - if (!operator || lastExec == null) - return; - - const missedRuns = operator.missedRunCount(lastExec); - return missedRuns > Math.min(30, operator.runsPerDay() * 2); - } - - if (!showWarning()) return null - - return ( - - This validator has not REStaked recently. - - ) -} - -export default OperatorLastRestakeAlert \ No newline at end of file diff --git a/src/components/ProposalDetails.js b/src/components/ProposalDetails.js deleted file mode 100644 index ba842c130..000000000 --- a/src/components/ProposalDetails.js +++ /dev/null @@ -1,176 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import Moment from 'react-moment'; -import ReactMarkdown from 'react-markdown' -import remarkGfm from 'remark-gfm' - -import { - Table, -} from 'react-bootstrap' - -import Coins from './Coins'; -import ProposalProgress from './ProposalProgress'; -import VoteForm from './VoteForm'; -import AlertMessage from './AlertMessage'; -import Vote from '../utils/Vote.mjs'; - -function ProposalDetails(props) { - const { proposal, tally, vote, network } = props - const [granter, setGranter] = useState() - const [granterVote, setGranterVote] = useState() - const [error, setError] = useState() - - const { proposal_id, title, description } = proposal - - const fixDescription = description.replace(/\\n/g, ' \n') - - useEffect(() => { - if(props.address !== props.wallet?.address && props.granters.includes(props.address)){ - setGranter(props.address) - } - }, [props.address]); - - useEffect(() => { - if(granter){ - props.queryClient.getProposalVote(proposal_id, granter).then(result => { - return setGranterVote(Vote(result.vote)) - }).catch(error => { - setGranterVote(null) - }) - }else{ - setGranterVote(null) - } - }, [granter]); - - function onVote(proposal, vote){ - if(granter && props.address !== granter){ - setGranterVote(vote) - }else{ - props.onVote(proposal, vote) - } - } - - return ( - <> - {error && - - {error} - - } -
    -
    - - - - - - - - - - - - - - - {!proposal.isDeposit && ( - <> - - - - - - - - - - )} - {proposal.isDeposit && ( - <> - - - - - - - - - - )} - -
    ID#{proposal_id}
    Status{proposal.statusHuman}
    Type{proposal.typeHuman}
    Submit time - - {proposal.submit_time} - -
    Voting end time - - {proposal.voting_end_time} - -
    Deposit end time - - {proposal.deposit_end_time} - -
    Total deposit - {proposal.total_deposit.map(coins => { - return - })} -
    -
    - {props.address && ( -
    -

    - {props.granters.length > 0 ? ( - - ) : ( - Your Vote - )} -

    - -
    - )} -
    -
    - -
    -
    -
    -
    {title}
    - - }} - /> - - - - ) -} - -export default ProposalDetails \ No newline at end of file diff --git a/src/components/ProposalModal.js b/src/components/ProposalModal.js deleted file mode 100644 index 7454688c6..000000000 --- a/src/components/ProposalModal.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; - -import { - Modal, -} from 'react-bootstrap' - -import ProposalDetails from './ProposalDetails'; - -function ProposalModal(props) { - const { show, proposal, tally, vote, network, address } = props - - const handleClose = () => { - props.closeProposal() - } - - if(!proposal) return null - - return ( - <> - - - - {proposal.title} - - - - - - - - ); -} - -export default ProposalModal diff --git a/src/components/ProposalProgress.js b/src/components/ProposalProgress.js deleted file mode 100644 index 77ffcbecd..000000000 --- a/src/components/ProposalProgress.js +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import _ from 'lodash' -import { add, divide, multiply, format } from 'mathjs' - -import { - OverlayTrigger, - Tooltip, - ProgressBar -} from 'react-bootstrap' - -function ProposalProgress(props) { - const { proposal, tally } = props - const { proposal_id, status } = proposal - - if (!tally) return null - if (status === 'PROPOSAL_STATUS_DEPOSIT_PERIOD') return null - - const variants = { - 'yes': 'success', - 'abstain': 'info', - 'no': 'danger', - 'no_with_veto': 'danger' - } - const total = Object.values(tally).reduce((sum, value) => add(sum, value), 0) - if(total === 0) return null - - const progress = Object.keys(tally).reduce((sum, key) => { - sum[key] = multiply(divide(tally[key], total), 100) - return sum - }, {}) - - function variant(key){ - return variants[key.replace('_count', '')] - } - - return ( - - {Object.entries(progress).map(([key, value]) => { - return

    {[_.startCase(key), format(value, { precision: 2, notation: 'fixed' })].join(': ')}%

    - })} - - } - > - - {Object.entries(progress).map(([key, value]) => { - return ( - - ) - })} - -
    - ) -} - -export default ProposalProgress; diff --git a/src/components/Proposals.js b/src/components/Proposals.js deleted file mode 100644 index 6a87674c2..000000000 --- a/src/components/Proposals.js +++ /dev/null @@ -1,190 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import Moment from 'react-moment'; -import _ from 'lodash' -import FuzzySearch from 'fuzzy-search' - -import { - Table, - Button, - Nav, -} from 'react-bootstrap' -import { XCircle } from "react-bootstrap-icons"; - -import ProposalProgress from './ProposalProgress'; -import { PROPOSAL_STATUSES } from '../utils/Proposal.mjs'; - -function Proposals(props) { - const { proposals, tallies, votes } = props - - const [filter, setFilter] = useState({keywords: '', status: '', group: 'voting'}) - const [results, setResults] = useState([]) - - useEffect(() => { - let filtered = filteredProposals(proposals, filter) - let group = filter.group - while(filtered.length < 1 && group !== 'all'){ - group = 'all' - filtered = filteredProposals(proposals, {...filter, group}) - if(filtered.length > 0 || group === 'all'){ - return setFilter({ ...filter, group }) - } - } - setResults(filtered) - }, [proposals, filter]); - - function sortProposals(proposals){ - return _.sortBy(proposals, ({ proposal_id }) => { - return 0 - parseInt(proposal_id) - }); - } - - function filterProposals(event){ - setFilter({...filter, keywords: event.target.value}) - } - - function filteredProposals(proposals, filter){ - let searchResults = proposals - const { keywords, status, group } = filter - - if(status){ - searchResults = searchResults.filter(result => { - return result.status === status - }) - } - - searchResults = filterByGroup(searchResults, group) - - if (!keywords || keywords === '') return sortProposals(searchResults) - - const searcher = new FuzzySearch( - searchResults, ['content.title'], - { sort: true } - ) - - return searcher.search(keywords) - } - - function filterByGroup(proposals, group){ - switch (group) { - case 'voting': - proposals = proposals.filter((proposal) => proposal.isVoting) - break; - } - return proposals - } - - function renderProposal(proposal) { - const proposalId = proposal.proposal_id - const vote = votes[proposalId] - return ( - - - - - - - - - - ); - } - - return ( - <> -
    -
    -
    - - - - -
    -
    -
    -
    - - - - -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - {results.length > 0 && -
    {proposalId} - props.showProposal(proposal)}> - {proposal.title} - - - {proposal.statusHuman} - - - {proposal.isDeposit ? proposal.deposit_end_time : proposal.voting_end_time} - - - {proposal.isVoting && ( - vote ? vote.optionHuman : - )} - - - -
    - -
    -
    - - - - - - - - - - - - - {results.map(item => renderProposal(item))} - -
    #ProposalStatusEnd TimeVoteProgress
    - } - {results.length < 1 && -

    No proposals found

    - } - - ) -} - -export default Proposals; diff --git a/src/components/REStakeGrantForm.js b/src/components/REStakeGrantForm.js deleted file mode 100644 index 53b45bce0..000000000 --- a/src/components/REStakeGrantForm.js +++ /dev/null @@ -1,228 +0,0 @@ -import React, { useState, useEffect, useReducer } from 'react'; -import moment from 'moment' -import { pow, multiply, divide, larger, bignumber } from 'mathjs' - -import { MsgGrant } from "cosmjs-types/cosmos/authz/v1beta1/tx"; -import { StakeAuthorization } from "cosmjs-types/cosmos/staking/v1beta1/authz"; -import { Timestamp } from "cosmjs-types/google/protobuf/timestamp"; - -import { - Button, - Form, -} from 'react-bootstrap' - -import Coins from './Coins'; -import { buildExecMessage, coin, rewardAmount } from '../utils/Helpers.mjs'; -import RevokeGrant from './RevokeGrant'; -import AlertMessage from './AlertMessage'; -import OperatorLastRestakeAlert from './OperatorLastRestakeAlert'; - -function REStakeGrantForm(props) { - const { grants, wallet, operator, address, network, lastExec } = props - const { stakeGrant, maxTokens, validators } = grants || {} - const defaultExpiry = moment().add(1, 'year') - const [loading, setLoading] = useState(false); - const [error, setError] = useState() - const [state, setState] = useReducer( - (state, newState) => ({ ...state, ...newState }), - { maxTokensValue: '', expiryDateValue: defaultExpiry.format('YYYY-MM-DD') } - ) - - const reward = rewardAmount(props.rewards, network.denom) - - useEffect(() => { - setState({ - validators: validators || (!stakeGrant && [operator.address]), - maxTokens, - expiryDate: expiryDate(), - }) - }, [grants, operator]) - - useEffect(() => { - setState({ - expiryDateValue: (expiryDate() || defaultExpiry).format('YYYY-MM-DD'), - maxTokensValue: maxTokens && state.maxTokensValue === '' ? divide(bignumber(maxTokens), pow(10, network.decimals)) : maxTokens ? state.maxTokensValue : '', - }) - }, [operator]) - - function handleInputChange(e) { - setState({ [e.target.name]: e.target.value }); - } - - function expiryDate() { - const stakeExpiry = stakeGrant && stakeGrant.expiration && moment(stakeGrant.expiration) - return stakeExpiry - } - - function maxTokensDenom() { - if (state.maxTokensValue === '') return - - const decimals = pow(10, network.decimals) - return bignumber(multiply(state.maxTokensValue, decimals)) - } - - function maxTokensValid() { - return !maxTokensDenom() || larger(maxTokensDenom(), reward) - } - - function showLoading(isLoading) { - setLoading(isLoading) - props.setLoading && props.setLoading(isLoading) - } - - function handleSubmit(event) { - event.preventDefault() - showLoading(true) - const expiry = moment(state.expiryDateValue) - let maxTokens - if (state.maxTokensValue !== '') { - maxTokens = coin(maxTokensDenom(), network.denom) - } - - const messages = [ - buildGrantMsg("/cosmos.staking.v1beta1.StakeAuthorization", - StakeAuthorization.encode(StakeAuthorization.fromPartial({ - allowList: { address: [operator.address] }, - maxTokens: maxTokens, - authorizationType: 1 - })).finish(), - expiry - ) - ] - console.log(messages) - - props.signingClient.signAndBroadcast(wallet.address, messages).then((result) => { - console.log("Successfully broadcasted:", result); - showLoading(false) - props.onGrant(operator.botAddress, { - grantee: operator.botAddress, - granter: address, - expiration: expiry, - authorization: { - '@type': "/cosmos.staking.v1beta1.StakeAuthorization", - max_tokens: maxTokens, - allow_list: { address: [operator.address] } - } - }); - }, (error) => { - console.log('Failed to broadcast:', error) - showLoading(false) - setError('Failed to broadcast: ' + error.message) - }) - } - - function buildGrantMsg(type, authValue, expiryDate) { - const value = { - granter: address, - grantee: operator.botAddress, - grant: { - authorization: { - typeUrl: type, - value: authValue - }, - expiration: Timestamp.fromPartial({ - seconds: expiryDate.unix(), - nanos: 0 - }) - } - } - if (wallet?.address !== address) { - return buildExecMessage(wallet.address, [{ - typeUrl: "/cosmos.authz.v1beta1.MsgGrant", - value: MsgGrant.encode(MsgGrant.fromPartial(value)).finish() - }]) - } else { - return { - typeUrl: "/cosmos.authz.v1beta1.MsgGrant", - value: value - } - } - } - - function grantInformation() { - return ( - <> -

    {operator.moniker} will be able to carry out the following transactions on your behalf.

    -

    Delegate - allowed to delegate {maxTokensDenom() ? : 'any amount'} to {!state.validators ? 'any validator' : !state.validators.length || (state.validators.length === 1 && state.validators.includes(operator.address)) ? 'only their own validator' : 'validators ' + state.validators.join(', ')}.

    -

    This grant will expire automatically on {state.expiryDateValue}.

    -

    REStake only re-delegates {operator.moniker}'s accrued rewards and tries not to touch your balance.

    -

    REStake previously required a Withdraw grant but this is no longer necessary.

    - - ) - } - - const step = () => { - return 1 / pow(10, network.decimals) - } - - return ( - <> - - {error && - - {error} - - } -
    -
    - -
    - - Max amount -
    -
    - - {network.symbol} -
    -
    - Reduces with every delegation made by the validator
    Leave empty for unlimited -
    -
    -
    - - Expiry date - -
    Date the grant will expire. After this date you will need to re-grant
    -
    -
    - {!loading - ? ( -
    - {props.closeForm && ( - - )} - {grants?.grantsExist && ( - showLoading(loading)} - setError={setError} - buttonText="Disable" - /> - )} - -
    - ) - : - } -
    -
    - -
    -
    - {grantInformation()} -
    -
    - - ) -} - -export default REStakeGrantForm; diff --git a/src/components/REStakeStatus.js b/src/components/REStakeStatus.js deleted file mode 100644 index 34ac7a8f2..000000000 --- a/src/components/REStakeStatus.js +++ /dev/null @@ -1,96 +0,0 @@ -import React from "react"; -import _ from "lodash"; -import TooltipIcon from "./TooltipIcon"; - -import { - Spinner, -} from 'react-bootstrap' -import { XCircle, ToggleOn, ToggleOff } from "react-bootstrap-icons"; -import { joinString } from "../utils/Helpers.mjs"; -import Coins from "./Coins"; - -function REStakeStatus(props) { - const { network, validator, operator, delegation, grants, authzSupport, className } = props - - const minimumReward = operator && { - amount: operator.minimumReward, - denom: network.denom, - }; - - let content, icon, tooltip - let styleClass = 'text-decoration-underline' - if(operator){ - let tooltipContent - if(authzSupport){ - if(delegation){ - if(grants?.grantsValid){ - let limit - if(grants.maxTokens){ - limit =
    ( remaining)
    - }else{ - limit = '(no limit)' - } - tooltipContent = Grant active {limit} - // styleClass = 'text-success' - // styleClass = 'fst-italic' - icon = - }else if(grants?.grantsExist){ - tooltipContent = 'Update grants to enable REStake' - icon = - // styleClass = 'text-danger' - }else{ - tooltipContent = 'Grant to enable REStake' - icon = - } - }else{ - tooltipContent = 'Delegate to enable REStake' - } - }else{ - tooltipContent = `Enable REStake once ${network.prettyName} supports Authz` - } - if(props.isLoading('grants')) icon = ( - - Loading... - - ) - content = ( -
    - {icon} -
    - {operator.frequency()} -
    -
    - ) - tooltip = ( -
    -

    REStakes {operator.runTimesString()}

    -

    - Minimum reward is{" "} - -

    -

    {tooltipContent}

    -
    - ) - }else{ - content = - tooltip = 'This validator is not a REStake operator' - } - - return ( - - - - ) -} - -export default REStakeStatus \ No newline at end of file diff --git a/src/components/RevokeGrant.js b/src/components/RevokeGrant.js deleted file mode 100644 index db8ffdafb..000000000 --- a/src/components/RevokeGrant.js +++ /dev/null @@ -1,72 +0,0 @@ -import _ from 'lodash' -import { - Dropdown, - Button -} from 'react-bootstrap' -import { MsgRevoke } from "cosmjs-types/cosmos/authz/v1beta1/tx"; -import { buildExecableMessage, buildExecMessage } from '../utils/Helpers.mjs' - -function RevokeGrant(props) { - const { address, wallet, grantAddress, grants, signingClient } = props - - const buttonText = props.buttonText || 'Revoke' - - async function revoke(){ - if(props.setLoading) props.setLoading(true) - - let msgTypes = _.compact(grants).map(grant => { - switch (grant.authorization['@type']) { - case "/cosmos.staking.v1beta1.StakeAuthorization": - return "/cosmos.staking.v1beta1.MsgDelegate" - case "/cosmos.authz.v1beta1.GenericAuthorization": - return grant.authorization.msg - } - }) - let messages = msgTypes.map(type => buildRevokeMsg(type)) - if(wallet?.address !== address){ - messages = [buildExecMessage(wallet.address, messages)] - } - - console.log(messages) - - try { - const gas = await signingClient.simulate(wallet.address, messages) - const result = await signingClient.signAndBroadcast(wallet.address, messages, gas) - console.log("Successfully broadcasted:", result); - if(props.setLoading) props.setLoading(false) - props.onRevoke(grantAddress, msgTypes) - } catch (error) { - console.log('Failed to broadcast:', error) - if(props.setLoading) props.setLoading(false) - props.setError('Failed to broadcast: ' + error.message) - } - } - - function buildRevokeMsg(type){ - return buildExecableMessage(MsgRevoke, "/cosmos.authz.v1beta1.MsgRevoke", { - granter: address, - grantee: grantAddress, - msgTypeUrl: type - }, wallet?.address !== address) - } - - function disabled(){ - return props.disabled || !wallet?.hasPermission(address, 'Revoke') || !wallet?.authzSupport() - } - - if(props.button){ - return ( - - ) - } - - return ( - revoke()} > - {buttonText} - - ) -} - -export default RevokeGrant; diff --git a/src/components/SavedAddresses.js b/src/components/SavedAddresses.js deleted file mode 100644 index a33e6a3fc..000000000 --- a/src/components/SavedAddresses.js +++ /dev/null @@ -1,154 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -import { - Table, - Form, - Button, - Dropdown -} from 'react-bootstrap' - -import { - PlusCircle, - Eye, - Key, - Gear -} from 'react-bootstrap-icons' -import Address from './Address.js'; -import TooltipIcon from './TooltipIcon.js'; - -function SavedAddresses(props) { - const { network, networks, favouriteAddresses, updateFavouriteAddresses } = props - const [newAddress, setNewAddress] = useState('') - const [newLabel, setNewLabel] = useState('') - const [selectedNetwork, setSelectedNetwork] = useState(network) - - const networkAddresses = (selectedNetwork && favouriteAddresses[selectedNetwork.path]) || [] - - useEffect(() => { - setSelectedNetwork(network) - }, [network]) - - function setAddress(address) { - if (selectedNetwork.path === network.path) { - props.setAddress(address) - } - } - - function updateAddress(address, label) { - updateFavouriteAddresses({ - ...favouriteAddresses, [network.path]: networkAddresses.map(data => { - if (data.address === address) { - return { ...data, label } - } - return data - }) - }) - } - - function removeAddress(address) { - updateFavouriteAddresses({ ...favouriteAddresses, [network.path]: networkAddresses.filter(data => data.address !== address) }) - } - - function addAddress(event) { - event.preventDefault() - if (!validNewAddress()) return - - updateFavouriteAddresses({ ...favouriteAddresses, [network.path]: [...networkAddresses, { address: newAddress, label: newLabel }] }) - setNewAddress('') - setNewLabel('') - } - - function validNewAddress() { - return newAddress !== '' && !networkAddresses.some(el => el.address === newAddress) - } - - return ( - <> - - {selectedNetwork && ( - <> -
    - - - - - - - - - - {networkAddresses.map(({ label, address }) => { - return ( - - - - - - ) - })} - - - - - - -
    AddressLabel
    -
    props.address !== address && setAddress(address)} as={props.address === address && 'strong'} addressClassName="flex-fill" /> -
    updateAddress(address, e.target.value)} /> -
    -
    - {props.wallet?.address === address && ( - } - identifier={`${address}-wallet`} - tooltip="This is your connected wallet" - /> - )} - {props.address === address && ( - } - identifier={`${address}-view`} - tooltip="This is the address you are viewing" - /> - )} -
    - - - - - - setAddress(address)}> - {props.address === address ? 'Viewing address' : 'View address'} - - removeAddress(address)}> - Remove saved address - - - -
    -
    setNewAddress(e.target.value)} /> setNewLabel(e.target.value)} /> -
    - -
    -
    -
    - - )} -

    Save addresses to easily find and view them, and carry out any granted actions in the REStake UI on their behalf.

    -

    Select 'View address' for any saved address to enter View mode and see REStake as they would - useful for monitoring other addresses quickly.

    -

    If a saved address has granted you any permissions, REStake will detect them and enable the relevant features in View mode. This can make it very easy to manage staking and governance for multiple wallets.

    - - ); -} - -export default SavedAddresses diff --git a/src/components/SendModal.js b/src/components/SendModal.js deleted file mode 100644 index 4db6923dd..000000000 --- a/src/components/SendModal.js +++ /dev/null @@ -1,219 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import _ from 'lodash' -import { pow, multiply, divide, subtract, bignumber } from 'mathjs' - -import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx"; - -import { - Modal, - Button, - Form, -} from 'react-bootstrap' - -import AlertMessage from './AlertMessage'; -import { buildExecableMessage, buildExecMessage, coin, truncateAddress } from '../utils/Helpers.mjs'; -import Coins from './Coins'; - -function SendModal(props) { - const { show, network, address, wallet } = props - const [loading, setLoading] = useState(false); - const [error, setError] = useState() - const [state, setState] = useState({recipientValue: '', customRecipientValue: '', memoValue: ''}); - - const denom = network && network.symbol - const step = 1 / pow(10, network?.decimals || 6) - - useEffect(() => { - setState({ - ...state, - recipientValue: '', - customRecipientValue: '', - amountValue: '', - memoValue: '', - }) - setError(null) - }, [address]) - - function handleInputChange(e) { - setState({ ...state, [e.target.name]: e.target.value }); - } - - function showLoading(isLoading) { - setLoading(isLoading) - props.setLoading && props.setLoading(isLoading) - } - - function handleSubmit(event) { - event.preventDefault() - if(!valid()) return - - showLoading(true) - - - const coinValue = coinAmount() - - const messages = [ - buildSendMsg(address, recipient(), [coinValue]) - ] - console.log(messages) - - props.signingClient.signAndBroadcast(wallet.address, messages, null, state.memoValue).then((result) => { - console.log("Successfully broadcasted:", result); - showLoading(false) - setState({ - recipientValue: '', - customRecipientValue: '', - amountValue: '', - memoValue: '', - }) - props.onSend(recipient(), coinValue); - }, (error) => { - console.log('Failed to broadcast:', error) - showLoading(false) - setError('Failed to broadcast: ' + error.message) - }) - } - - function handleClose() { - setError(null) - props.onHide(); - } - - function buildSendMsg(address, recipient, amount) { - let message = buildExecableMessage(MsgSend, "/cosmos.bank.v1beta1.MsgSend", { - fromAddress: address, - toAddress: recipient, - amount: amount - }, wallet?.address !== address) - if(wallet?.address !== address){ - return buildExecMessage(wallet.address, [message]) - } - return message - } - - async function setAvailableAmount(){ - setError(null) - const decimals = pow(10, network.decimals) - const coinValue = coin(multiply(props.balance.amount, 0.95), network.denom) - const message = buildSendMsg(address, recipient(), [coinValue]) - props.signingClient.simulate(wallet.address, [message]).then(gas => { - const gasPrice = props.signingClient.getFee(gas).amount[0].amount - const amount = divide(subtract(bignumber(props.balance.amount), gasPrice), decimals) - - setState({...state, amountValue: amount > 0 ? amount : 0}) - }, error => { - console.log(error) - setError(error.message) - }) - } - - function recipient(){ - return state.recipientValue === 'custom' ? state.customRecipientValue : state.recipientValue - } - - function coinAmount(){ - if(!state.amountValue) return null - - const decimals = pow(10, network.decimals) - const denomAmount = multiply(state.amountValue, decimals) - if(denomAmount > 0){ - return coin(denomAmount, network.denom) - } - } - - function valid(){ - if(!state.recipientValue) return true - return validRecipient() && coinAmount() && wallet?.hasPermission(address, 'Send') - } - - function validAmount(){ - if(!state.amountValue) return true - - return !!coinAmount() - } - - function validRecipient(){ - const value = state.recipientValue === 'custom' ? state.customRecipientValue : state.recipientValue - if(!value) return true; - - return !network.prefix || value.startsWith(network.prefix) - } - - function favourites(){ - return props.favouriteAddresses.filter(el => el.address !== props.address) - } - - return ( - <> - - - Send - - - {error && - - {error} - - } -
    - - Recipient - - {state.recipientValue === 'custom' && ( - - )} - - {recipient() && ( - <> - - Amount -
    -
    - - {denom} -
    - {props.balance && -
    setAvailableAmount()}> - Available: -
    - } -
    -
    - - Memo - - -

    - {!loading - ? ( - - ) - : - } -

    - - )} -
    -
    -
    - - ); -} - -export default SendModal diff --git a/src/components/TooltipIcon.js b/src/components/TooltipIcon.js deleted file mode 100644 index 0699bade4..000000000 --- a/src/components/TooltipIcon.js +++ /dev/null @@ -1,25 +0,0 @@ -import { - OverlayTrigger, - Tooltip -} from 'react-bootstrap' - -function TooltipIcon(props) { - if(!props.tooltip && !props.children) return props.icon - if(!props.icon && !props.children) return null - return ( - <> - - {props.tooltip || props.children} - - } - >{props.icon || props.children} - - ) -} - -export default TooltipIcon diff --git a/src/components/ValidatorImage.js b/src/components/ValidatorImage.js deleted file mode 100644 index 45d793be7..000000000 --- a/src/components/ValidatorImage.js +++ /dev/null @@ -1,16 +0,0 @@ -function ValidatorImage(props) { - const { validator } = props - const className = props.className || '' - const defaultSize = !props.width || props.width > 30 ? 'lg' : 'sm' - const imageUrl = validator.mintscan_image || validator.keybase_image - return ( - <> - {imageUrl && imageUrl !== '' - ? - : - } - - ); -} - -export default ValidatorImage diff --git a/src/components/ValidatorLink.js b/src/components/ValidatorLink.js deleted file mode 100644 index d707729a4..000000000 --- a/src/components/ValidatorLink.js +++ /dev/null @@ -1,26 +0,0 @@ -import ValidatorName from "./ValidatorName" - -function ValidatorLink(props) { - let validator = props.validator - if(!validator && props.operator) validator = props.operator - if(!validator) return props.fallback || null - - const website = () => { - let url = validator.description && validator.description.website - if(!url) return - - return url.startsWith('http') ? url : ('https://' + url) - } - - if(!website()){ - return props.children || - } - - return ( - - {props.children || } - - ); -} - -export default ValidatorLink diff --git a/src/components/ValidatorModal.js b/src/components/ValidatorModal.js deleted file mode 100644 index b67ba3551..000000000 --- a/src/components/ValidatorModal.js +++ /dev/null @@ -1,211 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useParams, useNavigate } from "react-router-dom"; -import moment from 'moment' - -import ValidatorImage from './ValidatorImage' -import ValidatorLink from './ValidatorLink' -import AboutLedger from './AboutLedger'; - -import { - Modal, - Tab, - Nav, - Button -} from 'react-bootstrap' - -import ValidatorStake from './ValidatorStake'; -import ValidatorProfile from './ValidatorProfile'; - -function ValidatorModal(props) { - const { validator, delegations, operators, network, validators, grants } = props - const navigate = useNavigate() - const params = useParams(); - const [activeTab, setActiveTab] = useState(params.section || 'profile'); - const [registryData, setRegistryData] = useState({}) - const [lastExec, setLastExec] = useState() - - let operator - if (operators && validator){ - operator = network.getOperator(validator.operator_address); - } - - useEffect(() => { - setRegistryData() - setLastExec() - }, [validator]); - - useEffect(() => { - if(registryData == null){ - getRegistryData() - const interval = setInterval(() => { - getRegistryData() - }, 15_000); - return () => clearInterval(interval); - } - }, [validator, registryData]); - - useEffect(() => { - if(lastExec == null){ - getLastExec(2) - const interval = setInterval(() => { - getLastExec() - }, 60_000); - return () => clearInterval(interval); - } - }, [operator, lastExec]) - - useEffect(() => { - if(params.network !== network.name) return - - const shouldShow = props.show && validator - const shouldChangeValidator = params.validator !== validator?.operator_address - const shouldChangeTab = activeTab === 'profile' ? !!params.section : params.section !== activeTab - const shouldChangeUrl = shouldShow && (shouldChangeValidator || shouldChangeTab) - if (shouldChangeUrl) { - navigate(`/${network.name}/${validator.operator_address}${activeTab === 'profile' ? '' : `/${activeTab}`}`) - } else if (params.validator && props.show === false) { - navigate(`/${network.name}`) - } - }, [props.show, activeTab, validator]) - - useEffect(() => { - if (props.activeTab && props.activeTab != activeTab) { - setTab(props.activeTab) - } else if (params.section && params.section != activeTab) { - const section = ['delegate', 'restake'].includes(params.section) ? 'stake' : params.section - setTab(section) - } else if (!activeTab || !props.show) { - setTab('profile') - } - }, [props.show]) - - function setTab(tab){ - setActiveTab(tab || 'profile') - } - - function handleClose() { - props.hideModal(); - } - - function getRegistryData(){ - if(validator?.path && network.directory){ - network.directory.getRegistryValidator(validator.path).then(data => { - setRegistryData(data) - }) - }else{ - setRegistryData({}) - } - } - - function getLastExec(emptyResponseRetries) { - if(!operator || !network.authzSupport){ - setLastExec() - return - } - - network.queryClient.getTransactions([ - { key: 'events', value: `message.action='/cosmos.authz.v1beta1.MsgExec'` }, - { key: 'events', value: `message.sender='${operator.botAddress}'` } - ], { - pageSize: 1, - order: 2, - retries: 3, - timeout: 15_000 - }).then(data => { - if (data.tx_responses?.length > 0) { - setLastExec(moment(data.tx_responses[0].timestamp)) - } else if(emptyResponseRetries && lastExec == null) { - getLastExec(emptyResponseRetries - 1) - } else if(lastExec == null) { - setLastExec(false) - } - }).catch(error => console.log(error)) - } - - return ( - <> - - - -
    - - -
    -
    -
    - - {validator && ( - setTab(k)} id="validator-tabs"> - - - - - - - - - {network.authzSupport && !network.authzAminoSupport && operator && ( - - - - )} - - - )} - {activeTab === 'profile' && ( -
    - -
    - )} -
    -
    - - ); -} - -export default ValidatorModal diff --git a/src/components/ValidatorName.js b/src/components/ValidatorName.js deleted file mode 100644 index fce86d5d7..000000000 --- a/src/components/ValidatorName.js +++ /dev/null @@ -1,11 +0,0 @@ -function ValidatorName(props) { - const { validator, fallback } = props - if(!validator) return fallback || null - const moniker = validator.description?.moniker || validator.name || 'Unknown' - - return ( - {moniker} - ); -} - -export default ValidatorName \ No newline at end of file diff --git a/src/components/ValidatorNetworks.js b/src/components/ValidatorNetworks.js deleted file mode 100644 index 774162b15..000000000 --- a/src/components/ValidatorNetworks.js +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import _ from 'lodash' -import { - OverlayTrigger, - Tooltip, - Spinner -} from 'react-bootstrap' -import { Link } from 'react-router-dom' -import NetworkImage from './NetworkImage' - -function ValidatorNetworks(props) { - const { validator, registryData, network, networks } = props - const [networkLoading, setNetworkLoading] = useState() - - useEffect(() => { - if(network){ - setNetworkLoading(null) - } - }, [network]); - - if(!registryData?.chains) return null - - const validatorNetworks = _.uniqBy(_.compact(registryData.chains.map(chain => { - const chainNetwork = networks[chain.name] - if(chainNetwork){ - const { online } = chainNetwork - const active = network.path === chainNetwork.path - return { - key: chain.name, - name: chainNetwork.prettyName, - image: , - path: `/${chainNetwork.path}/${chain.address}`, - active, - online - } - } - })), 'key').sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1) - - return ( -
    - {validatorNetworks.map(validatorNetwork => { - return ( - {validatorNetwork.name}{!validatorNetwork.online && ' (API Offline)'} - } - > - {!validatorNetwork.online || validatorNetwork.active ? ( - {validatorNetwork.image} - ) : ( - setNetworkLoading(validatorNetwork.path)}> - {validatorNetwork.image} - - )} - - ) - })} - {networkLoading && ( - - Loading... - - )} -
    - ); -} - -export default ValidatorNetworks diff --git a/src/components/ValidatorProfile.js b/src/components/ValidatorProfile.js deleted file mode 100644 index 0665c6b5e..000000000 --- a/src/components/ValidatorProfile.js +++ /dev/null @@ -1,190 +0,0 @@ -import _ from 'lodash' -import React from 'react'; -import { round } from 'mathjs' - -import ValidatorLink from './ValidatorLink' -import Coins from './Coins' -import TooltipIcon from './TooltipIcon' - -import { - Table, - Spinner, -} from 'react-bootstrap' - -import { - XCircle, - HeartPulse -} from 'react-bootstrap-icons' -import ValidatorServices from './ValidatorServices'; -import ValidatorNetworks from './ValidatorNetworks'; -import OperatorLastRestake from './OperatorLastRestake'; -import Address from './Address'; -import ValidatorStatus from './ValidatorStatus' - -function ValidatorProfile(props) { - const { validator, operator, network, networks, registryData, lastExec } = props - - const minimumReward = () => { - return { - amount: operator.minimumReward, - denom: network.denom - } - } - - function uptime() { - if(!validator.uptime) return - - const period = validator.uptime_periods.slice(-1)[0] - const blockPeriod = validator.missed_blocks_periods.slice(-1)[0] - return {_.round(period.uptime * 100, 2)}% (missed {blockPeriod.missed.toLocaleString()} of {blockPeriod.blocks.toLocaleString()} blocks) - } - - return ( - <> -
    -
    - - - - - - - {!validator.active && ( - - - - - )} - {uptime() && ( - - - - - )} - - - - - {network.apyEnabled && ( - - - - - )} - - - - - - - - - - - - - -
    Validator Address
    Status
    Uptime{uptime()}
    REStake - {!!operator ? ( - - - - - - - - - - - {network.authzSupport && ( - - - - - )} - -
    Frequency{operator.runTimesString()}
    Minimum rewards
    Last REStake - -
    - ) : - } identifier={validator.operator_address} tooltip="This validator is not a REStake operator" /> - } -
    - APY} - identifier="delegations-apy" - > -
    -

    Based on commission, compounding frequency and estimated block times.

    -

    This is an estimate and best case scenario.

    -
    -
    -
    - {props.validatorApy[validator.operator_address] - ? {round(props.validatorApy[validator.operator_address] * 100, 2).toLocaleString()}% - : "-" - } -
    Commission{validator.commission.commission_rates.rate * 100}%
    Rank#{validator.rank}
    Voting power
    -
    -
    - - - - - - - {!!validator.description?.website && ( - - - - - )} - - - - - {validator?.path && ( - - - - - )} - -
    Contact{validator.description?.security_contact}
    Website{validator.description.website}
    Profiles - -
    Networks - -
    -

    - {validator.description?.details} -

    - {Object.entries(validator.public_nodes || {}).length > 0 && ( - <> -

    Public Nodes

    - - - {Object.entries(validator.public_nodes).map(([type, nodes]) => { - return ( - - - - - ) - })} - -
    - {type.toUpperCase()} - - {nodes.map(api => { - return {api.address} - }).reduce((prev, curr) => [prev,
    , curr])} -
    - - )} -
    -
    - - ) -} - -export default ValidatorProfile \ No newline at end of file diff --git a/src/components/ValidatorServices.js b/src/components/ValidatorServices.js deleted file mode 100644 index 5b986ea61..000000000 --- a/src/components/ValidatorServices.js +++ /dev/null @@ -1,111 +0,0 @@ -import { Link } from 'react-router-dom' -import { - OverlayTrigger, - Tooltip -} from 'react-bootstrap' -import { - HeartPulse -} from 'react-bootstrap-icons' - -import StakingRewardsIcon from '../assets/staking-rewards.svg' -import StakingRewardsWhiteIcon from '../assets/staking-rewards-white.svg' -import StakingRewardsVerifiedIcon from '../assets/staking-rewards-verified.svg' -import StakingRewardsVerifiedWhiteIcon from '../assets/staking-rewards-verified-white.svg' -import Cosmostation from '../assets/cosmostation.svg' -import PingPub from '../assets/ping-pub.svg' -import NodesGuru from '../assets/nodes-guru.svg' - -function ValidatorServices(props) { - const { validator, network, theme } = props - - const services = [] - - if(Object.entries(validator.public_nodes || {}).length){ - services.push({ - key: 'nodes', - tooltip: 'Provides public nodes used by REStake and other apps', - render: () => { - return ( - - - - ) - } - }) - } - - if(validator.services?.staking_rewards){ - let verified = validator.services.staking_rewards.verified - let icon = verified ? StakingRewardsVerifiedIcon : StakingRewardsIcon - if(theme === 'dark') icon = verified ? StakingRewardsVerifiedWhiteIcon : StakingRewardsWhiteIcon - services.push({ - key: 'stakingrewards', - tooltip: Staking Rewards
    {verified ? 'Verified' : ''} Provider
    , - icon: icon, - url: `https://www.stakingrewards.com/savings/${validator.services.staking_rewards.slug}` - }) - } - - const mintscan = network.chain.explorers?.find(el => el.kind === 'mintscan') - if(mintscan){ - services.push({ - key: 'mintscan', - tooltip: 'Mintscan Profile', - icon: Cosmostation, - url: `${mintscan.url}/validators/${validator.address}` - }) - } - - const ping = network.chain.explorers?.find(el => el.kind === 'ping.pub') - if(ping){ - services.push({ - key: 'pingpub', - tooltip: 'Ping.pub Profile', - icon: PingPub, - url: `${ping.url}/staking/${validator.address}` - }) - } - - const guru = network.chain.explorers?.find(el => el.kind === 'explorers.guru') - if(guru){ - services.push({ - key: 'nodesguru', - tooltip: '[NG] Explorers Profile', - icon: NodesGuru, - url: `${guru.url}/validator/${validator.address}` - }) - } - - const showServices = services.filter(el => { - if(props.show && !props.show.includes(el.key)) return false - if(props.exclude && props.exclude.includes(el.key)) return false - return true - }) - - if(!showServices.length) return null - - return ( -
    - {showServices.map(service => { - return ( - {service.tooltip} - } - > - {service.render ? service.render() : ( - - - - )} - - ) - })} -
    - ); -} - -export default ValidatorServices diff --git a/src/components/ValidatorStake.js b/src/components/ValidatorStake.js deleted file mode 100644 index 7bff5bc5a..000000000 --- a/src/components/ValidatorStake.js +++ /dev/null @@ -1,506 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { larger, smaller, round } from 'mathjs' -import moment from 'moment' - -import { - Table, - Dropdown, - Button, - Spinner -} from 'react-bootstrap' -import { ChevronLeft, CheckCircle, XCircle, QuestionCircle } from "react-bootstrap-icons"; - -import Coins from './Coins'; -import TooltipIcon from './TooltipIcon' - -import DelegateForm from './DelegateForm' -import { rewardAmount } from '../utils/Helpers.mjs'; -import ClaimRewards from './ClaimRewards'; -import Validators from './Validators' -import AlertMessage from './AlertMessage' -import REStakeGrantForm from './REStakeGrantForm'; -import Address from './Address'; -import OperatorLastRestake from './OperatorLastRestake'; -import CountdownRestake from './CountdownRestake'; -import RevokeGrant from './RevokeGrant'; -import ValidatorStatus from './ValidatorStatus' - -function ValidatorStake(props) { - const { network, validator, operator, balance, wallet, address, lastExec } = props - const [action, setAction] = useState(props.action) - const [selectedValidator, setSelectedValidator] = useState(); - const [loading, setLoading] = useState(false) - const [error, setError] = useState() - - const delegation = props.delegations && props.delegations[validator.address] - const validatorRewards = props.rewards && props.rewards[validator.address] - const reward = rewardAmount(validatorRewards, network.denom) - const validatorCommission = props.commission && props.commission[validator.address] - const commission = rewardAmount(validatorCommission, network.denom, 'commission') - const validatorGrants = props.grants && operator && props.grants[operator.botAddress] - const { grantsValid, grantsExist, maxTokens, stakeGrant } = validatorGrants || {} - - useEffect(() => { - setAction(props.action) - }, [props.action]) - - useEffect(() => { - setError() - props.onChangeAction && props.onChangeAction(action) - }, [action]) - - function actionText() { - if (action === 'redelegate') { - if (selectedValidator) { - return `Redelegate to ${selectedValidator.moniker}` - } - return 'Redelegate to...' - } - if (action === 'undelegate') return `Undelegate from ${validator.moniker}` - if (action === 'grant') return `${grantsValid ? 'Manage' : 'Enable'} ${validator.moniker} REStake` - return `Delegate to ${validator.moniker}` - } - - function onDelegate() { - setAction() - props.onDelegate() - } - - function onGrant(grantee, grant) { - setAction() - props.onGrant(grantee, grant) - } - - function onRevoke(grantee, msgTypes) { - setAction() - props.onRevoke(grantee, msgTypes) - } - - const minimumReward = () => { - return { - amount: operator.minimumReward, - denom: network.denom - } - } - - function expiryDate() { - const stakeExpiry = stakeGrant && stakeGrant.expiration && moment(stakeGrant.expiration) - return stakeExpiry - } - - return ( - <> - {error && - - {error} - - } - {action ? ( - <> -
    - {action && ( - { - action === 'redelegate' && selectedValidator ? setSelectedValidator() : setAction() - }} /> - )} -
    {actionText()}
    -
    - {action === 'redelegate' && !selectedValidator ? ( - <> - -
    - -
    - - ) : action === 'grant' ? ( - setAction()} - onGrant={onGrant} - onRevoke={onRevoke} - /> - ) : ( - setAction()} - onDelegate={onDelegate} /> - )} - - ) : ( - <> -
    -
    - - - {!validator.active && ( - - - - - )} - {network.apyEnabled && ( - <> - - - - - {operator && ( - - - - - )} - - )} - {address && ( - - - - - )} - {delegation?.balance?.amount && ( - - - - - )} - {!!commission && ( - - - - - )} - -
    Status
    - APR - -
    - {round(validator.getAPR() * 100, 2).toLocaleString()}% - } - identifier="delegations-apr" - > -
    -

    {round(network.chain.estimatedApr * 100, 2).toLocaleString()}% staking APR
    - {validator.commissionRate * 100}% commission

    -

    Staking APR is calculated based on recent block time and is not guaranteed.

    -
    -
    -
    -
    - APY - -
    - {round(validator.getAPY(operator) * 100, 2).toLocaleString()}% - } - identifier="delegations-apy" - > -
    -

    {round(validator.getAPR() * 100, 2).toLocaleString()}% APR compounded {operator.frequency(true)} by {validator.moniker}.

    -

    This is an estimate and best case scenario.

    -
    -
    -
    -
    Delegation - {!props.isLoading('delegations') ? ( - - ) : ( - - Loading... - - )} -
    Rewards - {!props.isLoading('rewards') ? ( - - ) : ( - - Loading... - - )} -
    Commission - {!props.isLoading('commission') ? ( - - ) : ( - - Loading... - - )} -
    -
    -
    - - - - - - - {operator && ( - <> - - - - - - - - - {network.authzSupport && ( - <> - - - - - - - - - - )} - - - - - {props.address && ( - <> - - - - - {grantsExist && ( - - - - - )} - - )} - - )} - -
    REStake - {operator ? ( - } - identifier={validator.operator_address} - tooltip="This validator can REStake your rewards" - /> - ) : ( - } - identifier={validator.operator_address} - tooltip="This validator is not a REStake operator" - /> - )} -
    Frequency - {operator.runTimesString()} -
    Minimum Reward - -
    Last REStake - -
    Next REStake - -
    REStake Address
    Grant Status - {!props.isLoading('grants') ? ( - grantsValid - ? Active
    expires {expiryDate().fromNow()}
    - : grantsExist - ? validatorGrants.validators && !validatorGrants.validators.includes(validator.address) - ? Grant invalid - : maxTokens && smaller(maxTokens, reward) - ? Not enough grant remaining - : Invalid / total delegation reached - : network.authzSupport ? Inactive : Authz not supported - ) : ( - - Loading... - - )} -
    Grant Remaining - {maxTokens ? ( - - ) : ( - 'Unlimited' - )} -
    -
    -
    -
    - {delegation?.balance?.amount && wallet && ( - !loading ? ( - - - Manage - - - - setLoading(loading) - } - setError={setError} - /> - - setLoading(loading) - } - setError={setError} - /> - {!!commission && ( - - setLoading(loading) - } - setError={setError} - /> - )} -
    - { - setSelectedValidator() - setAction('redelegate') - }}> - Redelegate - - setAction('undelegate')}> - Undelegate - -
    -
    - ) : ( - - ) - )} - - -
    - } - identifier={validator.operator_address} - tooltip={ - !wallet ? `Connect a wallet to delegate` - : !wallet?.hasPermission(address, 'Delegate') && `You don't have permission to do that.` - } - /> - {operator && grantsValid ? ( - - - REStake - - - { - setAction('grant') - }}> - Manage Grant - - - setLoading(loading) - } - setError={setError} /> - - - ) : operator && ( - - -
    - } - identifier={validator.operator_address} - tooltip={ - !network.authzSupport ? `${props.network.prettyName} doesn't support Authz just yet` - : !wallet ? `Connect a wallet to enable REStake` - : !wallet.authzSupport() ? `${wallet.getIsNanoLedger() ? 'Ledger devices' : 'This wallet'} can't send Authz transactions on ${network.prettyName} yet` - : !delegation?.balance?.amount ? `You must delegate to ${validator.moniker} before they can REStake for you.` - : !wallet?.hasPermission(address, 'Grant') && `You don't have permission to do that.` - } - /> - )} -
    - - )} - - ) -} - -export default ValidatorStake \ No newline at end of file diff --git a/src/components/ValidatorStatus.js b/src/components/ValidatorStatus.js deleted file mode 100644 index e2ca8a09c..000000000 --- a/src/components/ValidatorStatus.js +++ /dev/null @@ -1,11 +0,0 @@ -function ValidatorStatus(props) { - const { validator } = props - if (!validator) return null - - const status = validator.active ? 'Active' : validator.jailed ? 'Jailed' : validator.active != null ? 'Inactive' : 'Unknown' - let className = validator.active ? '' : 'text-danger' - - return {status} -} - -export default ValidatorStatus \ No newline at end of file diff --git a/src/components/Validators.js b/src/components/Validators.js deleted file mode 100644 index 7e4b446fa..000000000 --- a/src/components/Validators.js +++ /dev/null @@ -1,466 +0,0 @@ -import React, { useState, useReducer, useEffect } from 'react'; -import _ from 'lodash' -import FuzzySearch from 'fuzzy-search' -import { round } from 'mathjs' - -import { format, add } from 'mathjs' - -import Coins from "./Coins"; -import ValidatorImage from './ValidatorImage' -import TooltipIcon from './TooltipIcon' - -import { - Table, - Button, - Nav, - Spinner, - Badge -} from 'react-bootstrap' -import { XCircle } from "react-bootstrap-icons"; - -import ValidatorName from "./ValidatorName"; -import ValidatorServices from './ValidatorServices'; -import REStakeStatus from './REStakeStatus'; - -function Validators(props) { - const { address, wallet, network, validators, operators, delegations, operatorGrants } = props - - const [filter, setFilter] = useReducer( - (state, newState) => ({ ...state, ...newState }), - {keywords: '', status: 'active', group: 'delegated'} - ) - const [results, setResults] = useState([]) - - const showCommission = results && Object.values(results).find(el => el.isValidatorOperator(address)) - - useEffect(() => { - if(delegations && filter.group !== 'delegated'){ - return setFilter({ group: 'delegated' }) - } - }, [Object.keys(delegations || {}).length]); - - useEffect(() => { - let filtered = filteredValidators(validators, filter) - let group = filter.group - while(filtered.length < 1 && group !== 'all'){ - group = 'all' - filtered = filteredValidators(validators, {...filter, group}) - if(filtered.length > 0 || group === 'all'){ - return setFilter({ group }) - } - } - setResults(filtered) - }, [validators, operators, delegations, operatorGrants, filter]); - - function sortValidators(validators){ - validators = _.sortBy(validators, ({ operator_address: address }) => { - const delegation = delegations && delegations[address] - return 0 - (delegation?.balance?.amount || 0) - }); - return _.sortBy(validators, ({ operator_address: address, public_nodes }) => { - if(network.data.ownerAddress === address) return -5 - - const delegation = delegations && delegations[address] - const publicNodes = Object.entries(public_nodes || {}).length > 0 - const operator = operators && operators.find(el => el.address === address) - - let score = 1 - if (delegation) { - score = operator ? -3 : -2 - } else if(operator){ - score = -1 - } - if(publicNodes) score-- - return score - }); - } - - function filterValidators(event){ - setFilter({keywords: event.target.value}) - } - - function filteredValidators(validators, filter){ - let searchResults - if (props.exclude){ - searchResults = Object.values(_.omit(validators, props.exclude)) - }else{ - searchResults = Object.values(validators) - } - const { keywords, status, group } = filter - - if(status){ - searchResults = searchResults.filter(result => { - if (status === 'active') { - return result.status === 'BOND_STATUS_BONDED' - } else if (status === 'inactive') { - return result.status !== 'BOND_STATUS_BONDED' - } else { - return true - } - }) - } - - searchResults = filterByGroup(searchResults, group) - - if (!keywords || keywords === '') return sortValidators(searchResults) - - const searcher = new FuzzySearch( - searchResults, ['description.moniker'], - { sort: true } - ) - - return searcher.search(keywords) - } - - function filterByGroup(validators, group){ - switch (group) { - case 'delegated': - validators = validators.filter(({operator_address: address}) => { - return delegations && delegations[address] - }) - break; - } - return validators - } - - function operatorForValidator(validatorAddress) { - return operators.find((el) => el.address === validatorAddress); - } - - function renderValidator(validator) { - const validatorAddress = validator.operator_address - const delegation = delegations && delegations[validatorAddress]; - const validatorOperator = validator.isValidatorOperator(address) - const rewards = - props.rewards && props.rewards[validatorAddress]; - const denomRewards = rewards && rewards.reward.find( - (reward) => reward.denom === network.denom - ); - const commission = props.commission && props.commission[validatorAddress] - const denomCommission = commission && commission.commission.find( - (commission) => commission.denom === network.denom - ); - const operator = operatorForValidator(validatorAddress); - const grants = operator && operatorGrants[operator.botAddress] - - let rowVariant - if (validatorOperator) rowVariant = 'table-info' - - const delegationBalance = (delegation && delegation.balance) || { - amount: 0, - denom: network.denom, - }; - - let badge - if (validator.jailed) { - badge = { bg: 'danger', text: 'Jailed' } - } else if (!validator.active) { - badge = { bg: 'light', text: 'Inactive' } - } - - return ( - - - - - -
    props.showValidator(validator, { activeTab: 'profile' })}> -
    - - {badge ? {badge.text} : null} -
    #{validator.rank}
    -
    -
    - - - props.showValidator(validator, { activeTab: 'stake' })} - /> - - {network.apyEnabled && ( - - props.showValidator(validator, { activeTab: 'stake' })}> - {props.validatorApy[validatorAddress] !== undefined - ? {round(props.validatorApy[validatorAddress] * 100, 1).toLocaleString() + "%"} - : "-" - } - - - )} - - {format(validator.commission.commission_rates.rate * 100, 2)}% - - {props.isLoading('delegations') || Object.keys(delegations || {}).length ? ( - - {!props.isLoading('delegations') ? ( - delegationBalance?.amount ? ( -
    props.showValidator(validator, { activeTab: 'stake' })}> - - - -
    - ) : null - ) : ( - - Loading... - - )} - - ) : null} - {filter.group === 'delegated' && ( - <> - {!props.modal && ( - - {!props.isLoading('rewards') ? denomRewards && ( -
    props.showValidator(validator, { activeTab: 'stake' })}> - - - -
    - ) : ( - - Loading... - - )} - - )} - - )} - {!props.modal && showCommission && ( - - {!props.isLoading('commission') ? ( -
    props.showValidator(validator, { activeTab: 'stake' })}> - - - -
    - ) : ( - - Loading... - - )} - - )} - {!props.modal && ( - -
    - -
    - - )} - -
    - {props.buttonText ? ( - - ) : props.manageControl ? props.manageControl({validator, operator, delegation, rewards, grants, filter}) : null} -
    - - - ); - } - - return ( - <> -
    -
    -
    - - - - -
    -
    -
    -
    - - - - -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - {results.length > 0 && - - - - - - {network.apyEnabled && ( - - )} - - {props.isLoading('delegations') || Object.keys(delegations || {}).length ? ( - - ) : null} - {filter.group === 'delegated' && ( - <> - {!props.modal && ( - - )} - - )} - {!props.modal && showCommission && ( - - )} - {!props.modal && ( - - )} - - - - - {results.map(item => renderValidator(item))} - - - - - - {network.apyEnabled && ( - - )} - - {props.isLoading('delegations') || Object.keys(delegations || {}).length ? ( - - ) : null} - {filter.group === 'delegated' && ( - <> - {!props.modal && ( - - )} - - )} - {!props.modal && showCommission && ( - - )} - {!props.modal && ( - - )} - - - -
    ValidatorREStake - APY} - identifier="delegations-apy" - > -
    -

    Based on commission, compounding frequency and recent block times.

    -

    This is a best case scenario and may not be 100% accurate.

    -
    -
    -
    FeeDelegationRewardsCommission
    - - { - const delegation = delegations && delegations[result.operator_address] - if (!delegation) return sum - - return add(sum, delegation.balance.amount) - }, 0), - denom: network.denom - }} - asset={network.baseAsset} - precision={3} - /> - - - {props.rewards && ( - - { - const reward = props.rewards[result.operator_address]?.reward?.find(el => el.denom === network.denom) - if (!reward) return sum - - return add(sum, reward.amount) - }, 0), - denom: network.denom - }} - asset={network.baseAsset} - precision={3} - /> - - )} - - {props.commission && ( - - { - const commission = props.commission[result.operator_address]?.commission?.find(el => el.denom === network.denom) - if (!commission) return sum - - return add(sum, commission.amount) - }, 0), - denom: network.denom - }} - asset={network.baseAsset} - precision={3} - /> - - )} -
    - } - {results.length < 1 && -

    No validators found

    - } - - ) -} - -export default Validators; diff --git a/src/components/VoteForm.js b/src/components/VoteForm.js deleted file mode 100644 index a5e5ecd5c..000000000 --- a/src/components/VoteForm.js +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import _ from 'lodash' - -import { - Form, - Button -} from 'react-bootstrap' -import { MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx.js"; - -import Vote from '../utils/Vote.mjs'; -import { buildExecMessage } from '../utils/Helpers.mjs'; - - -function VoteForm(props) { - const { proposal, vote, address, wallet, granter, setError } = props - const { proposal_id } = proposal - - const choices = { - 'VOTE_OPTION_YES': 'Yes', - 'VOTE_OPTION_NO': 'No', - 'VOTE_OPTION_NO_WITH_VETO': 'No with Veto', - 'VOTE_OPTION_ABSTAIN': 'Abstain' - } - - const choiceIndices = { - 'VOTE_OPTION_YES': 1, - 'VOTE_OPTION_NO': 3, - 'VOTE_OPTION_NO_WITH_VETO': 4, - 'VOTE_OPTION_ABSTAIN': 2 - } - - const [choice, setChoice] = useState(vote?.option) - const [loading, setLoading] = useState() - - useEffect(() => { - setChoice(vote?.option) - }, [vote]) - - function handleVoteChange(event) { - setChoice(event.target.name) - } - - function handleSubmit(event) { - event.preventDefault(); - - if (!choice) { - return setError('Please choose an option') - } - - setLoading(true) - setError() - - const newVote = Vote({ - proposal_id: proposal_id, - voter: granter || wallet.address, - option: choice, - options: [ - { - choice, - weight: "1.000000000000000000" - } - ] - }) - - let message - const value = { - proposalId: proposal.proposal_id, - voter: newVote.voter, - option: newVote.optionValue - } - if(granter){ - message = buildExecMessage(wallet.address, [{ - typeUrl: "/cosmos.gov.v1beta1.MsgVote", - value: MsgVote.encode(MsgVote.fromPartial(value)).finish() - }]) - }else{ - message = { - typeUrl: "/cosmos.gov.v1beta1.MsgVote", - value: value - } - } - - console.log(message) - - props.signingClient.signAndBroadcast(wallet.address, [message]).then((result) => { - console.log("Successfully broadcasted:", result); - setLoading(false) - setError(null) - props.onVote(proposal, newVote) - }, (error) => { - console.log('Failed to broadcast:', error) - setLoading(false) - setError(error.message) - }) - } - - const voteChanged = vote && vote.option !== choice - - function canVote() { - if (!wallet?.address || !proposal.isVoting) return false - if (!hasPermission()) return false - - return choice && (!vote || (vote && voteChanged)) - } - - function hasPermission(){ - return wallet && wallet.hasPermission(granter || wallet.address, 'Vote') - } - - function buttonText() { - if (!proposal.isVoting) return proposal.isDeposit ? 'Voting not started' : 'Voting ended' - - return vote ? voteChanged ? 'Change vote' : 'Voted' : 'Vote' - } - - - return ( - <> -
    - -
    - {_.chunk(Object.entries(choices), 2).map((group, index) => { - return ( -
    - {group.map(([key, value]) => { - const voteChoice = vote && key === vote.option - return ( -
    - - - - {voteChoice ? {value} : value} - - -
    - ) - })} -
    - ) - }).flat()} -
    -
    -

    - {!loading - ? ( - - ) : ( - - )} -

    -
    - - ) -} - -export default VoteForm \ No newline at end of file diff --git a/src/components/Voting.js b/src/components/Voting.js deleted file mode 100644 index 2a1d70e4c..000000000 --- a/src/components/Voting.js +++ /dev/null @@ -1,214 +0,0 @@ -import React, { useState, useEffect, useReducer } from 'react'; -import _ from 'lodash' - -import { - Spinner, -} from 'react-bootstrap' -import { useParams, useNavigate } from "react-router-dom"; - -import AlertMessage from './AlertMessage'; -import Proposals from './Proposals'; -import { executeSync } from '../utils/Helpers.mjs'; -import ProposalModal from './ProposalModal'; -import Proposal from '../utils/Proposal.mjs'; -import Vote from '../utils/Vote.mjs'; - -function Voting(props) { - const { address, wallet, network } = props - const [showModal, setShowModal] = useState() - const [proposal, setProposal] = useState() - const [proposals, setProposals] = useState() - const [tallies, setTallies] = useReducer( - (tallies, newTallies) => (!newTallies ? {} : {...tallies, ...newTallies}), - {} - ) - const [votes, setVotes] = useReducer( - (votes, newVotes) => (!newVotes ? {} : {...votes, ...newVotes}), - {} - ) - const [error, setError] = useState() - const navigate = useNavigate(); - const params = useParams(); - - const voteGrants = (wallet?.grants || []).filter(grant => { - return grant.authorization['@type'] === '/cosmos.authz.v1beta1.GenericAuthorization' && - grant.authorization.msg === '/cosmos.gov.v1beta1.MsgVote' - }) - - useEffect(() => { - setProposals(false) - setTallies(false) - setVotes(false) - setError(false) - getProposals({clearExisting: true}) - }, [network]); - - useEffect(() => { - const interval = setInterval(() => { - getProposals() - }, 120_000); - return () => clearInterval(interval); - }, [network]); - - useEffect(() => { - if(!proposals || !params.proposalId) return - const prop = proposals.find(el => el.proposal_id === params.proposalId) - if(prop){ - showProposal(prop) - }else if(proposal){ - setProposal(null) - closeProposal() - } - }, [proposals, params.proposalId]) - - useEffect(() => { - if(!proposals) return - - getTallies(proposals) - - if(address){ - getVotes(proposals) - } - }, [proposals]); - - useEffect(() => { - setVotes(false) - if(proposals && address){ - getVotes(proposals, {clearExisting: true}) - } - }, [address]); - - async function getProposals(opts) { - if(!props.queryClient) return - const { clearExisting } = opts || {} - - try { - let newProposals = await props.queryClient.getProposals() - newProposals = newProposals.map(el => Proposal(el)) - setError() - setProposals(sortProposals(newProposals)) - setTallies(newProposals.reduce((sum, proposal) => { - if (!_.every(Object.values(proposal.final_tally_result), el => el === '0')) { - sum[proposal.proposal_id] = proposal.final_tally_result - } - return sum - }, {})) - } catch (error) { - if (!proposals || clearExisting) setProposals([]) - setError(`Failed to load proposals: ${error.message}`); - } - } - - async function getTallies(proposals) { - const calls = proposals.map((proposal) => { - return () => { - const { proposal_id, final_tally_result: result } = proposal - if (tallies[proposal_id]) return - - const talliesInvalid = _.every(Object.values(result), el => el === '0') - if (proposal.isVoting && talliesInvalid) { - return props.queryClient.getProposalTally(proposal_id).then(result => { - return setTallies({ [proposal_id]: result.tally }) - }) - } - } - }); - - await executeSync(calls, 2) - }; - - async function getVotes(proposals, opts) { - const { clearExisting } = opts || {} - const calls = proposals.filter(el => el.status === 'PROPOSAL_STATUS_VOTING_PERIOD').map((proposal) => { - return () => { - const { proposal_id } = proposal - if (votes[proposal_id] && !clearExisting) return - - return props.queryClient.getProposalVote(proposal_id, address).then(result => { - return setVotes({ [proposal_id]: Vote(result.vote) }) - }).catch(error => { }) - } - }); - - await executeSync(calls, 2) - }; - - function showProposal(proposal){ - setProposal(proposal) - setShowModal(true) - if (proposal.proposal_id !== params.proposalId) { - navigate(`/${network.name}/vote/${proposal.proposal_id}`) - } - } - - function closeProposal(){ - setShowModal(false) - navigate(`/${network.name}/vote`) - } - - function onVote(proposal, vote){ - setError(null) - setVotes({ - [proposal.proposal_id]: vote - }) - closeProposal() - } - - function sortProposals(proposals){ - return _.sortBy(proposals, ({ proposal_id }) => { - return 0 - parseInt(proposal_id) - }); - } - - if (!proposals) { - return ( -
    - - Loading... - -
    - ); - } - - const alerts = ( - <> - - - ); - - return ( - <> - {alerts} -
    - -
    - el.granter)} - favouriteAddresses={props.favouriteAddresses} - queryClient={props.queryClient} - signingClient={props.signingClient} - closeProposal={closeProposal} - onVote={onVote} - setError={setError} - /> - - ); -} - -export default Voting; \ No newline at end of file diff --git a/src/components/WalletModal.js b/src/components/WalletModal.js deleted file mode 100644 index a167ccb2c..000000000 --- a/src/components/WalletModal.js +++ /dev/null @@ -1,145 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import _ from 'lodash' - -import { - Modal, - Tab, - Nav, - Table, - Button -} from 'react-bootstrap' - -import Address from './Address.js'; -import Coins from './Coins.js'; -import Favourite from './Favourite.js'; -import SavedAddresses from './SavedAddresses.js'; - -function WalletModal(props) { - const { show, network, wallet, favouriteAddresses } = props - const [activeTab, setActiveTab] = useState(props.activeTab || wallet ? 'wallet' : 'saved') - - useEffect(() => { - if (props.activeTab && props.activeTab != activeTab) { - setActiveTab(props.activeTab) - }else if (!props.show){ - setActiveTab() - } - }, [props.show]) - - function onHide() { - props.onHide() - } - - if(!network) return null - - // const balances = _.sortBy((props.balances || []).map(balance => { - const balances = _.sortBy((props.balances || []).filter(el => el.denom === network.denom).map(balance => { - const asset = network.assets.find(a => a.base?.denom === balance.denom) - return { - ...balance, - asset - } - }), ({ asset }) => { - if(!asset) return 0 - if(network.denom === asset.base?.denom) return -2 - return -1 - }) - - return ( - <> - onHide()}> - - Wallet - - - setActiveTab(k)} id="wallet-tabs"> - - - - {wallet && ( - <> - - - - - - - {wallet.name && ( - - - - - )} - - - - - - - - - -
    Wallet Provider -
    - {props.signerProvider?.label || 'None'} -
    -
    Wallet Name -
    - {wallet.name} -
    -
    Address -
    -
    - -
    -
    Balance - {balances.map(balance => { - return ( -
    - -
    - ) - })} -
    -
    - - -
    - - )} -
    - - - -
    -
    -
    -
    - - ); -} - -export default WalletModal diff --git a/src/converters/Authz.mjs b/src/converters/Authz.mjs deleted file mode 100644 index bf843d9bd..000000000 --- a/src/converters/Authz.mjs +++ /dev/null @@ -1,126 +0,0 @@ -import moment from 'moment' -import { GenericAuthorization } from "cosmjs-types/cosmos/authz/v1beta1/authz.js"; -import { StakeAuthorization } from "cosmjs-types/cosmos/staking/v1beta1/authz.js"; - -function createAuthzAuthorizationAminoConverter(){ - return { - "/cosmos.authz.v1beta1.GenericAuthorization": { - aminoType: "cosmos-sdk/GenericAuthorization", - toAmino: (value) => GenericAuthorization.decode(value), - fromAmino: ({ msg }) => (GenericAuthorization.encode(GenericAuthorization.fromPartial({ - msg - })).finish()) - }, - "/cosmos.staking.v1beta1.StakeAuthorization": { - aminoType: "cosmos-sdk/StakeAuthorization", - toAmino: (value) => { - const { allowList, maxTokens, authorizationType } = StakeAuthorization.decode(value) - return { - Validators: { - type: "cosmos-sdk/StakeAuthorization/AllowList", - value: { - allow_list: allowList - } - }, - max_tokens: maxTokens, - authorization_type: authorizationType - } - }, - fromAmino: ({ allow_list, max_tokens, authorization_type }) => (StakeAuthorization.encode(StakeAuthorization.fromPartial({ - allowList: allow_list, - maxTokens: max_tokens, - authorizationType: authorization_type - })).finish()) - } - } -} - -const dateConverter = { - toAmino(date){ - return moment(date.seconds.toNumber() * 1000).utc().format() - }, - fromAmino(date){ - return { - seconds: moment(date).unix(), - nanos: 0 - } - } -} - -export function createAuthzAminoConverters() { - const grantConverter = createAuthzAuthorizationAminoConverter() - return { - "/cosmos.authz.v1beta1.MsgGrant": { - aminoType: "cosmos-sdk/MsgGrant", - toAmino: ({ granter, grantee, grant }) => { - const converter = grantConverter[grant.authorization.typeUrl] - return { - granter, - grantee, - grant: { - authorization: { - type: converter.aminoType, - value: converter.toAmino(grant.authorization.value) - }, - expiration: dateConverter.toAmino(grant.expiration) - } - } - }, - fromAmino: ({ granter, grantee, grant }) => { - const protoType = Object.keys(grantConverter).find(type => grantConverter[type].aminoType === grant.authorization.type) - const converter = grantConverter[protoType] - return { - granter, - grantee, - grant: { - authorization: { - typeUrl: protoType, - value: converter.fromAmino(grant.authorization.value) - }, - expiration: dateConverter.fromAmino(grant.expiration) - } - } - }, - }, - "/cosmos.authz.v1beta1.MsgRevoke": { - aminoType: "cosmos-sdk/MsgRevoke", - toAmino: ({ granter, grantee, msgTypeUrl }) => ({ - granter, - grantee, - msg_type_url: msgTypeUrl - }), - fromAmino: ({ granter, grantee, msg_type_url }) => ({ - granter, - grantee, - msgTypeUrl: msg_type_url - }), - }, - }; -} - -export function createAuthzExecAminoConverters(registry, aminoTypes) { - return { - "/cosmos.authz.v1beta1.MsgExec": { - aminoType: "cosmos-sdk/MsgExec", - toAmino: ({ grantee, msgs }) => ({ - grantee, - msgs: msgs.map(({typeUrl, value}) => { - const msgType = registry.lookupType(typeUrl) - return aminoTypes.toAmino({ typeUrl, value: msgType.decode(value) }) - }) - }), - fromAmino: ({ grantee, msgs }) => ({ - grantee, - msgs: msgs.map(({type, value}) => { - const proto = aminoTypes.fromAmino({ type, value }) - const typeUrl = proto.typeUrl - const msgType = registry.lookupType(typeUrl) - return { - typeUrl, - value: msgType.encode(msgType.fromPartial(proto.value)).finish() - } - }) - }), - }, - }; -} diff --git a/src/favicon-16x16.png b/src/favicon-16x16.png deleted file mode 100644 index d69d78618df81c1365a9cdce8b8aca09be1e225c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 582 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 diff --git a/src/index.css b/src/index.css deleted file mode 100644 index cc4230942..000000000 --- a/src/index.css +++ /dev/null @@ -1,15 +0,0 @@ -@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css"); - -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/src/index.html b/src/index.html deleted file mode 100644 index 567411646..000000000 --- a/src/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - REStake | Auto-compound your staking rewards - - - - - -
    - - diff --git a/src/index.js b/src/index.js deleted file mode 100644 index df29de4f9..000000000 --- a/src/index.js +++ /dev/null @@ -1,60 +0,0 @@ -import Bugsnag from '@bugsnag/js' -import BugsnagPluginReact from '@bugsnag/plugin-react' -import React from 'react'; - -import ReactDOM from 'react-dom'; -import { - BrowserRouter, - Routes, - Route -} from "react-router-dom"; - -import NetworkFinder from './components/NetworkFinder' -import reportWebVitals from './utils/reportWebVitals'; - -import './index.css'; - -const app = ( - - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - -) - -if (process.env.BUGSNAG_KEY) { - Bugsnag.start({ - apiKey: process.env.BUGSNAG_KEY, - plugins: [new BugsnagPluginReact()], - enabledReleaseStages: ['production', 'staging'], - releaseStage: process.env.NODE_ENV - }) - - const ErrorBoundary = Bugsnag.getPlugin('react') - .createErrorBoundary(React) - - ReactDOM.render( - - {app} - , - document.getElementById('root') - ); -}else{ - ReactDOM.render( - app, - document.getElementById('root') - ); -} - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/src/manifest.json b/src/manifest.json deleted file mode 100644 index 27dc2a406..000000000 --- a/src/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "short_name": "REStake", - "name": "REStake", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/src/robots.txt b/src/robots.txt deleted file mode 100644 index e9e57dc4d..000000000 --- a/src/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/src/setupTests.js b/src/setupTests.js deleted file mode 100644 index 8f2609b7b..000000000 --- a/src/setupTests.js +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/src/site.webmanifest b/src/site.webmanifest deleted file mode 100644 index 45dc8a206..000000000 --- a/src/site.webmanifest +++ /dev/null @@ -1 +0,0 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/src/utils/FalconSignerProvider.mjs b/src/utils/FalconSignerProvider.mjs deleted file mode 100644 index 07c7b2524..000000000 --- a/src/utils/FalconSignerProvider.mjs +++ /dev/null @@ -1,16 +0,0 @@ -import SignerProvider from "./SignerProvider.mjs"; - -export default class FalconSignerProvider extends SignerProvider { - key = 'falcon' - label = 'Falcon Wallet' - keychangeEvent = 'falcon_keystorechange' - - enable(network){ - const { chainId } = network - return this.provider.connect(chainId) - } - - suggestChain(network){ - return this.provider.importZone(network.suggestChain()) - } -} \ No newline at end of file diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index e54f13308..6790d2902 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -1,7 +1,6 @@ import _ from 'lodash' import { format, floor, bignumber } from 'mathjs' import { coin as _coin } from '@cosmjs/stargate' -import truncateMiddle from 'truncate-middle' export function timeStamp(...args) { console.log('[' + new Date().toISOString().substring(11, 23) + ']', ...args); @@ -15,11 +14,6 @@ export function joinString(...args){ return _.compact(args).join(' ') } -export function truncateAddress(address) { - const firstDigit = address.search(/\d/) - return truncateMiddle(address, firstDigit + 6, 6, '…') -} - export function rewardAmount(rewards, denom, type){ if (!rewards) return 0; @@ -71,7 +65,7 @@ export function parseGrants(grants, grantee, granter) { // claimGrant is removed but we track for now to allow revoke const claimGrant = grants.find((el) => { if ( - (!el.grantee || el.grantee === grantee) && + (!el.grantee || el.grantee === grantee) && (!el.granter || el.granter === granter) && (el.authorization["@type"] === "/cosmos.authz.v1beta1.GenericAuthorization" && @@ -85,7 +79,7 @@ export function parseGrants(grants, grantee, granter) { }); const stakeGrant = grants.find((el) => { if ( - (!el.grantee || el.grantee === grantee) && + (!el.grantee || el.grantee === grantee) && (!el.granter || el.granter === granter) && (el.authorization["@type"] === "/cosmos.staking.v1beta1.StakeAuthorization" || ( diff --git a/src/utils/KeplrMobileSignerProvider.mjs b/src/utils/KeplrMobileSignerProvider.mjs deleted file mode 100644 index 80e57a3a2..000000000 --- a/src/utils/KeplrMobileSignerProvider.mjs +++ /dev/null @@ -1,97 +0,0 @@ -import WalletConnect from "@walletconnect/client"; -import { KeplrWalletConnectV1 } from "@keplr-wallet/wc-client"; - -import SignerProvider from "./SignerProvider.mjs" - -export default class KeplrMobileSignerProvider extends SignerProvider { - key = 'keplr-mobile' - label = 'Keplr Mobile' - keychangeEvent = 'keplr_keystorechange' - suggestChainSupport = false - - constructor({ connectModal }) { - super() - this.connectModal = connectModal - this.connector = new WalletConnect({ - bridge: "https://bridge.walletconnect.org", // Required - signingMethods: [ - "keplr_enable_wallet_connect_v1", - "keplr_sign_amino_wallet_connect_v1", - ], - qrcodeModal: this.connectModal, - }); - } - - available() { - return true - } - - connected() { - return this.connector.connected - } - - async connect(network) { - let key - try { - this.connectModal.open() - key = await super.connect(network) - this.connectModal.close() - return key - } catch (error) { - this.connectModal.close() - throw(error) - } - } - - async enable(network) { - await this.createSession() - await this.provider.enable(network.chainId) - } - - async disconnect() { - return this.provider?.clearSaved() - } - - async forceDisconnect() { - try { - this.disconnect() - this.connector.killSession() - } catch (error) { - console.log(error) - } - } - - getSigner(network) { - const { chainId } = network - // return this.provider.getOfflineSignerAuto(chainId) // no signDirect support currently - return this.provider.getOfflineSignerOnlyAmino(chainId) - } - - async createSession() { - if (!this.connector.connected) { - this.connector.createSession(); - return new Promise((resolve, reject) => { - this.connector.on("connect", (error) => { - if (error) { - reject(error); - } else { - this.provider = new KeplrWalletConnectV1(this.connector); - resolve(); - } - }); - }); - } else { - this.provider = new KeplrWalletConnectV1(this.connector); - return Promise.resolve(); - } - } - - handleEnableError(network, error){ - switch (error?.message) { - case `There is no chain info for ${network.chainId}`: - throw new Error(`${network.prettyName} (${network.chainId}) is not supported`) - default: - super.handleEnableError(network, error) - } - } -} \ No newline at end of file diff --git a/src/utils/KeplrSignerProvider.mjs b/src/utils/KeplrSignerProvider.mjs deleted file mode 100644 index a71024e63..000000000 --- a/src/utils/KeplrSignerProvider.mjs +++ /dev/null @@ -1,24 +0,0 @@ -import SignerProvider from "./SignerProvider.mjs" - -export default class KeplrSignerProvider extends SignerProvider { - key = 'keplr' - label = 'Keplr Extension' - keychangeEvent = 'keplr_keystorechange' - - enable(network){ - if (network.gasPricePrefer) { - this.setOptions({ - sign: { preferNoSetFee: true } - }) - } - return super.enable(network) - } - - setOptions(options){ - return this.provider.defaultOptions = options - } - - getOptions(){ - return this.provider.defaultOptions - } -} \ No newline at end of file diff --git a/src/utils/LeapSignerProvider.mjs b/src/utils/LeapSignerProvider.mjs deleted file mode 100644 index 6505cf17c..000000000 --- a/src/utils/LeapSignerProvider.mjs +++ /dev/null @@ -1,34 +0,0 @@ -import SignerProvider from "./SignerProvider.mjs" - -export default class LeapSignerProvider extends SignerProvider { - key = 'leap' - label = 'Leap Wallet' - keychangeEvent = 'leap_keystorechange' - suggestChainSupport = false - - enable(network){ - if (network.gasPricePrefer) { - this.setOptions({ - sign: { preferNoSetFee: true } - }) - } - return super.enable(network) - } - - setOptions(options){ - return this.provider.defaultOptions = options - } - - getOptions(){ - return this.provider.defaultOptions - } - - handleEnableError(network, error){ - switch (error?.message) { - case `Invalid chain id`: - throw new Error(`${network.prettyName} (${network.chainId}) is not supported`) - default: - super.handleEnableError(network, error) - } - } -} \ No newline at end of file diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index c4152503f..3a9a1c3ae 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -45,7 +45,7 @@ class Network { } estimateOperatorCount() { - if(!this.operatorAddresses) return 0 + if(!this.operatorAddresses) return 0 return Object.keys(this.operatorAddresses).filter(el => this.allowOperator(el)).length } @@ -101,12 +101,11 @@ class Network { this.gasPricePrefer = this.data.gasPricePrefer this.gasModifier = this.data.gasModifier || 1.5 this.txTimeout = this.data.txTimeout || 60_000 - this.keywords = this.buildKeywords() } async connect(opts) { try { - this.queryClient = await QueryClient(this.chain.chainId, this.restUrl, { + this.queryClient = await QueryClient(this.chain.chainId, this.restUrl, { connectTimeout: opts?.timeout, apiVersions: this.chain.apiVersions }) @@ -158,53 +157,6 @@ class Network { {} ) } - - suggestChain(){ - const currency = { - coinDenom: this.symbol, - coinMinimalDenom: this.denom, - coinDecimals: this.decimals - } - if(this.coinGeckoId){ - currency.coinGeckoId = this.coinGeckoId - } - const data = { - rpc: this.rpcUrl, - rest: this.restUrl, - chainId: this.chainId, - chainName: this.prettyName, - stakeCurrency: currency, - bip44: { coinType: this.slip44 }, - walletUrlForStaking: "https://restake.app/" + this.name, - bech32Config: { - bech32PrefixAccAddr: this.prefix, - bech32PrefixAccPub: this.prefix + "pub", - bech32PrefixValAddr: this.prefix + "valoper", - bech32PrefixValPub: this.prefix + "valoperpub", - bech32PrefixConsAddr: this.prefix + "valcons", - bech32PrefixConsPub: this.prefix + "valconspub" - }, - currencies: [currency], - feeCurrencies: [{...currency, gasPriceStep: this.gasPriceStep }] - } - if(this.keplrFeatures()){ - data.features = this.keplrFeatures() - } - return data - } - - keplrFeatures(){ - if(this.data.keplrFeatures) return this.data.keplrFeatures - if(this.slip44 === 60) return ["ibc-transfer", "ibc-go", "eth-address-gen", "eth-key-sign"] - } - - buildKeywords(){ - return _.compact([ - ...this.chain?.keywords || [], - this.authzSupport && 'authz', - this.authzAminoSupport && 'full authz ledger', - ]) - } } export default Network; diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index 654300da6..d70a926ec 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -1,110 +1,17 @@ -import moment from 'moment' -import parse from 'parse-duration' import _ from 'lodash' const Operator = (network, data) => { const { address } = data const botAddress = data.restake.address - let runTime = data.restake.run_time - runTime = runTime && Array.isArray(runTime) ? runTime : [runTime?.split(',')].flat() const minimumReward = data.restake.minimum_reward - function missedRunCount(lastExec){ - if(lastExec === false) return 9999 - let count = 0 - let times = runTimes().reverse() - let date = times[0] - while(date && date.isAfter(lastExec)){ - times.forEach(time => { - if(time.isBefore() && time.isAfter(lastExec)){ - count++ - } - }) - times = runTimes(date.subtract(1, 'day').startOf('day')).reverse() - date = times[0] - } - return count - } - - function isInterval(){ - return runTime.length === 1 && runTime[0].startsWith('every') - } - - function runTimes(start) { - if (!runTime) return [] - start = start || moment().startOf('day') - - return _.compact(runTime.map(time => { - if(time.startsWith('every')){ - let date = start.clone() - const interval = parse(time.replace('every ', '')) - if(!interval) return - - return [...Array(Math.floor(parse('1d') / interval))].map((_, i) => { - let current = date.clone() - date.add(interval, 'ms') - return current - }) - }else{ - let date = start.clone() - let [hours, minutes, seconds] = time.split(':') - if(parseInt(hours) >= 24) hours = 0 - date.utc(true).add({hours, minutes, seconds}) - return date - } - }).flat()).sort((a, b) => a.valueOf() - b.valueOf()) - } - - function runsPerDay(max) { - let runs = runTimes().length - return max && runs > max ? max : runs - } - - function runTimesString(){ - if (!isInterval()) { - if(runTime.length > 1){ - return `at ${runTime.join(', ')}` - }else{ - return `at ${runTime.join(', ')} every day` - } - } - return runTime.join(', ') - } - - function frequency(includeEvery) { - if(runTime.length > 1 && !isInterval()){ - return runTime.length + 'x daily' - }else{ - if(isInterval()){ - return includeEvery ? runTime[0] : runTime[0].replace('every ', '') - } - return 'daily' - } - } - - function nextRun() { - const today = runTimes() - const tomorrow = runTimes(moment().startOf('day').add(1, 'day')) - return [ - ...today, - ...tomorrow - ].find(el => el.isAfter()) - } - return { address, botAddress, - runTime, minimumReward, moniker: data.description?.moniker, description: data.description, data, - nextRun, - frequency, - runTimes, - runTimesString, - runsPerDay, - missedRunCount } } diff --git a/src/utils/Proposal.mjs b/src/utils/Proposal.mjs deleted file mode 100644 index fecf0b212..000000000 --- a/src/utils/Proposal.mjs +++ /dev/null @@ -1,60 +0,0 @@ -export const PROPOSAL_STATUSES = { - '': 'All', - 'PROPOSAL_STATUS_DEPOSIT_PERIOD': 'Deposit Period', - 'PROPOSAL_STATUS_VOTING_PERIOD': 'Voting Period', - 'PROPOSAL_STATUS_PASSED': 'Passed', - 'PROPOSAL_STATUS_REJECTED': 'Rejected', - 'PROPOSAL_STATUS_FAILED': 'Failed' -} - -const Proposal = (data) => { - let { proposal_id, content, messages, metadata } = data - if(!proposal_id && data.id) proposal_id = data.id - - let title, description, typeHuman - if(metadata){ - try { - metadata = JSON.parse(metadata) - title = metadata.title - description = metadata.summary - } catch (e) { - console.log(e) - } - } - - if(messages){ - content = messages.find(el => el['@type'] === '/cosmos.gov.v1.MsgExecLegacyContent')?.content - messages = messages.filter(el => el['@type'] !== '/cosmos.gov.v1.MsgExecLegacyContent') - typeHuman = messages.map(el => el['@type'].split('.').reverse()[0]).join(', ') - } - - if(content){ - title = title || content.title - description = description || content.description - typeHuman = typeHuman || (content['@type'] ? content['@type'].split('.').reverse()[0] : 'Unknown') - } - - title = title || typeHuman - description = description || metadata - - const statusHuman = PROPOSAL_STATUSES[data.status] - - const isDeposit = data.status === 'PROPOSAL_STATUS_DEPOSIT_PERIOD' - const isVoting = data.status === 'PROPOSAL_STATUS_VOTING_PERIOD' - - return { - ...data, - proposal_id, - title, - typeHuman, - statusHuman, - description, - content, - metadata, - messages, - isDeposit, - isVoting - } -} - -export default Proposal; diff --git a/src/utils/SignerProvider.mjs b/src/utils/SignerProvider.mjs deleted file mode 100644 index 8fe1992b5..000000000 --- a/src/utils/SignerProvider.mjs +++ /dev/null @@ -1,75 +0,0 @@ -export default class SignerProvider { - suggestChainSupport = true - - constructor(provider){ - this.provider = provider - } - - available(){ - return !!this.provider - } - - connected(){ - return this.available() - } - - async connect(network){ - try { - await this.enable(network) - return await this.getKey(network) - } catch (e) { - if(!this.suggestChainSupport){ - this.handleEnableError(network, e) - } - try { - await this.suggestChain(network) - return await this.getKey(network) - } catch (s) { - this.handleSuggestError(network, e) - } - } - } - - disconnect(){ - } - - enable(network){ - const { chainId } = network - return this.provider.enable(chainId) - } - - async getKey(network){ - const { chainId } = network - const key = await this.provider.getKey(chainId) - return key - } - - getSigner(network){ - const { chainId } = network - return this.provider.getOfflineSignerAuto(chainId) - } - - suggestChain(network){ - if(this.suggestChainSupport){ - return this.provider.experimentalSuggestChain(network.suggestChain()) - }else{ - throw new Error(`${network.prettyName} (${network.chainId}) is not supported`) - } - } - - handleEnableError(network, error){ - throw(error) - } - - handleSuggestError(network, error){ - throw(error) - } - - setOptions(options){ - return {} - } - - getOptions(){ - return {} - } -} \ No newline at end of file diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 2e27537e5..8683ecec7 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -6,25 +6,16 @@ import Long from "long"; import { defaultRegistryTypes as defaultStargateTypes, assertIsDeliverTxSuccess, - GasPrice, - AminoTypes, - createBankAminoConverters, - createDistributionAminoConverters, - createFreegrantAminoConverters, - createGovAminoConverters, - createIbcAminoConverters, - createStakingAminoConverters, + GasPrice } from "@cosmjs/stargate"; import { sleep } from "@cosmjs/utils"; import { makeSignDoc, Registry } from "@cosmjs/proto-signing"; -import { makeSignDoc as makeAminoSignDoc } from "@cosmjs/amino"; import { toBase64, fromBase64 } from '@cosmjs/encoding' import { PubKey } from "cosmjs-types/cosmos/crypto/secp256k1/keys.js"; import { SignMode } from "cosmjs-types/cosmos/tx/signing/v1beta1/signing.js"; import { AuthInfo, Fee, TxBody, TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx.js"; import { coin } from './Helpers.mjs' -import { createAuthzAminoConverters, createAuthzExecAminoConverters } from '../converters/Authz.mjs' function SigningClient(network, signer) { @@ -32,17 +23,6 @@ function SigningClient(network, signer) { const { restUrl, gasModifier: defaultGasModifier, slip44: coinType, chainId } = network const registry = new Registry(defaultStargateTypes); - const defaultConverters = { - ...createAuthzAminoConverters(), - ...createBankAminoConverters(), - ...createDistributionAminoConverters(), - ...createGovAminoConverters(), - ...createStakingAminoConverters(network.prefix), - ...createIbcAminoConverters(), - ...createFreegrantAminoConverters(), - } - let aminoTypes = new AminoTypes(defaultConverters) - aminoTypes = new AminoTypes({...defaultConverters, ...createAuthzExecAminoConverters(registry, aminoTypes)}) function getAccount(address) { return axios @@ -82,7 +62,7 @@ function SigningClient(network, signer) { value = nestedAccount } - return value + return value }) .catch((error) => { if(error.response?.status === 404){ @@ -112,17 +92,9 @@ function SigningClient(network, signer) { } async function signAndBroadcastWithoutBalanceCheck(address, msgs, gas, memo, gasPrice) { - let defaultOptions - if(signer.keplr.defaultOptions){ - defaultOptions = _.clone(signer.keplr.defaultOptions); - signer.keplr.defaultOptions = {...defaultOptions, sign: { disableBalanceCheck: true }} - } try { return await signAndBroadcast(address, msgs, gas, memo, gasPrice) } finally { - if(defaultOptions){ - signer.keplr.defaultOptions = defaultOptions - } } } @@ -141,7 +113,7 @@ function SigningClient(network, signer) { const txPollTimeout = setTimeout(() => { timedOut = true; }, timeoutMs); - + const pollForTx = async (txId) => { if (timedOut) { throw new Error( @@ -181,36 +153,17 @@ function SigningClient(network, signer) { const account = await getAccount(address) const { account_number: accountNumber, sequence } = account const txBodyBytes = makeBodyBytes(messages, memo) - let aminoMsgs - try { - aminoMsgs = convertToAmino(messages) - } catch (e) { } - if(aminoMsgs && signer.signAmino){ - // Sign as amino if possible for Ledger and Keplr support - const signDoc = makeAminoSignDoc(aminoMsgs, fee, chainId, memo, accountNumber, sequence); - const { signature, signed } = await signer.signAmino(address, signDoc); - const authInfoBytes = await makeAuthInfoBytes(account, { - amount: signed.fee.amount, - gasLimit: signed.fee.gas, - }, SignMode.SIGN_MODE_LEGACY_AMINO_JSON) - return { - bodyBytes: makeBodyBytes(messages, signed.memo), - authInfoBytes: authInfoBytes, - signatures: [Buffer.from(signature.signature, "base64")], - } - }else{ - // Sign using standard protobuf messages - const authInfoBytes = await makeAuthInfoBytes(account, { - amount: fee.amount, - gasLimit: fee.gas, - }, SignMode.SIGN_MODE_DIRECT) - const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber); - const { signature, signed } = await signer.signDirect(address, signDoc); - return { - bodyBytes: signed.bodyBytes, - authInfoBytes: signed.authInfoBytes, - signatures: [fromBase64(signature.signature)], - } + // Sign using standard protobuf messages + const authInfoBytes = await makeAuthInfoBytes(account, { + amount: fee.amount, + gasLimit: fee.gas, + }, SignMode.SIGN_MODE_DIRECT) + const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber); + const { signature, signed } = await signer.signDirect(address, signDoc); + return { + bodyBytes: signed.bodyBytes, + authInfoBytes: signed.authInfoBytes, + signatures: [fromBase64(signature.signature)], } } @@ -236,22 +189,6 @@ function SigningClient(network, signer) { } } - function convertToAmino(messages){ - return messages.map(message => { - if(message.typeUrl.startsWith('/cosmos.authz') && !network.authzAminoSupport){ - throw new Error('This chain does not support amino conversion for Authz messages') - } - if(message.typeUrl === '/cosmos.authz.v1beta1.MsgExec' && network.path === 'osmosis'){ - // Osmosis MsgExec gov is broken with Amino currently - // See https://github.com/osmosis-labs/cosmos-sdk/pull/342 - if(message.value.msgs.some(msg => msg.typeUrl.startsWith('/cosmos.gov'))){ - throw new Error('Osmosis does not support amino conversion for Authz Exec gov messages') - } - } - return aminoTypes.toAmino(message) - }) - } - function parseTxResult(result){ return { code: result.code, diff --git a/src/utils/Vote.mjs b/src/utils/Vote.mjs deleted file mode 100644 index c1c9928d4..000000000 --- a/src/utils/Vote.mjs +++ /dev/null @@ -1,37 +0,0 @@ -import { equal } from 'mathjs' - -export const VOTE_CHOICES = { - 'VOTE_OPTION_YES': 'Yes', - 'VOTE_OPTION_NO': 'No', - 'VOTE_OPTION_ABSTAIN': 'Abstain', - 'VOTE_OPTION_NO_WITH_VETO': 'No with Veto' -} - -export const VOTE_VALUES = { - 'VOTE_OPTION_YES': 1, - 'VOTE_OPTION_NO': 3, - 'VOTE_OPTION_NO_WITH_VETO': 4, - 'VOTE_OPTION_ABSTAIN': 2 -} - -const Vote = (data) => { - - let option = data.option - let optionHuman = VOTE_CHOICES[data.option] - let optionValue = VOTE_VALUES[data.option] - - if(!optionValue && data.options?.length){ - option = data.options.find(el => equal(el.weight, 1))?.option - optionHuman = option ? VOTE_CHOICES[option] : 'Multiple' - optionValue = option ? VOTE_VALUES[option] : null // doesn't support multiple right now - } - - return { - ...data, - option, - optionHuman, - optionValue - } -} - -export default Vote; diff --git a/src/utils/reportWebVitals.js b/src/utils/reportWebVitals.js deleted file mode 100644 index 5253d3ad9..000000000 --- a/src/utils/reportWebVitals.js +++ /dev/null @@ -1,13 +0,0 @@ -const reportWebVitals = onPerfEntry => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; From 419e1d7d4d22d22795faf1035f853ae008071045 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 26 Mar 2023 14:00:19 +0100 Subject: [PATCH 802/862] Update README --- README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ebb8ad6ae..88029d985 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ REStake is also a convenient staking tool, allowing you to claim and compound yo Try it out at [restake.app](https://restake.app). +The REStake UI has it's own dedicated repository at [eco-stake/restake-ui](https://github.com/eco-stake/restake-ui). + ## How it works / Authz Authz is a new feature for Tendermint chains which lets you grant permission to another wallet to carry out certain transactions for you. These transactions are sent by the grantee on behalf of the granter, meaning the validator will send and pay for the TX, but actions will affect your wallet (such as claiming rewards). @@ -21,7 +23,7 @@ A script is also provided which allows a validator to automatically search their - As of writing, Ledger is unable to send the necessary transactions to enable Authz. This is purely due to the way transactions are sent to a Ledger device and a workaround should be possible soon. - Authz is also not fully supported yet. Many chains are yet to update. The REStake UI will fall back to being a manual staking app with useful manual compounding features. - Currently REStake needs the browser extension version of Keplr, but WalletConnect and Keplr iOS functionality will be added ASAP. -- REStake requires Nodejs version 17.x or later, it will not work with earlier versions. +- REStake requires Nodejs version 18.x or later, it will not work with earlier versions. ## Become an operator @@ -87,18 +89,18 @@ cp .env.sample .env #### Instructions for NPM -##### Install nodejs@v17 +##### Install nodejs@v18 ```bash -curl -sL https://deb.nodesource.com/setup_17.x -o /tmp/nodesource_setup.sh +curl -sL https://deb.nodesource.com/setup_18.x -o /tmp/nodesource_setup.sh # read the script file and when you're sure it's safe run it chmod +x /tmp/nodesource_setup.sh /tmp/nodesource_setup.sh apt install nodejs -y node --version -> v17.9.0 +> v18.15.0 npm --version -> 8.5.5 +> 9.5.0 ``` @@ -404,13 +406,9 @@ Note that most attributes from Chain Registry can be overridden by defining the ### Running the UI -Run the UI using docker with one line: - -```bash -docker run -p 80:80 -t ghcr.io/eco-stake/restake -``` +The REStake UI now has it's own dedicated repository at [eco-stake/restake-ui](https://github.com/eco-stake/restake-ui). -Alternative run from source using `docker-compose up` or `npm start`. +Check the project README for instructions on running your own UI. ## Ethos From 67baf630d4c12b4712d40a94cbf13745907a523b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 26 Mar 2023 14:02:08 +0100 Subject: [PATCH 803/862] Update Node versions --- .nvmrc | 2 +- README.md | 1 - README_TURKISH.md | 10 +++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.nvmrc b/.nvmrc index 98d9bcb75..3c032078a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -17 +18 diff --git a/README.md b/README.md index 88029d985..4342a3281 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,6 @@ node --version > v18.15.0 npm --version > 9.5.0 - ``` ##### Clone the repository and build it diff --git a/README_TURKISH.md b/README_TURKISH.md index e8493a3e2..b6110349b 100644 --- a/README_TURKISH.md +++ b/README_TURKISH.md @@ -88,18 +88,18 @@ cp .env.sample .env #### NPM için Talimatlar -##### nodejs@v17 yükleme +##### nodejs@v18 yükleme ```bash -curl -sL https://deb.nodesource.com/setup_17.x -o /tmp/nodesource_setup.sh +curl -sL https://deb.nodesource.com/setup_18.x -o /tmp/nodesource_setup.sh # read the script file and when you're sure it's safe run it chmod +x /tmp/nodesource_setup.sh /tmp/nodesource_setup.sh apt install nodejs -y node --version -> v17.9.0 +> v18.15.0 npm --version -> 8.5.5 +> 9.5.0 ``` Depoyu klonlama ve yükleme @@ -267,7 +267,7 @@ NPM kurulumu için `Requires` ve `After` direktiflerini kaldırın ve` `ExecStar 🔴 **Eğer `Failed to restake service with docker compose` gibi bir hata alırsanız yine `chmod 777 /usr/bin/docker-compose` komutu ile dosyaya okuma, yazma ve çalıştırma izni veriniz.** -Çözüm için değerli arkadaşım [Odyseus](https://github.com/odyseus8)'a teşekkür ederim. +Çözüm için değerli arkadaşım [Odyseus](https://github.com/odyseus8)'a teşekkür ederim. ##### systemd timer dosyası oluşturma From c6c6b02e54274175ea6ccae50348ab89572c40a2 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 26 Mar 2023 14:04:18 +0100 Subject: [PATCH 804/862] Update dependencies --- package-lock.json | 1880 ++++++++++----------------------------------- 1 file changed, 407 insertions(+), 1473 deletions(-) diff --git a/package-lock.json b/package-lock.json index 664ddc94c..75c07edee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "restake", "version": "0.1.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -19,15 +19,14 @@ "dotenv": "^16.0.0", "lodash": "^4.17.21", "mathjs": "^10.4.3" - }, - "devDependencies": {} + } }, "node_modules/@babel/runtime": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", - "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" @@ -43,24 +42,24 @@ } }, "node_modules/@cosmjs/amino": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.7.tgz", - "integrity": "sha512-NTCUS3+u9bxwW8moC0N1RS4Gk/fs3JPHTKcp7ksH39V4+7uOKM2rGsyFAekiHNxYngnQ+1hU5x2tddS25soK6Q==", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.13.tgz", + "integrity": "sha512-IHnH2zGwaY69qT4mVAavr/pfzx6YE+ud1NHJbvVePlbGiz68CXTi5LHR+K0lrKB5mQ7E+ZErWz2mw5U/x+V1wQ==", "dependencies": { - "@cosmjs/crypto": "0.28.7", - "@cosmjs/encoding": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/utils": "0.28.7" + "@cosmjs/crypto": "0.28.13", + "@cosmjs/encoding": "0.28.13", + "@cosmjs/math": "0.28.13", + "@cosmjs/utils": "0.28.13" } }, "node_modules/@cosmjs/crypto": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.7.tgz", - "integrity": "sha512-Fuq90nnqXQb6VvUadGtPy1X6arXY+yhqB95VzN8GM/ZdBLeigu6a3XjOvFki4lKO94QdLn2e9huD8dm4tDQDsA==", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.13.tgz", + "integrity": "sha512-ynKfM0q/tMBQMHJby6ad8lR3gkgBKaelQhIsCZTjClsnuC7oYT9y3ThSZCUWr7Pa9h0J8ahU2YV2oFWFVWJQzQ==", "dependencies": { - "@cosmjs/encoding": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/utils": "0.28.7", + "@cosmjs/encoding": "0.28.13", + "@cosmjs/math": "0.28.13", + "@cosmjs/utils": "0.28.13", "@noble/hashes": "^1", "bn.js": "^5.2.0", "elliptic": "^6.5.3", @@ -68,9 +67,9 @@ } }, "node_modules/@cosmjs/encoding": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.7.tgz", - "integrity": "sha512-m2OuRhxux0YacvtjTocEOHyjnKO/KKGjqXlAY5HXGJpyntr+PIlJFdoS9tHax+1rNRrblZXwYIT+UqOs+kSk5Q==", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.13.tgz", + "integrity": "sha512-jtXbAYtV77rLHxoIrjGFsvgGjeTKttuHRv6cvuy3toCZzY7JzTclKH5O2g36IIE4lXwD9xwuhGJ2aa6A3dhNkA==", "dependencies": { "base64-js": "^1.3.0", "bech32": "^1.1.4", @@ -78,60 +77,60 @@ } }, "node_modules/@cosmjs/json-rpc": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.7.tgz", - "integrity": "sha512-2qgRL/9ih/ZYU/8LmtDQopaCKJBKqsuoSXfb2XO3yv6EkE28yiA8YAwLW5IrXjl1tfSiolHaBWarEASS/8Q5xA==", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.13.tgz", + "integrity": "sha512-fInSvg7x9P6p+GWqet+TMhrMTM3OWWdLJOGS5w2ryubMjgpR1rLiAx77MdTNkArW+/6sUwku0sN4veM4ENQu6A==", "dependencies": { - "@cosmjs/stream": "0.28.7", + "@cosmjs/stream": "0.28.13", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/math": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.7.tgz", - "integrity": "sha512-wSXIOOGVzgtRFsGwScpvQ6C6DUx97K7Ez3KyvRALNzspEnIoKPDgTXf438zhZnb+0+ERAwgdkb/6zWaDoVBfUA==", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.13.tgz", + "integrity": "sha512-PDpL8W/kbyeWi0mQ2OruyqE8ZUAdxPs1xCbDX3WXJwy2oU+X2UTbkuweJHVpS9CIqmZulBoWQAmlf6t6zr1N/g==", "dependencies": { "bn.js": "^5.2.0" } }, "node_modules/@cosmjs/proto-signing": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.7.tgz", - "integrity": "sha512-cOwDQVv95KnpHWlkrtxZ+2Q+z2Qp8Co//g+bwNc+YZ403iGHXSK5PRe3YsPa4f3elKJkJpHVi5oyzp4DB8p7mA==", - "dependencies": { - "@cosmjs/amino": "0.28.7", - "@cosmjs/crypto": "0.28.7", - "@cosmjs/encoding": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/utils": "0.28.7", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.13.tgz", + "integrity": "sha512-nSl/2ZLsUJYz3Ad0RY3ihZUgRHIow2OnYqKsESMu+3RA/jTi9bDYhiBu8mNMHI0xrEJry918B2CyI56pOUHdPQ==", + "dependencies": { + "@cosmjs/amino": "0.28.13", + "@cosmjs/crypto": "0.28.13", + "@cosmjs/encoding": "0.28.13", + "@cosmjs/math": "0.28.13", + "@cosmjs/utils": "0.28.13", "cosmjs-types": "^0.4.0", "long": "^4.0.0" } }, "node_modules/@cosmjs/socket": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.7.tgz", - "integrity": "sha512-+sCR5AzjjsKlA0K7m8YdxldMvgJa3Tqnx0AAyQXlNw2VXmW1zu9DkKZWW475XFhwO9UYfrdIsOe1vbhnUjQb5g==", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.13.tgz", + "integrity": "sha512-lavwGxQ5VdeltyhpFtwCRVfxeWjH5D5mmN7jgx9nuCf3XSFbTcOYxrk2pQ4usenu1Q1KZdL4Yl5RCNrJuHD9Ug==", "dependencies": { - "@cosmjs/stream": "0.28.7", + "@cosmjs/stream": "0.28.13", "isomorphic-ws": "^4.0.1", "ws": "^7", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/stargate": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.7.tgz", - "integrity": "sha512-3nY7czFUaY74gacbQAZuNji1qJWWQjv2C1cxYNqe7qAZAvCiz6A9adJVUmCJRL4peeG7tKiOny1J5IFbsgRu0g==", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.13.tgz", + "integrity": "sha512-dVBMazDz8/eActHsRcZjDHHptOBMqvibj5CFgEtZBp22gP6ASzoAUXTlkSVk5FBf4sfuUHoff6st134/+PGMAg==", "dependencies": { "@confio/ics23": "^0.6.8", - "@cosmjs/amino": "0.28.7", - "@cosmjs/encoding": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/proto-signing": "0.28.7", - "@cosmjs/stream": "0.28.7", - "@cosmjs/tendermint-rpc": "0.28.7", - "@cosmjs/utils": "0.28.7", + "@cosmjs/amino": "0.28.13", + "@cosmjs/encoding": "0.28.13", + "@cosmjs/math": "0.28.13", + "@cosmjs/proto-signing": "0.28.13", + "@cosmjs/stream": "0.28.13", + "@cosmjs/tendermint-rpc": "0.28.13", + "@cosmjs/utils": "0.28.13", "cosmjs-types": "^0.4.0", "long": "^4.0.0", "protobufjs": "~6.11.3", @@ -139,25 +138,25 @@ } }, "node_modules/@cosmjs/stream": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.7.tgz", - "integrity": "sha512-zTMZadlpmxAMtKUHX/dnYs1STung392+P50WgSUAjllG7CZYl7SAY03AKgc+aoF1ohYQcgH9H7VSgabEgLPhmg==", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.13.tgz", + "integrity": "sha512-AnjtfwT8NwPPkd3lhZhjOlOzT0Kn9bgEu2IPOZjQ1nmG2bplsr6TJmnwn0dJxHT7UGtex17h6whKB5N4wU37Wg==", "dependencies": { "xstream": "^11.14.0" } }, "node_modules/@cosmjs/tendermint-rpc": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.7.tgz", - "integrity": "sha512-xhIVJL3ol+fZxywP76Ik9pHqCvBdU/BGAw6p48mhla3L3xNcFN2Nf+EnJWcIZPqZl8bHm5QHzPk9VqVFVYCMCw==", - "dependencies": { - "@cosmjs/crypto": "0.28.7", - "@cosmjs/encoding": "0.28.7", - "@cosmjs/json-rpc": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/socket": "0.28.7", - "@cosmjs/stream": "0.28.7", - "@cosmjs/utils": "0.28.7", + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.13.tgz", + "integrity": "sha512-GB+ZmfuJIGQm0hsRtLYjeR3lOxF7Z6XyCBR0cX5AAYOZzSEBJjevPgUHD6tLn8zIhvzxaW3/VKnMB+WmlxdH4w==", + "dependencies": { + "@cosmjs/crypto": "0.28.13", + "@cosmjs/encoding": "0.28.13", + "@cosmjs/json-rpc": "0.28.13", + "@cosmjs/math": "0.28.13", + "@cosmjs/socket": "0.28.13", + "@cosmjs/stream": "0.28.13", + "@cosmjs/utils": "0.28.13", "axios": "^0.21.2", "readonly-date": "^1.0.0", "xstream": "^11.14.0" @@ -172,14 +171,14 @@ } }, "node_modules/@cosmjs/utils": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.7.tgz", - "integrity": "sha512-0ya5mRaDY956n87dKjx6wfqgH/uEfrZyMswIhcEPqQPegwp4FDd6cwJtSdv/d5S7fgvoEs2UABvUFNzCT5C0hg==" + "version": "0.28.13", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.13.tgz", + "integrity": "sha512-dVeMBiyg+46x7XBZEfJK8yTihphbCFpjVYmLJVqmTsHfJwymQ65cpyW/C+V/LgWARGK8hWQ/aX9HM5Ao8QmMSg==" }, "node_modules/@ethersproject/abstract-provider": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.6.1.tgz", - "integrity": "sha512-BxlIgogYJtp1FS8Muvj8YfdClk3unZH0vRMVX791Z9INBNT/kuACZ9GzaY1Y4yFq+YSy6/w4gzj3HCRKrK9hsQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", "funding": [ { "type": "individual", @@ -191,19 +190,19 @@ } ], "dependencies": { - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/networks": "^5.6.3", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/transactions": "^5.6.2", - "@ethersproject/web": "^5.6.1" + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" } }, "node_modules/@ethersproject/abstract-signer": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.6.2.tgz", - "integrity": "sha512-n1r6lttFBG0t2vNiI3HoWaS/KdOt8xyDjzlP2cuevlWLG6EX0OwcKLyG/Kp/cuwNxdy/ous+R/DEMdTUwWQIjQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", "funding": [ { "type": "individual", @@ -215,17 +214,17 @@ } ], "dependencies": { - "@ethersproject/abstract-provider": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0" + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" } }, "node_modules/@ethersproject/address": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.1.tgz", - "integrity": "sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", "funding": [ { "type": "individual", @@ -237,17 +236,17 @@ } ], "dependencies": { - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/rlp": "^5.6.1" + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" } }, "node_modules/@ethersproject/base64": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.6.1.tgz", - "integrity": "sha512-qB76rjop6a0RIYYMiB4Eh/8n+Hxu2NIZm8S/Q7kNo5pmZfXhHGHmS4MinUainiBC54SCyRnwzL+KZjj8zbsSsw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", "funding": [ { "type": "individual", @@ -259,13 +258,13 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1" + "@ethersproject/bytes": "^5.7.0" } }, "node_modules/@ethersproject/basex": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.6.1.tgz", - "integrity": "sha512-a52MkVz4vuBXR06nvflPMotld1FJWSj2QT0985v7P/emPZO00PucFAkbcmq2vpVU7Ts7umKiSI6SppiLykVWsA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", "funding": [ { "type": "individual", @@ -277,14 +276,14 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/properties": "^5.6.0" + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" } }, "node_modules/@ethersproject/bignumber": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.6.2.tgz", - "integrity": "sha512-v7+EEUbhGqT3XJ9LMPsKvXYHFc8eHxTowFCG/HgJErmq4XHJ2WR7aeyICg3uTOAQ7Icn0GFHAohXEhxQHq4Ubw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", "funding": [ { "type": "individual", @@ -296,15 +295,15 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", "bn.js": "^5.2.1" } }, "node_modules/@ethersproject/bytes": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.6.1.tgz", - "integrity": "sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", "funding": [ { "type": "individual", @@ -316,13 +315,13 @@ } ], "dependencies": { - "@ethersproject/logger": "^5.6.0" + "@ethersproject/logger": "^5.7.0" } }, "node_modules/@ethersproject/constants": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.6.1.tgz", - "integrity": "sha512-QSq9WVnZbxXYFftrjSjZDUshp6/eKp6qrtdBtUCm0QxCV5z1fG/w3kdlcsjMCQuQHUnAclKoK7XpXMezhRDOLg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", "funding": [ { "type": "individual", @@ -334,13 +333,13 @@ } ], "dependencies": { - "@ethersproject/bignumber": "^5.6.2" + "@ethersproject/bignumber": "^5.7.0" } }, "node_modules/@ethersproject/hash": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.6.1.tgz", - "integrity": "sha512-L1xAHurbaxG8VVul4ankNX5HgQ8PNCTrnVXEiFnE9xoRnaUcgfD12tZINtDinSllxPLCtGwguQxJ5E6keE84pA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", "funding": [ { "type": "individual", @@ -352,20 +351,21 @@ } ], "dependencies": { - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/strings": "^5.6.1" + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" } }, "node_modules/@ethersproject/hdnode": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.6.2.tgz", - "integrity": "sha512-tERxW8Ccf9CxW2db3WsN01Qao3wFeRsfYY9TCuhmG0xNpl2IO8wgXU3HtWIZ49gUWPggRy4Yg5axU0ACaEKf1Q==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", "funding": [ { "type": "individual", @@ -377,24 +377,24 @@ } ], "dependencies": { - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/basex": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/pbkdf2": "^5.6.1", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/sha2": "^5.6.1", - "@ethersproject/signing-key": "^5.6.2", - "@ethersproject/strings": "^5.6.1", - "@ethersproject/transactions": "^5.6.2", - "@ethersproject/wordlists": "^5.6.1" + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" } }, "node_modules/@ethersproject/json-wallets": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.6.1.tgz", - "integrity": "sha512-KfyJ6Zwz3kGeX25nLihPwZYlDqamO6pfGKNnVMWWfEVVp42lTfCZVXXy5Ie8IZTN0HKwAngpIPi7gk4IJzgmqQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", "funding": [ { "type": "individual", @@ -406,25 +406,25 @@ } ], "dependencies": { - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/address": "^5.6.1", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/hdnode": "^5.6.2", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/pbkdf2": "^5.6.1", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/random": "^5.6.1", - "@ethersproject/strings": "^5.6.1", - "@ethersproject/transactions": "^5.6.2", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", "aes-js": "3.0.0", "scrypt-js": "3.0.1" } }, "node_modules/@ethersproject/keccak256": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.6.1.tgz", - "integrity": "sha512-bB7DQHCTRDooZZdL3lk9wpL0+XuG3XLGHLh3cePnybsO3V0rdCAOQGpn/0R3aODmnTOOkCATJiD2hnL+5bwthA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", "funding": [ { "type": "individual", @@ -436,14 +436,14 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", + "@ethersproject/bytes": "^5.7.0", "js-sha3": "0.8.0" } }, "node_modules/@ethersproject/logger": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.6.0.tgz", - "integrity": "sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", "funding": [ { "type": "individual", @@ -456,9 +456,9 @@ ] }, "node_modules/@ethersproject/networks": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.3.tgz", - "integrity": "sha512-QZxRH7cA5Ut9TbXwZFiCyuPchdWi87ZtVNHWZd0R6YFgYtes2jQ3+bsslJ0WdyDe0i6QumqtoYqvY3rrQFRZOQ==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", "funding": [ { "type": "individual", @@ -470,13 +470,13 @@ } ], "dependencies": { - "@ethersproject/logger": "^5.6.0" + "@ethersproject/logger": "^5.7.0" } }, "node_modules/@ethersproject/pbkdf2": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.6.1.tgz", - "integrity": "sha512-k4gRQ+D93zDRPNUfmduNKq065uadC2YjMP/CqwwX5qG6R05f47boq6pLZtV/RnC4NZAYOPH1Cyo54q0c9sshRQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", "funding": [ { "type": "individual", @@ -488,14 +488,14 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/sha2": "^5.6.1" + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" } }, "node_modules/@ethersproject/properties": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.6.0.tgz", - "integrity": "sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", "funding": [ { "type": "individual", @@ -507,13 +507,13 @@ } ], "dependencies": { - "@ethersproject/logger": "^5.6.0" + "@ethersproject/logger": "^5.7.0" } }, "node_modules/@ethersproject/random": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.6.1.tgz", - "integrity": "sha512-/wtPNHwbmng+5yi3fkipA8YBT59DdkGRoC2vWk09Dci/q5DlgnMkhIycjHlavrvrjJBkFjO/ueLyT+aUDfc4lA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", "funding": [ { "type": "individual", @@ -525,14 +525,14 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0" + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" } }, "node_modules/@ethersproject/rlp": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.6.1.tgz", - "integrity": "sha512-uYjmcZx+DKlFUk7a5/W9aQVaoEC7+1MOBgNtvNg13+RnuUwT4F0zTovC0tmay5SmRslb29V1B7Y5KCri46WhuQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", "funding": [ { "type": "individual", @@ -544,14 +544,14 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0" + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" } }, "node_modules/@ethersproject/sha2": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.6.1.tgz", - "integrity": "sha512-5K2GyqcW7G4Yo3uenHegbXRPDgARpWUiXc6RiF7b6i/HXUoWlb7uCARh7BAHg7/qT/Q5ydofNwiZcim9qpjB6g==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", "funding": [ { "type": "individual", @@ -563,15 +563,15 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", "hash.js": "1.1.7" } }, "node_modules/@ethersproject/signing-key": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.6.2.tgz", - "integrity": "sha512-jVbu0RuP7EFpw82vHcL+GP35+KaNruVAZM90GxgQnGqB6crhBqW/ozBfFvdeImtmb4qPko0uxXjn8l9jpn0cwQ==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", "funding": [ { "type": "individual", @@ -583,18 +583,18 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", "bn.js": "^5.2.1", "elliptic": "6.5.4", "hash.js": "1.1.7" } }, "node_modules/@ethersproject/strings": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.6.1.tgz", - "integrity": "sha512-2X1Lgk6Jyfg26MUnsHiT456U9ijxKUybz8IM1Vih+NJxYtXhmvKBcHOmvGqpFSVJ0nQ4ZCoIViR8XlRw1v/+Cw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", "funding": [ { "type": "individual", @@ -606,15 +606,15 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/constants": "^5.6.1", - "@ethersproject/logger": "^5.6.0" + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" } }, "node_modules/@ethersproject/transactions": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.6.2.tgz", - "integrity": "sha512-BuV63IRPHmJvthNkkt9G70Ullx6AcM+SDc+a8Aw/8Yew6YwT51TcBKEp1P4oOQ/bP25I18JJr7rcFRgFtU9B2Q==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", "funding": [ { "type": "individual", @@ -626,21 +626,21 @@ } ], "dependencies": { - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/constants": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/rlp": "^5.6.1", - "@ethersproject/signing-key": "^5.6.2" + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" } }, "node_modules/@ethersproject/wallet": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.6.2.tgz", - "integrity": "sha512-lrgh0FDQPuOnHcF80Q3gHYsSUODp6aJLAdDmDV0xKCN/T7D99ta1jGVhulg3PY8wiXEngD0DfM0I2XKXlrqJfg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", "funding": [ { "type": "individual", @@ -652,27 +652,27 @@ } ], "dependencies": { - "@ethersproject/abstract-provider": "^5.6.1", - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/hash": "^5.6.1", - "@ethersproject/hdnode": "^5.6.2", - "@ethersproject/json-wallets": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/random": "^5.6.1", - "@ethersproject/signing-key": "^5.6.2", - "@ethersproject/transactions": "^5.6.2", - "@ethersproject/wordlists": "^5.6.1" + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" } }, "node_modules/@ethersproject/web": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.6.1.tgz", - "integrity": "sha512-/vSyzaQlNXkO1WV+RneYKqCJwualcUdx/Z3gseVovZP0wIlOFcCE1hkRhKBH8ImKbGQbMl9EAAyJFrJu7V0aqA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", "funding": [ { "type": "individual", @@ -684,17 +684,17 @@ } ], "dependencies": { - "@ethersproject/base64": "^5.6.1", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/strings": "^5.6.1" + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" } }, "node_modules/@ethersproject/wordlists": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.6.1.tgz", - "integrity": "sha512-wiPRgBpNbNwCQFoCr8bcWO8o5I810cqO6mkdtKfLKFlLxeCWcnzDi4Alu8iyNzlhYuS9npCwivMbRWF19dyblw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", "funding": [ { "type": "individual", @@ -706,22 +706,28 @@ } ], "dependencies": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/hash": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/strings": "^5.6.1" + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" } }, "node_modules/@noble/hashes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz", - "integrity": "sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", @@ -736,12 +742,12 @@ "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -750,27 +756,27 @@ "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@tharsis/address-converter": { "version": "0.1.8", @@ -790,9 +796,14 @@ "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, "node_modules/@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "node_modules/@types/node": { + "version": "18.15.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.10.tgz", + "integrity": "sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==" }, "node_modules/aes-js": { "version": "3.0.0", @@ -819,9 +830,9 @@ } }, "node_modules/axios-retry": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.3.1.tgz", - "integrity": "sha512-RohAUQTDxBSWLFEnoIG/6bvmy8l3TfpkclgStjl5MDCMBDgapAWCmr1r/9harQfWC8bzLC8job6UcL1A1Yc+/Q==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.4.0.tgz", + "integrity": "sha512-VdgaP+gHH4iQYCCNUWF2pcqeciVOdGrBBAYUfTY+wPcO5Ltvp/37MLFNCmJKo7Gj3SHvCSdL8ouI1qLYJN3liA==", "dependencies": { "@babel/runtime": "^7.15.4", "is-retry-allowed": "^2.2.0" @@ -894,7 +905,7 @@ "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" }, "node_modules/bs58": { "version": "4.0.1", @@ -951,17 +962,17 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/compare-versions": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz", - "integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ==" + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.3.tgz", + "integrity": "sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A==" }, "node_modules/complex.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.0.tgz", - "integrity": "sha512-RdcrDz7YynXp/YXGwXIZ4MtmxXXniT5WmLFRX93cuXUX+0geWAqB8l1BoLXF+3BkzviVzHlpw27P9ow7MvlcmA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.1.tgz", + "integrity": "sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==", "engines": { "node": "*" }, @@ -973,7 +984,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/cosmjs-types": { "version": "0.4.1", @@ -998,45 +1009,30 @@ "sha3": "^2.1.1" } }, - "node_modules/crypto-addr-codec/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/dotenv": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", - "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==", + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", "engines": { "node": ">=12" } @@ -1068,15 +1064,15 @@ "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { "node": ">=0.8.0" } }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "funding": [ { "type": "individual", @@ -1107,22 +1103,35 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -1134,9 +1143,9 @@ } }, "node_modules/globalthis": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", - "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dependencies": { "define-properties": "^1.1.3" }, @@ -1161,11 +1170,33 @@ "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -1178,7 +1209,7 @@ "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -1207,7 +1238,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1227,9 +1258,9 @@ } }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dependencies": { "has": "^1.0.3" }, @@ -1259,7 +1290,7 @@ "node_modules/javascript-natural-sort": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" }, "node_modules/js-sha3": { "version": "0.8.0", @@ -1267,16 +1298,16 @@ "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, "node_modules/libsodium": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz", - "integrity": "sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ==" + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.11.tgz", + "integrity": "sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A==" }, "node_modules/libsodium-wrappers": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", - "integrity": "sha512-pO3F1Q9NPLB/MWIhehim42b/Fwb30JNScCNh8TcQ/kIc+qGLQch8ag8wb0keK3EP5kbGakk1H8Wwo7v+36rNQg==", + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz", + "integrity": "sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q==", "dependencies": { - "libsodium": "^0.7.0" + "libsodium": "^0.7.11" } }, "node_modules/link-module-alias": { @@ -1304,12 +1335,12 @@ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "node_modules/mathjs": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.4.3.tgz", - "integrity": "sha512-C50lWorCOplBec9Ik5fzhHuOx4G4+mtdz3r1G2I1/r8yj+CpYFXLXNqTdg59oKmIF1tKcIzpxlC4s2dGL7f3pg==", + "version": "10.6.4", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.6.4.tgz", + "integrity": "sha512-omQyvRE1jIy+3k2qsqkWASOcd45aZguXZDckr3HtnTYyXk5+2xpVfC3kATgbO2Srjxlqww3TVdhD0oUdZ/hiFA==", "dependencies": { - "@babel/runtime": "^7.17.8", - "complex.js": "^2.1.0", + "@babel/runtime": "^7.18.6", + "complex.js": "^2.1.1", "decimal.js": "^10.3.1", "escape-latex": "^1.2.0", "fraction.js": "^4.2.0", @@ -1322,7 +1353,7 @@ "mathjs": "bin/cli.js" }, "engines": { - "node": ">= 12" + "node": ">= 14" } }, "node_modules/minimalistic-assert": { @@ -1333,7 +1364,7 @@ "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" }, "node_modules/minimatch": { "version": "3.1.2", @@ -1347,9 +1378,12 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/object-keys": { "version": "1.1.1", @@ -1362,7 +1396,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { "wrappy": "1" } @@ -1370,7 +1404,7 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "engines": { "node": ">=0.10.0" } @@ -1405,11 +1439,6 @@ "pbts": "bin/pbts" } }, - "node_modules/protobufjs/node_modules/@types/node": { - "version": "13.13.52", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", - "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" - }, "node_modules/readonly-date": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", @@ -1418,7 +1447,7 @@ "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dependencies": { "resolve": "^1.1.6" }, @@ -1427,16 +1456,16 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -1456,9 +1485,23 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/scrypt-js": { "version": "3.0.1", @@ -1555,12 +1598,12 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz", - "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "engines": { "node": ">=8.3.0" }, @@ -1586,1114 +1629,5 @@ "symbol-observable": "^2.0.3" } } - }, - "dependencies": { - "@babel/runtime": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", - "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@confio/ics23": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@confio/ics23/-/ics23-0.6.8.tgz", - "integrity": "sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==", - "requires": { - "@noble/hashes": "^1.0.0", - "protobufjs": "^6.8.8" - } - }, - "@cosmjs/amino": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.7.tgz", - "integrity": "sha512-NTCUS3+u9bxwW8moC0N1RS4Gk/fs3JPHTKcp7ksH39V4+7uOKM2rGsyFAekiHNxYngnQ+1hU5x2tddS25soK6Q==", - "requires": { - "@cosmjs/crypto": "0.28.7", - "@cosmjs/encoding": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/utils": "0.28.7" - } - }, - "@cosmjs/crypto": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.7.tgz", - "integrity": "sha512-Fuq90nnqXQb6VvUadGtPy1X6arXY+yhqB95VzN8GM/ZdBLeigu6a3XjOvFki4lKO94QdLn2e9huD8dm4tDQDsA==", - "requires": { - "@cosmjs/encoding": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/utils": "0.28.7", - "@noble/hashes": "^1", - "bn.js": "^5.2.0", - "elliptic": "^6.5.3", - "libsodium-wrappers": "^0.7.6" - } - }, - "@cosmjs/encoding": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.7.tgz", - "integrity": "sha512-m2OuRhxux0YacvtjTocEOHyjnKO/KKGjqXlAY5HXGJpyntr+PIlJFdoS9tHax+1rNRrblZXwYIT+UqOs+kSk5Q==", - "requires": { - "base64-js": "^1.3.0", - "bech32": "^1.1.4", - "readonly-date": "^1.0.0" - } - }, - "@cosmjs/json-rpc": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.7.tgz", - "integrity": "sha512-2qgRL/9ih/ZYU/8LmtDQopaCKJBKqsuoSXfb2XO3yv6EkE28yiA8YAwLW5IrXjl1tfSiolHaBWarEASS/8Q5xA==", - "requires": { - "@cosmjs/stream": "0.28.7", - "xstream": "^11.14.0" - } - }, - "@cosmjs/math": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.7.tgz", - "integrity": "sha512-wSXIOOGVzgtRFsGwScpvQ6C6DUx97K7Ez3KyvRALNzspEnIoKPDgTXf438zhZnb+0+ERAwgdkb/6zWaDoVBfUA==", - "requires": { - "bn.js": "^5.2.0" - } - }, - "@cosmjs/proto-signing": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.7.tgz", - "integrity": "sha512-cOwDQVv95KnpHWlkrtxZ+2Q+z2Qp8Co//g+bwNc+YZ403iGHXSK5PRe3YsPa4f3elKJkJpHVi5oyzp4DB8p7mA==", - "requires": { - "@cosmjs/amino": "0.28.7", - "@cosmjs/crypto": "0.28.7", - "@cosmjs/encoding": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/utils": "0.28.7", - "cosmjs-types": "^0.4.0", - "long": "^4.0.0" - } - }, - "@cosmjs/socket": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.7.tgz", - "integrity": "sha512-+sCR5AzjjsKlA0K7m8YdxldMvgJa3Tqnx0AAyQXlNw2VXmW1zu9DkKZWW475XFhwO9UYfrdIsOe1vbhnUjQb5g==", - "requires": { - "@cosmjs/stream": "0.28.7", - "isomorphic-ws": "^4.0.1", - "ws": "^7", - "xstream": "^11.14.0" - } - }, - "@cosmjs/stargate": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.7.tgz", - "integrity": "sha512-3nY7czFUaY74gacbQAZuNji1qJWWQjv2C1cxYNqe7qAZAvCiz6A9adJVUmCJRL4peeG7tKiOny1J5IFbsgRu0g==", - "requires": { - "@confio/ics23": "^0.6.8", - "@cosmjs/amino": "0.28.7", - "@cosmjs/encoding": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/proto-signing": "0.28.7", - "@cosmjs/stream": "0.28.7", - "@cosmjs/tendermint-rpc": "0.28.7", - "@cosmjs/utils": "0.28.7", - "cosmjs-types": "^0.4.0", - "long": "^4.0.0", - "protobufjs": "~6.11.3", - "xstream": "^11.14.0" - } - }, - "@cosmjs/stream": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.7.tgz", - "integrity": "sha512-zTMZadlpmxAMtKUHX/dnYs1STung392+P50WgSUAjllG7CZYl7SAY03AKgc+aoF1ohYQcgH9H7VSgabEgLPhmg==", - "requires": { - "xstream": "^11.14.0" - } - }, - "@cosmjs/tendermint-rpc": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.7.tgz", - "integrity": "sha512-xhIVJL3ol+fZxywP76Ik9pHqCvBdU/BGAw6p48mhla3L3xNcFN2Nf+EnJWcIZPqZl8bHm5QHzPk9VqVFVYCMCw==", - "requires": { - "@cosmjs/crypto": "0.28.7", - "@cosmjs/encoding": "0.28.7", - "@cosmjs/json-rpc": "0.28.7", - "@cosmjs/math": "0.28.7", - "@cosmjs/socket": "0.28.7", - "@cosmjs/stream": "0.28.7", - "@cosmjs/utils": "0.28.7", - "axios": "^0.21.2", - "readonly-date": "^1.0.0", - "xstream": "^11.14.0" - }, - "dependencies": { - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - } - } - }, - "@cosmjs/utils": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.7.tgz", - "integrity": "sha512-0ya5mRaDY956n87dKjx6wfqgH/uEfrZyMswIhcEPqQPegwp4FDd6cwJtSdv/d5S7fgvoEs2UABvUFNzCT5C0hg==" - }, - "@ethersproject/abstract-provider": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.6.1.tgz", - "integrity": "sha512-BxlIgogYJtp1FS8Muvj8YfdClk3unZH0vRMVX791Z9INBNT/kuACZ9GzaY1Y4yFq+YSy6/w4gzj3HCRKrK9hsQ==", - "requires": { - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/networks": "^5.6.3", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/transactions": "^5.6.2", - "@ethersproject/web": "^5.6.1" - } - }, - "@ethersproject/abstract-signer": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.6.2.tgz", - "integrity": "sha512-n1r6lttFBG0t2vNiI3HoWaS/KdOt8xyDjzlP2cuevlWLG6EX0OwcKLyG/Kp/cuwNxdy/ous+R/DEMdTUwWQIjQ==", - "requires": { - "@ethersproject/abstract-provider": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0" - } - }, - "@ethersproject/address": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.1.tgz", - "integrity": "sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q==", - "requires": { - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/rlp": "^5.6.1" - } - }, - "@ethersproject/base64": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.6.1.tgz", - "integrity": "sha512-qB76rjop6a0RIYYMiB4Eh/8n+Hxu2NIZm8S/Q7kNo5pmZfXhHGHmS4MinUainiBC54SCyRnwzL+KZjj8zbsSsw==", - "requires": { - "@ethersproject/bytes": "^5.6.1" - } - }, - "@ethersproject/basex": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.6.1.tgz", - "integrity": "sha512-a52MkVz4vuBXR06nvflPMotld1FJWSj2QT0985v7P/emPZO00PucFAkbcmq2vpVU7Ts7umKiSI6SppiLykVWsA==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/properties": "^5.6.0" - } - }, - "@ethersproject/bignumber": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.6.2.tgz", - "integrity": "sha512-v7+EEUbhGqT3XJ9LMPsKvXYHFc8eHxTowFCG/HgJErmq4XHJ2WR7aeyICg3uTOAQ7Icn0GFHAohXEhxQHq4Ubw==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "bn.js": "^5.2.1" - } - }, - "@ethersproject/bytes": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.6.1.tgz", - "integrity": "sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g==", - "requires": { - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/constants": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.6.1.tgz", - "integrity": "sha512-QSq9WVnZbxXYFftrjSjZDUshp6/eKp6qrtdBtUCm0QxCV5z1fG/w3kdlcsjMCQuQHUnAclKoK7XpXMezhRDOLg==", - "requires": { - "@ethersproject/bignumber": "^5.6.2" - } - }, - "@ethersproject/hash": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.6.1.tgz", - "integrity": "sha512-L1xAHurbaxG8VVul4ankNX5HgQ8PNCTrnVXEiFnE9xoRnaUcgfD12tZINtDinSllxPLCtGwguQxJ5E6keE84pA==", - "requires": { - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/strings": "^5.6.1" - } - }, - "@ethersproject/hdnode": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.6.2.tgz", - "integrity": "sha512-tERxW8Ccf9CxW2db3WsN01Qao3wFeRsfYY9TCuhmG0xNpl2IO8wgXU3HtWIZ49gUWPggRy4Yg5axU0ACaEKf1Q==", - "requires": { - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/basex": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/pbkdf2": "^5.6.1", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/sha2": "^5.6.1", - "@ethersproject/signing-key": "^5.6.2", - "@ethersproject/strings": "^5.6.1", - "@ethersproject/transactions": "^5.6.2", - "@ethersproject/wordlists": "^5.6.1" - } - }, - "@ethersproject/json-wallets": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.6.1.tgz", - "integrity": "sha512-KfyJ6Zwz3kGeX25nLihPwZYlDqamO6pfGKNnVMWWfEVVp42lTfCZVXXy5Ie8IZTN0HKwAngpIPi7gk4IJzgmqQ==", - "requires": { - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/address": "^5.6.1", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/hdnode": "^5.6.2", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/pbkdf2": "^5.6.1", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/random": "^5.6.1", - "@ethersproject/strings": "^5.6.1", - "@ethersproject/transactions": "^5.6.2", - "aes-js": "3.0.0", - "scrypt-js": "3.0.1" - } - }, - "@ethersproject/keccak256": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.6.1.tgz", - "integrity": "sha512-bB7DQHCTRDooZZdL3lk9wpL0+XuG3XLGHLh3cePnybsO3V0rdCAOQGpn/0R3aODmnTOOkCATJiD2hnL+5bwthA==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "js-sha3": "0.8.0" - } - }, - "@ethersproject/logger": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.6.0.tgz", - "integrity": "sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg==" - }, - "@ethersproject/networks": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.6.3.tgz", - "integrity": "sha512-QZxRH7cA5Ut9TbXwZFiCyuPchdWi87ZtVNHWZd0R6YFgYtes2jQ3+bsslJ0WdyDe0i6QumqtoYqvY3rrQFRZOQ==", - "requires": { - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/pbkdf2": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.6.1.tgz", - "integrity": "sha512-k4gRQ+D93zDRPNUfmduNKq065uadC2YjMP/CqwwX5qG6R05f47boq6pLZtV/RnC4NZAYOPH1Cyo54q0c9sshRQ==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/sha2": "^5.6.1" - } - }, - "@ethersproject/properties": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.6.0.tgz", - "integrity": "sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg==", - "requires": { - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/random": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.6.1.tgz", - "integrity": "sha512-/wtPNHwbmng+5yi3fkipA8YBT59DdkGRoC2vWk09Dci/q5DlgnMkhIycjHlavrvrjJBkFjO/ueLyT+aUDfc4lA==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/rlp": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.6.1.tgz", - "integrity": "sha512-uYjmcZx+DKlFUk7a5/W9aQVaoEC7+1MOBgNtvNg13+RnuUwT4F0zTovC0tmay5SmRslb29V1B7Y5KCri46WhuQ==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/sha2": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.6.1.tgz", - "integrity": "sha512-5K2GyqcW7G4Yo3uenHegbXRPDgARpWUiXc6RiF7b6i/HXUoWlb7uCARh7BAHg7/qT/Q5ydofNwiZcim9qpjB6g==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "hash.js": "1.1.7" - } - }, - "@ethersproject/signing-key": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.6.2.tgz", - "integrity": "sha512-jVbu0RuP7EFpw82vHcL+GP35+KaNruVAZM90GxgQnGqB6crhBqW/ozBfFvdeImtmb4qPko0uxXjn8l9jpn0cwQ==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "bn.js": "^5.2.1", - "elliptic": "6.5.4", - "hash.js": "1.1.7" - } - }, - "@ethersproject/strings": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.6.1.tgz", - "integrity": "sha512-2X1Lgk6Jyfg26MUnsHiT456U9ijxKUybz8IM1Vih+NJxYtXhmvKBcHOmvGqpFSVJ0nQ4ZCoIViR8XlRw1v/+Cw==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/constants": "^5.6.1", - "@ethersproject/logger": "^5.6.0" - } - }, - "@ethersproject/transactions": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.6.2.tgz", - "integrity": "sha512-BuV63IRPHmJvthNkkt9G70Ullx6AcM+SDc+a8Aw/8Yew6YwT51TcBKEp1P4oOQ/bP25I18JJr7rcFRgFtU9B2Q==", - "requires": { - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/constants": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/rlp": "^5.6.1", - "@ethersproject/signing-key": "^5.6.2" - } - }, - "@ethersproject/wallet": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.6.2.tgz", - "integrity": "sha512-lrgh0FDQPuOnHcF80Q3gHYsSUODp6aJLAdDmDV0xKCN/T7D99ta1jGVhulg3PY8wiXEngD0DfM0I2XKXlrqJfg==", - "requires": { - "@ethersproject/abstract-provider": "^5.6.1", - "@ethersproject/abstract-signer": "^5.6.2", - "@ethersproject/address": "^5.6.1", - "@ethersproject/bignumber": "^5.6.2", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/hash": "^5.6.1", - "@ethersproject/hdnode": "^5.6.2", - "@ethersproject/json-wallets": "^5.6.1", - "@ethersproject/keccak256": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/random": "^5.6.1", - "@ethersproject/signing-key": "^5.6.2", - "@ethersproject/transactions": "^5.6.2", - "@ethersproject/wordlists": "^5.6.1" - } - }, - "@ethersproject/web": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.6.1.tgz", - "integrity": "sha512-/vSyzaQlNXkO1WV+RneYKqCJwualcUdx/Z3gseVovZP0wIlOFcCE1hkRhKBH8ImKbGQbMl9EAAyJFrJu7V0aqA==", - "requires": { - "@ethersproject/base64": "^5.6.1", - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/strings": "^5.6.1" - } - }, - "@ethersproject/wordlists": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.6.1.tgz", - "integrity": "sha512-wiPRgBpNbNwCQFoCr8bcWO8o5I810cqO6mkdtKfLKFlLxeCWcnzDi4Alu8iyNzlhYuS9npCwivMbRWF19dyblw==", - "requires": { - "@ethersproject/bytes": "^5.6.1", - "@ethersproject/hash": "^5.6.1", - "@ethersproject/logger": "^5.6.0", - "@ethersproject/properties": "^5.6.0", - "@ethersproject/strings": "^5.6.1" - } - }, - "@noble/hashes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz", - "integrity": "sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg==" - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" - }, - "@tharsis/address-converter": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@tharsis/address-converter/-/address-converter-0.1.8.tgz", - "integrity": "sha512-z7zdNczV8RIzBNxzIzRFhC5ujiQ3Lt04At9rooo2pL6QONDDMMLxsqH3o28ie80k5DXSXaMJ6gffATeehxwAkw==", - "requires": { - "bech32": "^2.0.0", - "crypto-addr-codec": "^0.1.7", - "link-module-alias": "^1.2.0", - "shx": "^0.3.4" - }, - "dependencies": { - "bech32": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", - "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" - } - } - }, - "@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, - "aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", - "requires": { - "follow-redirects": "^1.14.8" - } - }, - "axios-retry": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.3.1.tgz", - "integrity": "sha512-RohAUQTDxBSWLFEnoIG/6bvmy8l3TfpkclgStjl5MDCMBDgapAWCmr1r/9harQfWC8bzLC8job6UcL1A1Yc+/Q==", - "requires": { - "@babel/runtime": "^7.15.4", - "is-retry-allowed": "^2.2.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" - }, - "big-integer": { - "version": "1.6.36", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz", - "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==" - }, - "blakejs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", - "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" - }, - "bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "requires": { - "base-x": "^3.0.2" - } - }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "compare-versions": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz", - "integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ==" - }, - "complex.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.0.tgz", - "integrity": "sha512-RdcrDz7YynXp/YXGwXIZ4MtmxXXniT5WmLFRX93cuXUX+0geWAqB8l1BoLXF+3BkzviVzHlpw27P9ow7MvlcmA==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "cosmjs-types": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.4.1.tgz", - "integrity": "sha512-I7E/cHkIgoJzMNQdFF0YVqPlaTqrqKHrskuSTIqlEyxfB5Lf3WKCajSXVK2yHOfOFfSux/RxEdpMzw/eO4DIog==", - "requires": { - "long": "^4.0.0", - "protobufjs": "~6.11.2" - } - }, - "crypto-addr-codec": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz", - "integrity": "sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg==", - "requires": { - "base-x": "^3.0.8", - "big-integer": "1.6.36", - "blakejs": "^1.1.0", - "bs58": "^4.0.1", - "ripemd160-min": "0.0.6", - "safe-buffer": "^5.2.0", - "sha3": "^2.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "dotenv": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", - "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==" - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - } - } - }, - "escape-latex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" - }, - "fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globalthis": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", - "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", - "requires": { - "define-properties": "^1.1.3" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "requires": { - "has": "^1.0.3" - } - }, - "is-retry-allowed": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", - "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==" - }, - "isomorphic-ws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", - "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", - "requires": {} - }, - "javascript-natural-sort": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" - }, - "js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" - }, - "libsodium": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz", - "integrity": "sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ==" - }, - "libsodium-wrappers": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz", - "integrity": "sha512-pO3F1Q9NPLB/MWIhehim42b/Fwb30JNScCNh8TcQ/kIc+qGLQch8ag8wb0keK3EP5kbGakk1H8Wwo7v+36rNQg==", - "requires": { - "libsodium": "^0.7.0" - } - }, - "link-module-alias": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/link-module-alias/-/link-module-alias-1.2.0.tgz", - "integrity": "sha512-ahPjXepbSVKbahTB6LxR//VHm8HPfI+QQygCH+E82spBY4HR5VPJTvlhKBc9F7muVxnS6C1rRfoPOXAbWO/fyw==", - "requires": { - "chalk": "^2.4.1" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "mathjs": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.4.3.tgz", - "integrity": "sha512-C50lWorCOplBec9Ik5fzhHuOx4G4+mtdz3r1G2I1/r8yj+CpYFXLXNqTdg59oKmIF1tKcIzpxlC4s2dGL7f3pg==", - "requires": { - "@babel/runtime": "^7.17.8", - "complex.js": "^2.1.0", - "decimal.js": "^10.3.1", - "escape-latex": "^1.2.0", - "fraction.js": "^4.2.0", - "javascript-natural-sort": "^0.7.1", - "seedrandom": "^3.0.5", - "tiny-emitter": "^2.1.0", - "typed-function": "^2.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "dependencies": { - "@types/node": { - "version": "13.13.52", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz", - "integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==" - } - } - }, - "readonly-date": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", - "integrity": "sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==" - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "requires": { - "resolve": "^1.1.6" - } - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "ripemd160-min": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz", - "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" - }, - "seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" - }, - "sha3": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", - "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", - "requires": { - "buffer": "6.0.3" - } - }, - "shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "shx": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", - "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", - "requires": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "symbol-observable": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-2.0.3.tgz", - "integrity": "sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==" - }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, - "typed-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", - "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz", - "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==", - "requires": {} - }, - "xstream": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/xstream/-/xstream-11.14.0.tgz", - "integrity": "sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw==", - "requires": { - "globalthis": "^1.0.1", - "symbol-observable": "^2.0.3" - } - } } } From 7812bc037f43ebb0b4cb3f339a1b45dd0c7dd927 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sun, 26 Mar 2023 14:18:48 +0100 Subject: [PATCH 805/862] Update dependencies to latest versions --- package-lock.json | 247 +++++++++++++++++++++++++--------------- package.json | 18 +-- src/utils/Validator.mjs | 4 +- 3 files changed, 166 insertions(+), 103 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75c07edee..49d26f7d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,17 +8,17 @@ "name": "restake", "version": "0.1.0", "dependencies": { - "@cosmjs/proto-signing": "^0.28.1", - "@cosmjs/stargate": "^0.28.1", - "@ethersproject/wallet": "^5.6.2", + "@cosmjs/proto-signing": "^0.30.1", + "@cosmjs/stargate": "^0.30.1", + "@ethersproject/wallet": "^5.7.0", "@tharsis/address-converter": "^0.1.8", - "axios": "^0.26.0", - "axios-retry": "^3.3.1", - "compare-versions": "^5.0.1", - "cosmjs-types": "^0.4.1", - "dotenv": "^16.0.0", + "axios": "^1.3.4", + "axios-retry": "^3.4.0", + "compare-versions": "^5.0.3", + "cosmjs-types": "^0.7.1", + "dotenv": "^16.0.3", "lodash": "^4.17.21", - "mathjs": "^10.4.3" + "mathjs": "^11.7.0" } }, "node_modules/@babel/runtime": { @@ -42,34 +42,34 @@ } }, "node_modules/@cosmjs/amino": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.13.tgz", - "integrity": "sha512-IHnH2zGwaY69qT4mVAavr/pfzx6YE+ud1NHJbvVePlbGiz68CXTi5LHR+K0lrKB5mQ7E+ZErWz2mw5U/x+V1wQ==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.30.1.tgz", + "integrity": "sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==", "dependencies": { - "@cosmjs/crypto": "0.28.13", - "@cosmjs/encoding": "0.28.13", - "@cosmjs/math": "0.28.13", - "@cosmjs/utils": "0.28.13" + "@cosmjs/crypto": "^0.30.1", + "@cosmjs/encoding": "^0.30.1", + "@cosmjs/math": "^0.30.1", + "@cosmjs/utils": "^0.30.1" } }, "node_modules/@cosmjs/crypto": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.13.tgz", - "integrity": "sha512-ynKfM0q/tMBQMHJby6ad8lR3gkgBKaelQhIsCZTjClsnuC7oYT9y3ThSZCUWr7Pa9h0J8ahU2YV2oFWFVWJQzQ==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.30.1.tgz", + "integrity": "sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ==", "dependencies": { - "@cosmjs/encoding": "0.28.13", - "@cosmjs/math": "0.28.13", - "@cosmjs/utils": "0.28.13", + "@cosmjs/encoding": "^0.30.1", + "@cosmjs/math": "^0.30.1", + "@cosmjs/utils": "^0.30.1", "@noble/hashes": "^1", "bn.js": "^5.2.0", - "elliptic": "^6.5.3", + "elliptic": "^6.5.4", "libsodium-wrappers": "^0.7.6" } }, "node_modules/@cosmjs/encoding": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.13.tgz", - "integrity": "sha512-jtXbAYtV77rLHxoIrjGFsvgGjeTKttuHRv6cvuy3toCZzY7JzTclKH5O2g36IIE4lXwD9xwuhGJ2aa6A3dhNkA==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.30.1.tgz", + "integrity": "sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ==", "dependencies": { "base64-js": "^1.3.0", "bech32": "^1.1.4", @@ -77,86 +77,86 @@ } }, "node_modules/@cosmjs/json-rpc": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.13.tgz", - "integrity": "sha512-fInSvg7x9P6p+GWqet+TMhrMTM3OWWdLJOGS5w2ryubMjgpR1rLiAx77MdTNkArW+/6sUwku0sN4veM4ENQu6A==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.30.1.tgz", + "integrity": "sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ==", "dependencies": { - "@cosmjs/stream": "0.28.13", + "@cosmjs/stream": "^0.30.1", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/math": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.13.tgz", - "integrity": "sha512-PDpL8W/kbyeWi0mQ2OruyqE8ZUAdxPs1xCbDX3WXJwy2oU+X2UTbkuweJHVpS9CIqmZulBoWQAmlf6t6zr1N/g==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.30.1.tgz", + "integrity": "sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q==", "dependencies": { "bn.js": "^5.2.0" } }, "node_modules/@cosmjs/proto-signing": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.13.tgz", - "integrity": "sha512-nSl/2ZLsUJYz3Ad0RY3ihZUgRHIow2OnYqKsESMu+3RA/jTi9bDYhiBu8mNMHI0xrEJry918B2CyI56pOUHdPQ==", - "dependencies": { - "@cosmjs/amino": "0.28.13", - "@cosmjs/crypto": "0.28.13", - "@cosmjs/encoding": "0.28.13", - "@cosmjs/math": "0.28.13", - "@cosmjs/utils": "0.28.13", - "cosmjs-types": "^0.4.0", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.30.1.tgz", + "integrity": "sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ==", + "dependencies": { + "@cosmjs/amino": "^0.30.1", + "@cosmjs/crypto": "^0.30.1", + "@cosmjs/encoding": "^0.30.1", + "@cosmjs/math": "^0.30.1", + "@cosmjs/utils": "^0.30.1", + "cosmjs-types": "^0.7.1", "long": "^4.0.0" } }, "node_modules/@cosmjs/socket": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.13.tgz", - "integrity": "sha512-lavwGxQ5VdeltyhpFtwCRVfxeWjH5D5mmN7jgx9nuCf3XSFbTcOYxrk2pQ4usenu1Q1KZdL4Yl5RCNrJuHD9Ug==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.30.1.tgz", + "integrity": "sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow==", "dependencies": { - "@cosmjs/stream": "0.28.13", + "@cosmjs/stream": "^0.30.1", "isomorphic-ws": "^4.0.1", "ws": "^7", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/stargate": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.13.tgz", - "integrity": "sha512-dVBMazDz8/eActHsRcZjDHHptOBMqvibj5CFgEtZBp22gP6ASzoAUXTlkSVk5FBf4sfuUHoff6st134/+PGMAg==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.30.1.tgz", + "integrity": "sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog==", "dependencies": { "@confio/ics23": "^0.6.8", - "@cosmjs/amino": "0.28.13", - "@cosmjs/encoding": "0.28.13", - "@cosmjs/math": "0.28.13", - "@cosmjs/proto-signing": "0.28.13", - "@cosmjs/stream": "0.28.13", - "@cosmjs/tendermint-rpc": "0.28.13", - "@cosmjs/utils": "0.28.13", - "cosmjs-types": "^0.4.0", + "@cosmjs/amino": "^0.30.1", + "@cosmjs/encoding": "^0.30.1", + "@cosmjs/math": "^0.30.1", + "@cosmjs/proto-signing": "^0.30.1", + "@cosmjs/stream": "^0.30.1", + "@cosmjs/tendermint-rpc": "^0.30.1", + "@cosmjs/utils": "^0.30.1", + "cosmjs-types": "^0.7.1", "long": "^4.0.0", "protobufjs": "~6.11.3", "xstream": "^11.14.0" } }, "node_modules/@cosmjs/stream": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.13.tgz", - "integrity": "sha512-AnjtfwT8NwPPkd3lhZhjOlOzT0Kn9bgEu2IPOZjQ1nmG2bplsr6TJmnwn0dJxHT7UGtex17h6whKB5N4wU37Wg==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.30.1.tgz", + "integrity": "sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ==", "dependencies": { "xstream": "^11.14.0" } }, "node_modules/@cosmjs/tendermint-rpc": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.13.tgz", - "integrity": "sha512-GB+ZmfuJIGQm0hsRtLYjeR3lOxF7Z6XyCBR0cX5AAYOZzSEBJjevPgUHD6tLn8zIhvzxaW3/VKnMB+WmlxdH4w==", - "dependencies": { - "@cosmjs/crypto": "0.28.13", - "@cosmjs/encoding": "0.28.13", - "@cosmjs/json-rpc": "0.28.13", - "@cosmjs/math": "0.28.13", - "@cosmjs/socket": "0.28.13", - "@cosmjs/stream": "0.28.13", - "@cosmjs/utils": "0.28.13", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.30.1.tgz", + "integrity": "sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ==", + "dependencies": { + "@cosmjs/crypto": "^0.30.1", + "@cosmjs/encoding": "^0.30.1", + "@cosmjs/json-rpc": "^0.30.1", + "@cosmjs/math": "^0.30.1", + "@cosmjs/socket": "^0.30.1", + "@cosmjs/stream": "^0.30.1", + "@cosmjs/utils": "^0.30.1", "axios": "^0.21.2", "readonly-date": "^1.0.0", "xstream": "^11.14.0" @@ -171,9 +171,9 @@ } }, "node_modules/@cosmjs/utils": { - "version": "0.28.13", - "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.13.tgz", - "integrity": "sha512-dVeMBiyg+46x7XBZEfJK8yTihphbCFpjVYmLJVqmTsHfJwymQ65cpyW/C+V/LgWARGK8hWQ/aX9HM5Ao8QmMSg==" + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.30.1.tgz", + "integrity": "sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g==" }, "node_modules/@ethersproject/abstract-provider": { "version": "5.7.0", @@ -821,12 +821,19 @@ "node": ">=4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", + "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", "dependencies": { - "follow-redirects": "^1.14.8" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/axios-retry": { @@ -964,6 +971,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/compare-versions": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.3.tgz", @@ -987,9 +1005,9 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/cosmjs-types": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.4.1.tgz", - "integrity": "sha512-I7E/cHkIgoJzMNQdFF0YVqPlaTqrqKHrskuSTIqlEyxfB5Lf3WKCajSXVK2yHOfOFfSux/RxEdpMzw/eO4DIog==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.7.1.tgz", + "integrity": "sha512-qP89SGwi6YpvMTrM9CPzTfZ0JPNlXzgimqMLsa/ZjzW+L6MC8TCr6XmoWtFOT6GSfefvJLwFWq7YCtL456Bdzg==", "dependencies": { "long": "^4.0.0", "protobufjs": "~6.11.2" @@ -1029,6 +1047,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -1088,6 +1114,19 @@ } } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -1335,19 +1374,19 @@ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "node_modules/mathjs": { - "version": "10.6.4", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-10.6.4.tgz", - "integrity": "sha512-omQyvRE1jIy+3k2qsqkWASOcd45aZguXZDckr3HtnTYyXk5+2xpVfC3kATgbO2Srjxlqww3TVdhD0oUdZ/hiFA==", + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-11.7.0.tgz", + "integrity": "sha512-RCXtrP5xGIbl9PUc5+7QL81rBCUjzoIZ0ugNqKsarOUxg+x7deY0BzfNai+bGfUL/T+1uYq1xs5w2xVdL3lp0g==", "dependencies": { - "@babel/runtime": "^7.18.6", + "@babel/runtime": "^7.21.0", "complex.js": "^2.1.1", - "decimal.js": "^10.3.1", + "decimal.js": "^10.4.3", "escape-latex": "^1.2.0", "fraction.js": "^4.2.0", "javascript-natural-sort": "^0.7.1", "seedrandom": "^3.0.5", "tiny-emitter": "^2.1.0", - "typed-function": "^2.1.0" + "typed-function": "^4.1.0" }, "bin": { "mathjs": "bin/cli.js" @@ -1356,6 +1395,25 @@ "node": ">= 14" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -1439,6 +1497,11 @@ "pbts": "bin/pbts" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/readonly-date": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", @@ -1588,11 +1651,11 @@ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "node_modules/typed-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.1.0.tgz", - "integrity": "sha512-bctQIOqx2iVbWGDGPWwIm18QScpu2XRmkC19D8rQGFsjKSgteq/o1hTZvIG/wuDq8fanpBDrLkLq+aEN/6y5XQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.1.0.tgz", + "integrity": "sha512-DGwUl6cioBW5gw2L+6SMupGwH/kZOqivy17E4nsh1JI9fKF87orMmlQx3KISQPmg3sfnOUGlwVkroosvgddrlg==", "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/wrappy": { diff --git a/package.json b/package.json index f3a132884..4c6748125 100644 --- a/package.json +++ b/package.json @@ -3,17 +3,17 @@ "version": "0.1.0", "private": true, "dependencies": { - "@cosmjs/proto-signing": "^0.28.1", - "@cosmjs/stargate": "^0.28.1", - "@ethersproject/wallet": "^5.6.2", + "@cosmjs/proto-signing": "^0.30.1", + "@cosmjs/stargate": "^0.30.1", + "@ethersproject/wallet": "^5.7.0", "@tharsis/address-converter": "^0.1.8", - "axios": "^0.26.0", - "axios-retry": "^3.3.1", - "compare-versions": "^5.0.1", - "cosmjs-types": "^0.4.1", - "dotenv": "^16.0.0", + "axios": "^1.3.4", + "axios-retry": "^3.4.0", + "compare-versions": "^5.0.3", + "cosmjs-types": "^0.7.1", + "dotenv": "^16.0.3", "lodash": "^4.17.21", - "mathjs": "^10.4.3" + "mathjs": "^11.7.0" }, "scripts": { "dryrun": "node scripts/dryrun.mjs", diff --git a/src/utils/Validator.mjs b/src/utils/Validator.mjs index b74ba5255..3992d8a7b 100644 --- a/src/utils/Validator.mjs +++ b/src/utils/Validator.mjs @@ -1,10 +1,10 @@ -import { Bech32 } from '@cosmjs/encoding' +import Bech32 from "bech32"; const Validator = (network, data) => { const address = data.operator_address const { delegations } = data const totalSlashes = data.slashes?.length - const blockPeriod = data.missed_blocks_periods && data.missed_blocks_periods.slice(-1)[0] + const blockPeriod = data.missed_blocks_periods && data.missed_blocks_periods.slice(-1)[0] const uptime = blockPeriod?.blocks && ((blockPeriod.blocks - blockPeriod.missed) / blockPeriod.blocks) const missedBlocks = blockPeriod?.missed const totalTokens = delegations?.total_tokens_display From 2c09868f54baebd6f7bd0c1ed4f889a124fbb2c1 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 4 Apr 2023 20:39:35 +0100 Subject: [PATCH 806/862] Handle networks not included in networks.json --- src/autostake/index.mjs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index aed494ae0..4c3bfdae8 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -17,6 +17,7 @@ import EthSigner from '../utils/EthSigner.mjs'; export default function Autostake(mnemonic, opts) { opts = opts || {} + let failed = false if (!mnemonic) { timeStamp('Please provide a MNEMONIC environment variable') @@ -26,7 +27,10 @@ export default function Autostake(mnemonic, opts) { async function run(networkNames, networksOverridePath) { const networks = getNetworksData(networksOverridePath) for (const name of networkNames) { - if (name && !networks.map(el => el.name).includes(name)) return timeStamp('Invalid network name:', name) + if (name && !networks.map(el => el.name).includes(name)){ + // Assume network will be found in Chain Registry + networks.push({ name, path: name }) + } } const calls = networks.map(data => { return async () => { @@ -37,7 +41,7 @@ export default function Autostake(mnemonic, opts) { health.started('⚛') const results = await runWithRetry(data, health) const { success, skipped } = results[results.length - 1] || {} - if(!skipped){ + if(!skipped && !failed){ health.log(`Autostake ${success ? 'completed' : 'failed'} after ${results.length} attempt(s)`) results.forEach(({networkRunner, error}, index) => { health.log(`Attempt ${index + 1}:`) @@ -76,7 +80,7 @@ export default function Autostake(mnemonic, opts) { error = e.message } runners.push({ success: networkRunner?.didSucceed(), networkRunner, error, failedAddresses }) - if (!networkRunner?.didSucceed() && !networkRunner?.forceFail && retries < maxRetries) { + if (!networkRunner?.didSucceed() && !networkRunner?.forceFail && retries < maxRetries && !failed) { await logResults(health, networkRunner, error, `Failed attempt ${retries + 1}/${maxRetries + 1}, retrying in 30 seconds...`) await new Promise(r => setTimeout(r, 30 * 1000)); return await runWithRetry(data, health, retries + 1, runners) @@ -111,8 +115,12 @@ export default function Autostake(mnemonic, opts) { let config = { ...opts } try { await network.load() - } catch { - throw new Error('Unable to load network data for', network.name) + } catch(e) { + if(e.response.status === 404){ + failed = true + throw new Error(`${network.name} not found in Chain Registry`) + } + throw new Error(`Unable to load network data for ${network.name}`) } timeStamp('Loaded', network.prettyName) From 98df45f2590401aff8ce0fe32a1661d64f547e01 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 4 Apr 2023 20:39:51 +0100 Subject: [PATCH 807/862] Skip logs/throttle when grants loaded in a batch --- src/autostake/NetworkRunner.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index d91f6d394..18b4652bf 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -135,8 +135,10 @@ export default class NetworkRunner { } }) let grantedAddresses = await mapSync(grantCalls, this.opts.batchQueries, (batch, index) => { - timeStamp('...batch', index + 1) - return this.throttleQuery() + if(!allGrants){ + timeStamp('...batch', index + 1) + return this.throttleQuery() + } }) return _.compact(grantedAddresses.flat()) } @@ -314,7 +316,7 @@ export default class NetworkRunner { } async throttleQuery(){ - if(!this.opts.queryThrottle) return + if(!this.opts.queryThrottle) return await new Promise(r => setTimeout(r, this.opts.queryThrottle)); } From be327f0e91b00ca61760641a52dd305af3322516 Mon Sep 17 00:00:00 2001 From: ZENODE Date: Sun, 16 Apr 2023 15:07:05 +0200 Subject: [PATCH 808/862] Added gasPrice for Genesis L1, issue #728 --- src/networks.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 96fd76883..f71e30aca 100644 --- a/src/networks.json +++ b/src/networks.json @@ -215,7 +215,8 @@ }, { "name": "genesisl1", - "txTimeout": 120000 + "txTimeout": 120000, + "gasPrice": "51000000000el1" }, { "name": "tgrade", From 4bec23799eb76d98820cb6e076dfa0e90548799e Mon Sep 17 00:00:00 2001 From: schultzie Date: Tue, 2 May 2023 13:06:44 -0700 Subject: [PATCH 809/862] Add auto health check creation --- src/autostake/Health.mjs | 50 +++++++++++++++++++++++++++++----------- src/autostake/index.mjs | 4 ++-- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index aa2ebad15..631718650 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -4,51 +4,75 @@ import { timeStamp } from '../utils/Helpers.mjs' class Health { constructor(config, opts) { - const { address, uuid } = config || {} - const { dryRun } = opts || {} + const { address, uuid, name, api_key } = config || {} + const { dryRun, networkName } = opts || {} + console.log(networkName); this.address = address || 'https://hc-ping.com' this.uuid = uuid + this.name = name || networkName + this.api_key = api_key this.dryRun = dryRun this.logs = [] + this.pingUrl = this.getOrCreateHealthCheck(); } - started(...args){ + getOrCreateHealthCheck(...args) { + if (!this.api_key) return; + + let config = { + headers: { + "X-Api-Key": this.api_key, + } + } + + let data = { + "name": this.name, "channels": "*", "timeout": 43200, "grace": 86400, "unique": ["name"] + } + + try { + var response = await axios.post([this.address, 'api/v2/checks/'].join('/'), data, config).then((res) => res.data.pingUrl); + } catch (error) { + timeStamp("Health Check creation failed: " + error); + } + } + + started(...args) { timeStamp(...args) - if(this.uuid) timeStamp('Starting health', [this.address, this.uuid].join('/')) + if (this.uuid) timeStamp('Starting health', [this.address, this.uuid].join('/')) return this.ping('start', [args.join(' ')]) } - success(...args){ + success(...args) { timeStamp(...args) return this.ping(undefined, [...this.logs, args.join(' ')]) } - failed(...args){ + failed(...args) { timeStamp(...args) return this.ping('fail', [...this.logs, args.join(' ')]) } - log(...args){ + log(...args) { timeStamp(...args) this.logs = [...this.logs, args.join(' ')] } - addLogs(logs){ + addLogs(logs) { this.logs = this.logs.concat(logs) } - async sendLog(){ + async sendLog() { await this.ping('log', this.logs) this.logs = [] } - async ping(action, logs){ - if(!this.uuid) return - if(this.dryRun) return timeStamp('DRYRUN: Skipping health check ping') + async ping(action, logs) { + if (!this.uuid) return + if (this.dryRun) return timeStamp('DRYRUN: Skipping health check ping') return axios.request({ method: 'POST', - url: _.compact([this.address, this.uuid, action]).join('/'), + url: _.compact([this.address, this.uuid, action]).join('/'), data: logs.join("\n") }).catch(error => { timeStamp('Health ping failed', error.message) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 4c3bfdae8..545bcfe31 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -36,8 +36,8 @@ export default function Autostake(mnemonic, opts) { return async () => { if (networkNames && networkNames.length && !networkNames.includes(data.name)) return if (data.enabled === false) return - - const health = new Health(data.healthCheck, { dryRun: opts.dryRun }) + console.log(data); + const health = new Health(data.healthCheck, { dryRun: opts.dryRun, networkName: data.name }) health.started('⚛') const results = await runWithRetry(data, health) const { success, skipped } = results[results.length - 1] || {} From a8c9c4efbc48af4173e308536637a16be80b2156 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Tue, 2 May 2023 13:33:48 -0700 Subject: [PATCH 810/862] Update check Signed-off-by: Dylan Schultz --- src/autostake/Health.mjs | 47 +++++++++++++++++----------------- src/autostake/index.mjs | 2 +- src/networks.local.json.sample | 8 ------ 3 files changed, 25 insertions(+), 32 deletions(-) delete mode 100644 src/networks.local.json.sample diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 631718650..525c95cc8 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -4,38 +4,17 @@ import { timeStamp } from '../utils/Helpers.mjs' class Health { constructor(config, opts) { - const { address, uuid, name, api_key } = config || {} + const { address, uuid, name, apiKey } = config || {} const { dryRun, networkName } = opts || {} - console.log(networkName); this.address = address || 'https://hc-ping.com' this.uuid = uuid this.name = name || networkName - this.api_key = api_key + this.apiKey = apiKey this.dryRun = dryRun this.logs = [] this.pingUrl = this.getOrCreateHealthCheck(); } - getOrCreateHealthCheck(...args) { - if (!this.api_key) return; - - let config = { - headers: { - "X-Api-Key": this.api_key, - } - } - - let data = { - "name": this.name, "channels": "*", "timeout": 43200, "grace": 86400, "unique": ["name"] - } - - try { - var response = await axios.post([this.address, 'api/v2/checks/'].join('/'), data, config).then((res) => res.data.pingUrl); - } catch (error) { - timeStamp("Health Check creation failed: " + error); - } - } - started(...args) { timeStamp(...args) if (this.uuid) timeStamp('Starting health', [this.address, this.uuid].join('/')) @@ -61,6 +40,28 @@ class Health { this.logs = this.logs.concat(logs) } + async getOrCreateHealthCheck(...args) { + if (!this.apiKey) return; + + let config = { + headers: { + "X-Api-Key": this.apiKey, + } + } + + let data = { + "name": this.name, "channels": "*", "timeout": 43200, "grace": 86400, "unique": ["name"] + } + + try { + await axios.post([this.address, 'api/v2/checks/'].join('/'), data, config).then((res) => { + this.uuid = res.data.ping_url.split('/')[4]; + }); + } catch (error) { + timeStamp("Health Check creation failed: " + error); + } + } + async sendLog() { await this.ping('log', this.logs) this.logs = [] diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 545bcfe31..309d54c29 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -36,7 +36,7 @@ export default function Autostake(mnemonic, opts) { return async () => { if (networkNames && networkNames.length && !networkNames.includes(data.name)) return if (data.enabled === false) return - console.log(data); + const health = new Health(data.healthCheck, { dryRun: opts.dryRun, networkName: data.name }) health.started('⚛') const results = await runWithRetry(data, health) diff --git a/src/networks.local.json.sample b/src/networks.local.json.sample deleted file mode 100644 index b5f190270..000000000 --- a/src/networks.local.json.sample +++ /dev/null @@ -1,8 +0,0 @@ -{ - "akash": { - "prettyName": "Akash", - "restUrl": [ - "https://rest.validator.com/osmosis" - ] - } -} From 3ac7ea78db8b1abda54602c291b07d4b3e6b5854 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Tue, 2 May 2023 13:51:36 -0700 Subject: [PATCH 811/862] cleanup Signed-off-by: Dylan Schultz --- src/autostake/Health.mjs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 525c95cc8..38e46e9ba 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -7,12 +7,19 @@ class Health { const { address, uuid, name, apiKey } = config || {} const { dryRun, networkName } = opts || {} this.address = address || 'https://hc-ping.com' - this.uuid = uuid this.name = name || networkName this.apiKey = apiKey this.dryRun = dryRun + this.uuid = uuid this.logs = [] - this.pingUrl = this.getOrCreateHealthCheck(); + this.getOrCreateHealthCheck() + + if (address) { + // This is necessary as the default provider - hc-ping.com - has a built in ping mechanism + // whereas providing self-hosted addresses do NOT. + // https://healthchecks.selfhosted.com/ping/{uuid} rather than https://hc-ping.com/{uuid} + this.address = this.address + "/ping" + } } started(...args) { @@ -55,10 +62,10 @@ class Health { try { await axios.post([this.address, 'api/v2/checks/'].join('/'), data, config).then((res) => { - this.uuid = res.data.ping_url.split('/')[4]; + this.uuid = res.data.ping_url.split('/')[4] }); } catch (error) { - timeStamp("Health Check creation failed: " + error); + timeStamp("Health Check creation failed: " + error) } } From 6d3d40e83e088732449c800c1a3a51b6712883e4 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Tue, 2 May 2023 13:56:08 -0700 Subject: [PATCH 812/862] Add sample back in Signed-off-by: Dylan Schultz --- src/networks.local.json.sample | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/networks.local.json.sample diff --git a/src/networks.local.json.sample b/src/networks.local.json.sample new file mode 100644 index 000000000..b5f190270 --- /dev/null +++ b/src/networks.local.json.sample @@ -0,0 +1,8 @@ +{ + "akash": { + "prettyName": "Akash", + "restUrl": [ + "https://rest.validator.com/osmosis" + ] + } +} From 554c8d58e08219ced125ba625f41c57b0fbfc151 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Tue, 2 May 2023 13:59:08 -0700 Subject: [PATCH 813/862] Move UUID back Signed-off-by: Dylan Schultz --- src/autostake/Health.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 38e46e9ba..2a828208c 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -7,10 +7,10 @@ class Health { const { address, uuid, name, apiKey } = config || {} const { dryRun, networkName } = opts || {} this.address = address || 'https://hc-ping.com' + this.uuid = uuid this.name = name || networkName this.apiKey = apiKey this.dryRun = dryRun - this.uuid = uuid this.logs = [] this.getOrCreateHealthCheck() From 9693c2e0ddba11c20fda7cf8987dd4b0f505cf39 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Wed, 3 May 2023 08:29:46 -0700 Subject: [PATCH 814/862] Add config for gracePeriod and timeout Signed-off-by: Dylan Schultz --- src/autostake/Health.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 2a828208c..3521ca462 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -4,11 +4,13 @@ import { timeStamp } from '../utils/Helpers.mjs' class Health { constructor(config, opts) { - const { address, uuid, name, apiKey } = config || {} + const { address, uuid, name, apiKey, timeout, gracePeriod } = config || {} const { dryRun, networkName } = opts || {} this.address = address || 'https://hc-ping.com' - this.uuid = uuid this.name = name || networkName + this.gracePeriod = gracePeriod || 86400 // default 24 hours + this.timeout = timeout || 86400 // default 24 hours + this.uuid = uuid this.apiKey = apiKey this.dryRun = dryRun this.logs = [] @@ -57,7 +59,7 @@ class Health { } let data = { - "name": this.name, "channels": "*", "timeout": 43200, "grace": 86400, "unique": ["name"] + "name": this.name, "channels": "*", "timeout": this.timeout, "grace": this.gracePeriod, "unique": ["name"] } try { From 9d1906961674418a9c3b25e8c269612266b37a18 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Wed, 3 May 2023 08:40:41 -0700 Subject: [PATCH 815/862] Add documentation for auto-healthchecks Signed-off-by: Dylan Schultz --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 4342a3281..a0003ad94 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,24 @@ Add your Check UUID to the relevant network in your `networks.local.json` config } ``` +If you wish for your health checks to be created automatically, you can provide an [API key](https://healthchecks.io/docs/api/) to manage it for you. +The default behavior is for the check to be named after the network, but this can be overridden with the `name` property. + +**Note that the `uuid` is not necessary when using an `apiKey` to create checks automatically.** + +```JSON +{ + "cheqd": { + "healthCheck": { + "apiKey": "12_CanM1Q3T72uGH4kc32G14BdA4Emc4y", + "name": "cheqd every 12 hours", // optional, defaults to the network name + "timeout": 43200, // optional, expected seconds between each run + "gracePeriod": 86400, // optional, grace period seconds before it notifies + } + } +} +``` + ### Submitting your operator #### Setup your REStake operator From 5ddc22d47241ce93efc3d1f21275b04fba422044 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 9 Jun 2023 11:34:48 +0100 Subject: [PATCH 816/862] Increase Juno fees to match network minimum --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index f71e30aca..5f3d54f03 100644 --- a/src/networks.json +++ b/src/networks.json @@ -11,7 +11,7 @@ }, { "name": "juno", - "gasPrice": "0.0025ujuno", + "gasPrice": "0.0750ujuno", "ownerAddress": "junovaloper19ur8r2l25qhsy9xvej5zgpuc5cpn6syydmwtrt", "autostake": { "batchTxs": 25 From d3be332b694ddc50113cc8b74db5206187530708 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 12 Jun 2023 15:14:40 +0100 Subject: [PATCH 817/862] Use EthSigner when slip44 overridden to 60 --- src/autostake/index.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 309d54c29..02bb326e8 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -189,7 +189,7 @@ export default function Autostake(mnemonic, opts) { hdPaths: [hdPath] }); - if (network.slip44 === 60) { + if (slip44 === 60) { const ethSigner = EthWallet.fromMnemonic(mnemonic); signer = EthSigner(signer, ethSigner, network.prefix) } From 52c978e0ca53c7b4dca66715bd055e9a57011c6f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 27 Jun 2023 22:37:56 +0100 Subject: [PATCH 818/862] Handle null delegation response --- src/utils/QueryClient.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index bb50cb2aa..cc2c1b195 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -44,7 +44,9 @@ const QueryClient = async (chainId, restUrls, opts) => { return getAllPages((nextKey) => { return getValidatorDelegations(validatorAddress, pageSize, opts, nextKey); }, pageCallback).then((pages) => { - return pages.map((el) => el.delegation_responses).flat(); + return _.compact(pages.map((el) => { + return el.delegation_responses + }).flat()); }); } From fbad1651102e63a716d083897682edc8a9398d0b Mon Sep 17 00:00:00 2001 From: woof-chihuahua <123602654+woof-chihuahua@users.noreply.github.com> Date: Fri, 18 Aug 2023 10:43:04 +0100 Subject: [PATCH 819/862] Update networks.json fixing Chihuahua gasPrices --- src/networks.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/networks.json b/src/networks.json index 5f3d54f03..c992e9189 100644 --- a/src/networks.json +++ b/src/networks.json @@ -29,8 +29,8 @@ { "name": "chihuahua", "ownerAddress": "chihuahuavaloper19vwcee000fhazmpt4ultvnnkhfh23ppwxll8zz", - "gasPrice": "1uhuahua", - "gasPricePrefer": "1uhuahua" + "gasPrice": "500uhuahua", + "gasPricePrefer": "500uhuahua" }, { "name": "gravitybridge", From d4676918c3e65cb83f4661603c375192578bbf98 Mon Sep 17 00:00:00 2001 From: Vee Date: Fri, 1 Sep 2023 20:07:27 +0900 Subject: [PATCH 820/862] utils: Helpers: Support non-expiring grants Fixes: #692 --- src/utils/Helpers.mjs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 6790d2902..08d56ebe3 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -72,7 +72,15 @@ export function parseGrants(grants, grantee, granter) { el.authorization.msg === "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward") ) { - return Date.parse(el.expiration) > new Date(); + if (el.expiration === null) { + timeStamp("claimGrant does NOT expire!") + return true; + } else if (Date.parse(el.expiration) > new Date()) { + return true; + } else { + timeStamp("claimGrant expired!") + return false; + } } else { return false; } @@ -90,7 +98,15 @@ export function parseGrants(grants, grantee, granter) { "/cosmos.staking.v1beta1.MsgDelegate" )) ) { - return Date.parse(el.expiration) > new Date(); + if (el.expiration === null) { + timeStamp("stakeGrant does NOT expire!") + return true; + } else if (Date.parse(el.expiration) > new Date()) { + return true; + } else { + timeStamp("stakeGrant expired!") + return false; + } } else { return false; } From 25f7a56b7072aecc01c3d2b746b0dab4f390a089 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 24 Oct 2023 22:01:00 +0100 Subject: [PATCH 821/862] Remove unused code and unclear logs --- src/utils/Helpers.mjs | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 08d56ebe3..182de39d1 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -62,29 +62,6 @@ export function buildExecableMessage(type, typeUrl, value, shouldExec){ } export function parseGrants(grants, grantee, granter) { - // claimGrant is removed but we track for now to allow revoke - const claimGrant = grants.find((el) => { - if ( - (!el.grantee || el.grantee === grantee) && - (!el.granter || el.granter === granter) && - (el.authorization["@type"] === - "/cosmos.authz.v1beta1.GenericAuthorization" && - el.authorization.msg === - "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward") - ) { - if (el.expiration === null) { - timeStamp("claimGrant does NOT expire!") - return true; - } else if (Date.parse(el.expiration) > new Date()) { - return true; - } else { - timeStamp("claimGrant expired!") - return false; - } - } else { - return false; - } - }); const stakeGrant = grants.find((el) => { if ( (!el.grantee || el.grantee === grantee) && @@ -99,12 +76,10 @@ export function parseGrants(grants, grantee, granter) { )) ) { if (el.expiration === null) { - timeStamp("stakeGrant does NOT expire!") return true; } else if (Date.parse(el.expiration) > new Date()) { return true; } else { - timeStamp("stakeGrant expired!") return false; } } else { @@ -112,8 +87,7 @@ export function parseGrants(grants, grantee, granter) { } }) return { - claimGrant, - stakeGrant, + stakeGrant }; } From b10f122e38a7c9fd8883ac79aa4b711119981e24 Mon Sep 17 00:00:00 2001 From: zenodeapp Date: Thu, 2 Nov 2023 12:26:20 +0100 Subject: [PATCH 822/862] Added source to networks.json --- src/networks.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index c992e9189..b3716a354 100644 --- a/src/networks.json +++ b/src/networks.json @@ -228,7 +228,12 @@ }, { "name": "echelon", - "keplrFeatures": ["ibc-transfer", "ibc-go", "eth-address-gen", "eth-key-sign"] + "keplrFeatures": [ + "ibc-transfer", + "ibc-go", + "eth-address-gen", + "eth-key-sign" + ] }, { "name": "kichaintestnet", @@ -317,5 +322,12 @@ "name": "coreum", "ownerAddress": "corevaloper1py9v5f7e4aaka55lwtthk30scxhnqfa6agwxt8", "gasPrice": "0.0625ucore" + }, + { + "name": "source", + "gasPrice": "0.025usource", + "autostake": { + "gasModifier": 1.4 + } } ] From 6e1ecbd0178f9962e32ae31eb2630789946ededf Mon Sep 17 00:00:00 2001 From: zenodeapp Date: Thu, 2 Nov 2023 12:31:02 +0100 Subject: [PATCH 823/862] Reverted changes made by prettier. --- src/networks.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/networks.json b/src/networks.json index b3716a354..b8f57e9b6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -228,12 +228,7 @@ }, { "name": "echelon", - "keplrFeatures": [ - "ibc-transfer", - "ibc-go", - "eth-address-gen", - "eth-key-sign" - ] + "keplrFeatures": ["ibc-transfer", "ibc-go", "eth-address-gen", "eth-key-sign"] }, { "name": "kichaintestnet", From be7afbf1af9f62cc67f599df8285e9831968e82b Mon Sep 17 00:00:00 2001 From: OmniWired Date: Mon, 6 Nov 2023 12:51:20 +0900 Subject: [PATCH 824/862] Update Dockerfile slim uses debian bookworm and is not supported anymore. This causes "gpg" errors. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2fff449f8..17bdc2b46 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # dev env -FROM node:18-slim +FROM node:18-buster RUN apt-get update && apt-get install -y python3 make g++ From 7f410e2c92d925cd3914d70b0f24d2563cb665cf Mon Sep 17 00:00:00 2001 From: Rootul Patel Date: Tue, 7 Nov 2023 22:08:09 +0100 Subject: [PATCH 825/862] docs: remove Ledger limitation --- README.md | 12 +++++------- README_TURKISH.md | 24 +++++++++++------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index a0003ad94..0a2fe0768 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ A script is also provided which allows a validator to automatically search their ## Limitations -- As of writing, Ledger is unable to send the necessary transactions to enable Authz. This is purely due to the way transactions are sent to a Ledger device and a workaround should be possible soon. - Authz is also not fully supported yet. Many chains are yet to update. The REStake UI will fall back to being a manual staking app with useful manual compounding features. - Currently REStake needs the browser extension version of Keplr, but WalletConnect and Keplr iOS functionality will be added ASAP. - REStake requires Nodejs version 18.x or later, it will not work with earlier versions. @@ -165,7 +164,6 @@ npm run dryrun osmosis **You should expect to see a warning that you are 'not an operator' until your REStake operator information is submitted in [Submitting your operator](#submitting-your-operator)** - ### Customise REStake and use your own node You will likely want to customise your networks config, e.g. to set your own node URLs to ensure your autocompounding script completes successfully. @@ -222,7 +220,7 @@ Don't forget to [update often](#updating-your-local-version)! #### Using `crontab` -Note: A helpful calculator for determining your REStake timer for `crontab` can be found here: https://crontab.guru/. +Note: A helpful calculator for determining your REStake timer for `crontab` can be found here: . Updated versions utilize `docker compose` instead of `docker-compose`. If you run into issues, try substituting `docker compose`. @@ -282,7 +280,7 @@ For NPM installs, remove `Requires` and `After` directives, and change `ExecStar The timer file defines the rules for running the restake service every day. All rules are described in the [systemd documentation](https://www.freedesktop.org/software/systemd/man/systemd.timer.html). -Note: Helpful calculator for determining restake times for `OnCalendar` can also be found at https://crontab.guru/. +Note: Helpful calculator for determining restake times for `OnCalendar` can also be found at . ```bash sudo vim /etc/systemd/system/restake.timer @@ -347,8 +345,8 @@ Add your Check UUID to the relevant network in your `networks.local.json` config } ``` -If you wish for your health checks to be created automatically, you can provide an [API key](https://healthchecks.io/docs/api/) to manage it for you. -The default behavior is for the check to be named after the network, but this can be overridden with the `name` property. +If you wish for your health checks to be created automatically, you can provide an [API key](https://healthchecks.io/docs/api/) to manage it for you. +The default behavior is for the check to be named after the network, but this can be overridden with the `name` property. **Note that the `uuid` is not necessary when using an `apiKey` to create checks automatically.** @@ -388,7 +386,7 @@ You now need to update the [Validator Registry](https://github.com/eco-stake/val `address` is your validator's address, and `restake.address` is the address from your new hot wallet you generated earlier. -`restake.run_time` is the time *in UTC* that you intend to run your bot, and there are a few options. Pass a single time, e.g. `09:00` to specify a single run at 9am UTC. Use an array for multiple specified times, e.g. `["09:00", "21:00"]`. Use an interval string for multiple times per hour/day, e.g. `"every 15 minutes"`. +`restake.run_time` is the time _in UTC_ that you intend to run your bot, and there are a few options. Pass a single time, e.g. `09:00` to specify a single run at 9am UTC. Use an array for multiple specified times, e.g. `["09:00", "21:00"]`. Use an interval string for multiple times per hour/day, e.g. `"every 15 minutes"`. `restake.minimum_reward` is the minimum reward to trigger autostaking, otherwise the address is skipped. This could be set higher for more frequent restaking. Note this is in the base denomination, e.g. `uosmo`. diff --git a/README_TURKISH.md b/README_TURKISH.md index b6110349b..2d8884bcf 100644 --- a/README_TURKISH.md +++ b/README_TURKISH.md @@ -20,8 +20,6 @@ Doğrulayıcının delegelerini otomatik olarak aramasını sağlayan bir komut ## Kısıtlamalar -Yazılı olarak, Ledger, Authz (Yetkilendirmeyi) etkinleştirmek için gerekli işlemleri gönderemiyor. Bu tamamen işlemlerin bir Ledger cihazına gönderilme şeklinden kaynaklanmaktadır ve yakında bir geçici çözüm mümkün olabilecektir. - Authz da henüz tam olarak desteklenmemektedir. Birçok zincir henüz güncellenmedi. REStake UI, kullanışlı manuel birleştirme özelliklerine sahip bir manuel stake uygulaması olmaya geri dönecek. Şu anda REStake, Keplr'ın tarayıcı uzantısı sürümüne ihtiyaç duyuyor, ancak WalletConnect ve Keplr iOS işlevselliği en kısa sürede eklenecek. @@ -110,6 +108,7 @@ cd restake npm install && npm run build cp .env.sample .env ``` + **Yeni .env dosyanıza anımsatıcı kelimelerinizi yazın.** #### Yerel versiyonunuzu güncelleme @@ -163,7 +162,6 @@ npm run dryrun osmosis **REStake operatör bilgilerinizi [Operatörünüzü kaydetme](#operat%C3%B6r%C3%BCn%C3%BCz%C3%BC-kaydetme) bölümünde gösterileceği gibi [Validator Kayıt Defteri](https://github.com/eco-stake/validator-registry)'ne kaydınızı yapana kadar 'operatör olmadığınıza dair bir uyarı görebilirsiniz.** - ### REStake'i özelleştirin ve node'unuzu kullanın Muhtemelen ağların yapılandırılmasını özelleştirmek isteyeceksiniz, örneğin auto compounding (ödüllerinizi otomatik olarak yeniden stake etmek) komut dosyanızın başarılı bir şekilde tamamlanmasını sağlamak için node'unuzun URL'lerinizi ayarlamak gibi. @@ -220,7 +218,7 @@ Her iki durumda da, sistem zamanınızın doğru olduğundan emin olun ve komut #### `crontab` Kullanma -NOT: REStake zamanlayıcınızı `crontab`'a göre belirlemek için faydalı bir hesap makinesini buradan ulaşabilirsiniz: https://crontab.guru/. +NOT: REStake zamanlayıcınızı `crontab`'a göre belirlemek için faydalı bir hesap makinesini buradan ulaşabilirsiniz: . Güncellenmiş sürümler, `docker-compose` yerine `docker compose` kullanır. Sorunlarla karşılaşırsanız, `docker compose` yerine bunu kullanmayı deneyin. @@ -261,9 +259,10 @@ ExecStart=/usr/bin/docker-compose run --rm app npm run autostake [Install] WantedBy=multi-user.target ``` -NPM kurulumu için `Requires` ve `After` direktiflerini kaldırın ve` `ExecStart`'ı` `ExecStart=/usr/bin/npm run autostake` olarak değiştirin. -🔴 **Not: Sorun yaşarsanız `WorkingDirectory=/path/to/restake` bölümünü `WorkingDirectory=/root/restake` olarak değiştiriniz. Eğer yine sorun yaşarsanız `chmod 777 /root/restake` komutu ile dosyaya okuma, yazma ve çalıştırma izni veriniz. Daha sonra `systemctl daemon-reload` yaptıktan sonra sistemi yeniden başlatınız. ** +NPM kurulumu için `Requires` ve `After` direktiflerini kaldırın ve``ExecStart`'ı` `ExecStart=/usr/bin/npm run autostake` olarak değiştirin. + +🔴 **Not: Sorun yaşarsanız `WorkingDirectory=/path/to/restake` bölümünü `WorkingDirectory=/root/restake` olarak değiştiriniz. Eğer yine sorun yaşarsanız `chmod 777 /root/restake` komutu ile dosyaya okuma, yazma ve çalıştırma izni veriniz. Daha sonra `systemctl daemon-reload` yaptıktan sonra sistemi yeniden başlatınız.** 🔴 **Eğer `Failed to restake service with docker compose` gibi bir hata alırsanız yine `chmod 777 /usr/bin/docker-compose` komutu ile dosyaya okuma, yazma ve çalıştırma izni veriniz.** @@ -271,9 +270,9 @@ NPM kurulumu için `Requires` ve `After` direktiflerini kaldırın ve` `ExecStar ##### systemd timer dosyası oluşturma -Zamanlayıcı dosyası, yeniden düzenleme hizmetini her gün çalıştırma kurallarını tanımlar. Tüm kurallar [systemd dokümanlarında] (https://www.freedesktop.org/software/systemd/man/systemd.timer.html) açıklanmaktadır. +Zamanlayıcı dosyası, yeniden düzenleme hizmetini her gün çalıştırma kurallarını tanımlar. Tüm kurallar [systemd dokümanlarında] () açıklanmaktadır. -Not: `OnCalendar` için restake sürelerini belirlemek için yararlı hesap makinesi https://crontab.guru/ adresinde bulunabilir. +Not: `OnCalendar` için restake sürelerini belirlemek için yararlı hesap makinesi adresinde bulunabilir. ```bash sudo vim /etc/systemd/system/restake.timer @@ -318,7 +317,7 @@ TriggeredBy: restake.timer ### İzleme -Her ağ için komut dosyası durumunu bildirmek için REStake oto-stake betiği [healthchecks.io] (https://healthchecks.io/) ile entegre olabilir. [HealthChecks.io] (https://healthchecks.io/) daha sonra, herhangi bir arızayı bildiğinizden emin olmak için e -posta, Discord ve Slack gibi birçok bildirim platformuyla entegre edilebilir. +Her ağ için komut dosyası durumunu bildirmek için REStake oto-stake betiği [healthchecks.io] () ile entegre olabilir. [HealthChecks.io] () daha sonra, herhangi bir arızayı bildiğinizden emin olmak için e -posta, Discord ve Slack gibi birçok bildirim platformuyla entegre edilebilir. Yapılandırıldıktan sonra, komut dosyası başladığında, başarılı ya da başarısız olduğunda REStake [healthchecks.io](https://healthchecks.io/)'a ping atacaktır. Kontrol günlüğü ilgili hata bilgilerini içerecektir ve yapılandırılması basittir. @@ -342,7 +341,6 @@ Kontrol UUID numaranızı aşağıdaki gibi `networks.local.json` yapılandırma Artık operatör bilgilerinizi oto-sake'i aktif etmek istediğiniz ağları eklemek için [Validator Kayıt Defteri](https://github.com/eco-stake/validator-registry)'ni güncellemeniz gerekiyor. Örnekler için README ve mevcut doğrulayıcıları kontrol edebilirsiniz, ancak bir ağ için yapılandırma şuna benziyor: - ```json { "name": "akash", @@ -370,15 +368,15 @@ REStake yapmak istediğiniz tüm ağlar için bu yapılandırmayı tekrarlayın. #### Operatörünüzü Validator Kayıt Defterine kaydetme -Artık [Validator Kayıt Defteri] (https://github.com/eco-stake/validator-registry) güncellemenizi mümkün olan en kısa sürede merge edilmek üzere pull request isteğinde bulunabilirsiniz. REStake, değişikliklerin birleştirilmesinden sonraki 15 dakika içinde otomatik olarak güncellenir. +Artık [Validator Kayıt Defteri] () güncellemenizi mümkün olan en kısa sürede merge edilmek üzere pull request isteğinde bulunabilirsiniz. REStake, değişikliklerin birleştirilmesinden sonraki 15 dakika içinde otomatik olarak güncellenir. ## Katkıda Bulunma ### Bir Ağ Ekleme/Güncelleme -Ağ bilgileri [Zincir Kayıt Defteri] (https://github.com/cosmos/chain-registry) [registry.cosmos.directory] (https://registry.cosmos.directory) API üzerinden alınır. Yeterli temel bilgilerin sağlandığı varsayılarak, REStake'e ana daldaki zincirler otomatik olarak eklenir. +Ağ bilgileri [Zincir Kayıt Defteri] () [registry.cosmos.directory] () API üzerinden alınır. Yeterli temel bilgilerin sağlandığı varsayılarak, REStake'e ana daldaki zincirler otomatik olarak eklenir. -'networks.json' dosyası, REStake'de 'desteklendiği' gibi hangi zincirlerin göründüğünü tanımlar; zincir adı Zincir Kayıt Defterinden dizin adıyla eşleştiği sürece, tüm zincir bilgileri otomatik olarak sağlanacaktır. Alternatif olarak zincirler, tek başına `networks.json`'da _desteklenebilir_, ancak bu belgelenmiş bir özellik değildir. +'networks.json' dosyası, REStake'de 'desteklendiği' gibi hangi zincirlerin göründüğünü tanımlar; zincir adı Zincir Kayıt Defterinden dizin adıyla eşleştiği sürece, tüm zincir bilgileri otomatik olarak sağlanacaktır. Alternatif olarak zincirler, tek başına `networks.json`'da *desteklenebilir*, ancak bu belgelenmiş bir özellik değildir. Bir zinciri yeniden eklemek veya geçersiz kılmak için gerekli bilgileri aşağıdaki gibi `networks.json`'a ekleyin: From 39d172e2a25ca1bb7702d9b7bf797cff668c3337 Mon Sep 17 00:00:00 2001 From: William Chong Date: Wed, 15 Nov 2023 00:38:14 +0800 Subject: [PATCH 826/862] update likecoin gas price --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index c992e9189..a2859887f 100644 --- a/src/networks.json +++ b/src/networks.json @@ -236,7 +236,7 @@ }, { "name": "likecoin", - "gasPrice": "10nanolike" + "gasPrice": "10000nanolike" }, { "name": "passage", From a7134aacf515d25bcde49c44444a9a5e2e0650d1 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 17 Nov 2023 22:23:22 +0000 Subject: [PATCH 827/862] Disable null expiration grants --- src/utils/Helpers.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 182de39d1..8be6f32ed 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -76,7 +76,10 @@ export function parseGrants(grants, grantee, granter) { )) ) { if (el.expiration === null) { - return true; + // null expiration is flakey currently + // sometimes it means it's expired, sometimes no expiration set (infinite grant) + // we have to treat as invalid until this is resolved + return false; } else if (Date.parse(el.expiration) > new Date()) { return true; } else { From ef5cf8eafeae6163a00a42341a7fc8814d13d39d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 17 Nov 2023 23:49:19 +0000 Subject: [PATCH 828/862] Avoid delegators query and use all grants if possible --- src/autostake/NetworkRunner.mjs | 88 ++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 18b4652bf..6c373c5e2 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -61,22 +61,12 @@ export default class NetworkRunner { timeStamp("Running with options:", this.opts) this.balance = await this.getBalance() || 0 - if (addresses) { - timeStamp("Checking", addresses.length, "addresses for grants...") - } else { - timeStamp('Finding delegators...') - addresses = await this.getDelegations().then(delegations => { - return delegations.map(delegation => { - if (delegation.balance.amount === 0) return - - return delegation.delegation.delegator_address - }) - }) - timeStamp("Checking", addresses.length, "delegators for grants...") + let grantedAddresses = await this.getAddressesFromGrants(addresses) + if(grantedAddresses === false){ + timeStamp('All grants query not supported, falling back to checking delegators...') + grantedAddresses = await this.getAddressesFromDelegators(addresses) } - let grantedAddresses = await this.getGrantedAddresses(addresses) - timeStamp("Found", grantedAddresses.length, "addresses with valid grants...") if (grantedAddresses.length) { await this.autostake(grantedAddresses) @@ -102,48 +92,77 @@ export default class NetworkRunner { ) } - getDelegations() { - let timeout = this.opts.delegationsTimeout - let pageSize = this.opts.batchPageSize - return this.queryClient.getAllValidatorDelegations(this.operator.address, pageSize, { timeout }, (pages) => { - timeStamp("...batch", pages.length) - return this.throttleQuery() - }).catch(error => { - throw new Error(`Failed to get delegations: ${error.message || error}`) - }) - } - - async getGrantedAddresses(addresses) { + async getAddressesFromGrants(addresses) { const { botAddress, address } = this.operator let timeout = this.opts.delegationsTimeout let pageSize = this.opts.batchPageSize let allGrants try { + timeStamp('Finding all grants...') allGrants = await this.queryClient.getGranteeGrants(botAddress, { timeout, pageSize }, (pages) => { timeStamp("...batch", pages.length) return this.throttleQuery() }) - } catch (e) { } + } catch (e) { + if(error.response?.status === 501){ + return false + }else{ + throw new Error('Failed to load grants') + } + } + if(addresses){ + timeStamp("Checking", addresses.length, "addresses for grants...") + }else{ + addresses = allGrants.map(grant => grant.granter) + } + let addressGrants = addresses.map(item => { + return this.parseGrantResponse(allGrants, botAddress, item, address) + }) + return _.compact(addressGrants.flat()) + } + + async getAddressesFromDelegators(addresses) { + if(!addresses){ + timeStamp('Finding delegators...') + addresses = await this.getDelegations().then(delegations => { + return delegations.map(delegation => { + if (delegation.balance.amount === 0) return + + return delegation.delegation.delegator_address + }) + }) + timeStamp("Checking", addresses.length, "delegators for grants...") + }else{ + timeStamp("Checking", addresses.length, "addresses for grants...") + } let grantCalls = addresses.map(item => { return async () => { - if (allGrants) return this.parseGrantResponse(allGrants, botAddress, item, address) try { - return await this.getGrants(item) + return await this.getGrantsIndividually(item) } catch (error) { this.setError(item, `Failed to get grants: ${error.message}`) } } }) let grantedAddresses = await mapSync(grantCalls, this.opts.batchQueries, (batch, index) => { - if(!allGrants){ - timeStamp('...batch', index + 1) - return this.throttleQuery() - } + timeStamp('...batch', index + 1) + return this.throttleQuery() }) return _.compact(grantedAddresses.flat()) } - getGrants(delegatorAddress) { + getDelegations() { + let timeout = this.opts.delegationsTimeout + let pageSize = this.opts.batchPageSize + return this.queryClient.getAllValidatorDelegations(this.operator.address, pageSize, { timeout }, (pages) => { + timeStamp("...batch", pages.length) + return this.throttleQuery() + }).catch(error => { + throw new Error(`Failed to get delegations: ${error.message || error}`) + }) + } + + getGrantsIndividually(delegatorAddress) { const { botAddress, address } = this.operator let timeout = this.opts.queryTimeout return this.queryClient.getGrants(botAddress, delegatorAddress, { timeout }) @@ -162,7 +181,6 @@ export default class NetworkRunner { let grantValidators, maxTokens if (result.stakeGrant) { if (result.stakeGrant.authorization['@type'] === "/cosmos.authz.v1beta1.GenericAuthorization") { - timeStamp(delegatorAddress, "Using GenericAuthorization, allowed") grantValidators = [validatorAddress]; } else { const { allow_list, deny_list } = result.stakeGrant.authorization From b5042b1076c0dc0b77554da017fab413b0b38aa6 Mon Sep 17 00:00:00 2001 From: zenodeapp Date: Tue, 21 Nov 2023 01:48:25 +0100 Subject: [PATCH 829/862] Changed gasPrice to 0.05usource, see PR #754 --- src/networks.json | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/networks.json b/src/networks.json index b8f57e9b6..e68e6e3ef 100644 --- a/src/networks.json +++ b/src/networks.json @@ -228,7 +228,12 @@ }, { "name": "echelon", - "keplrFeatures": ["ibc-transfer", "ibc-go", "eth-address-gen", "eth-key-sign"] + "keplrFeatures": [ + "ibc-transfer", + "ibc-go", + "eth-address-gen", + "eth-key-sign" + ] }, { "name": "kichaintestnet", @@ -320,9 +325,6 @@ }, { "name": "source", - "gasPrice": "0.025usource", - "autostake": { - "gasModifier": 1.4 - } + "gasPrice": "0.05usource" } ] From 54a532e81216716084550f4a7c719a42f95e022a Mon Sep 17 00:00:00 2001 From: zenodeapp Date: Tue, 21 Nov 2023 01:59:22 +0100 Subject: [PATCH 830/862] Reverted prettier adjustments... --- src/networks.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/networks.json b/src/networks.json index e68e6e3ef..9acf8c8a9 100644 --- a/src/networks.json +++ b/src/networks.json @@ -228,12 +228,7 @@ }, { "name": "echelon", - "keplrFeatures": [ - "ibc-transfer", - "ibc-go", - "eth-address-gen", - "eth-key-sign" - ] + "keplrFeatures": ["ibc-transfer", "ibc-go", "eth-address-gen", "eth-key-sign"] }, { "name": "kichaintestnet", From 4c7ed52d004c0c31754c0e3980ddde66ee890903 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Wed, 22 Nov 2023 15:10:03 +0000 Subject: [PATCH 831/862] Fix typo in error variable --- src/autostake/NetworkRunner.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 6c373c5e2..c6a5d924c 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -103,7 +103,7 @@ export default class NetworkRunner { timeStamp("...batch", pages.length) return this.throttleQuery() }) - } catch (e) { + } catch (error) { if(error.response?.status === 501){ return false }else{ From 420025f91c272f235aca17b94ca2ea42d8a6459d Mon Sep 17 00:00:00 2001 From: ZENODE Date: Wed, 29 Nov 2023 05:19:52 +0100 Subject: [PATCH 832/862] Added a retries value of 10 to source. --- src/networks.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 9acf8c8a9..03e9d59fc 100644 --- a/src/networks.json +++ b/src/networks.json @@ -320,6 +320,9 @@ }, { "name": "source", - "gasPrice": "0.05usource" + "gasPrice": "0.05usource", + "autostake": { + "retries": 10 + } } ] From d00c0049e7d23aca81337b436b99049235acb38e Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 1 Dec 2023 12:45:51 +0000 Subject: [PATCH 833/862] Ensure unique addresses --- src/autostake/NetworkRunner.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index c6a5d924c..e9725987c 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -115,7 +115,7 @@ export default class NetworkRunner { }else{ addresses = allGrants.map(grant => grant.granter) } - let addressGrants = addresses.map(item => { + let addressGrants = _.uniq(addresses).map(item => { return this.parseGrantResponse(allGrants, botAddress, item, address) }) return _.compact(addressGrants.flat()) @@ -135,7 +135,7 @@ export default class NetworkRunner { }else{ timeStamp("Checking", addresses.length, "addresses for grants...") } - let grantCalls = addresses.map(item => { + let grantCalls = _.uniq(addresses).map(item => { return async () => { try { return await this.getGrantsIndividually(item) From 2149149a93d39bdf1a8e86306742de61a5b09b7d Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Fri, 1 Dec 2023 12:52:13 +0000 Subject: [PATCH 834/862] Remove retries option --- src/networks.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/networks.json b/src/networks.json index 03e9d59fc..9acf8c8a9 100644 --- a/src/networks.json +++ b/src/networks.json @@ -320,9 +320,6 @@ }, { "name": "source", - "gasPrice": "0.05usource", - "autostake": { - "retries": 10 - } + "gasPrice": "0.05usource" } ] From 404cc53f001cf6322f6652dcd45ae7e319ee39fd Mon Sep 17 00:00:00 2001 From: Anthony BESCOND Date: Mon, 18 Dec 2023 13:28:08 +0100 Subject: [PATCH 835/862] feat: allow overriding directory data in networks --- src/utils/Operator.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index d70a926ec..18df73603 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -2,8 +2,8 @@ import _ from 'lodash' const Operator = (network, data) => { const { address } = data - const botAddress = data.restake.address - const minimumReward = data.restake.minimum_reward + const botAddress = network.data.restake?.address || data.restake.address + const minimumReward = network.data.restake?.minimumReward || data.restake.minimum_reward return { address, From fda4c60406387bed50d42e16570f9febe661a834 Mon Sep 17 00:00:00 2001 From: Anthony BESCOND Date: Tue, 19 Dec 2023 09:17:57 +0100 Subject: [PATCH 836/862] feat: registry override doc --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0a2fe0768..5cefe12dc 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,10 @@ Create a `src/networks.local.json` file and specify the networks you want to ove "queryThrottle": 100, "gasModifier": 1.1 }, + "restake": { + "address": "OVERRIDE_BOT_ADDRESS", + "minimumReward": "OVERRIDER_BOT_MINIMUM_REWARD" + }, "healthCheck": { "uuid": "XXXXX-XXX-XXXX" } From 84953ec98f64c5234d730a5e0c1792cdecd7b524 Mon Sep 17 00:00:00 2001 From: Active Nodes <11424273+activenodes@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:06:48 +0100 Subject: [PATCH 837/862] Added prettyName detail to health.success/failed Added prettyName to "Autostake finised/failed" log --- src/autostake/index.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 02bb326e8..7c469a6c7 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -49,9 +49,9 @@ export default function Autostake(mnemonic, opts) { }) } if (success || skipped) { - return health.success('Autostake finished') + return health.success('Autostake finished for ${data.prettyName}') } else { - return health.failed('Autostake failed') + return health.failed('Autostake failed for ${data.prettyName}') } } }) From 7f7d44f1bc17318d905bbe2cef4a3ce9eb7b7bc9 Mon Sep 17 00:00:00 2001 From: kii-awesome <94878333+kii-awesome@users.noreply.github.com> Date: Sat, 27 Jan 2024 00:59:01 +0700 Subject: [PATCH 838/862] add nolus & realio --- src/networks.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/networks.json b/src/networks.json index ab0d09fd2..9294a2f13 100644 --- a/src/networks.json +++ b/src/networks.json @@ -321,5 +321,13 @@ { "name": "source", "gasPrice": "0.05usource" + }, + { + "name": "nolus", + "gasPrice": "0.0025unls" + }, + { + "name": "realio", + "gasPrice": "1000000000ario" } ] From a3b34b5690c3c21a86b81268c54ff82cd56946f5 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 23 Mar 2024 16:03:16 +0000 Subject: [PATCH 839/862] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5cefe12dc..fad12a8ef 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ Create a `src/networks.local.json` file and specify the networks you want to ove "queryThrottle": 100, "gasModifier": 1.1 }, - "restake": { + "operator": { "address": "OVERRIDE_BOT_ADDRESS", "minimumReward": "OVERRIDER_BOT_MINIMUM_REWARD" }, From df73e1fb06f1de1696fdc917d84b58b8ba3b785b Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 23 Mar 2024 16:03:20 +0000 Subject: [PATCH 840/862] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fad12a8ef..379355ec2 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ Create a `src/networks.local.json` file and specify the networks you want to ove }, "operator": { "address": "OVERRIDE_BOT_ADDRESS", - "minimumReward": "OVERRIDER_BOT_MINIMUM_REWARD" + "minimumReward": "OVERRIDE_BOT_MINIMUM_REWARD" }, "healthCheck": { "uuid": "XXXXX-XXX-XXXX" From 8cff0350a4cf4a84952ca88a1a63c1fd32fd48f3 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 23 Mar 2024 16:03:24 +0000 Subject: [PATCH 841/862] Update src/utils/Operator.mjs --- src/utils/Operator.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index 18df73603..8efc0bb26 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -2,7 +2,7 @@ import _ from 'lodash' const Operator = (network, data) => { const { address } = data - const botAddress = network.data.restake?.address || data.restake.address + const botAddress = network.data.operator?.address || data.restake.address const minimumReward = network.data.restake?.minimumReward || data.restake.minimum_reward return { From c7a8c193870477219404fe9d9bd55a7cce760187 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Sat, 23 Mar 2024 16:03:27 +0000 Subject: [PATCH 842/862] Update src/utils/Operator.mjs --- src/utils/Operator.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/Operator.mjs b/src/utils/Operator.mjs index 8efc0bb26..d0e26aa7f 100644 --- a/src/utils/Operator.mjs +++ b/src/utils/Operator.mjs @@ -3,7 +3,7 @@ import _ from 'lodash' const Operator = (network, data) => { const { address } = data const botAddress = network.data.operator?.address || data.restake.address - const minimumReward = network.data.restake?.minimumReward || data.restake.minimum_reward + const minimumReward = network.data.operator?.minimumReward || data.restake.minimum_reward return { address, From a22485f0e5b26fea1c7d1c1cd5087eb946a6db72 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 8 Apr 2024 13:20:44 +0100 Subject: [PATCH 843/862] Fix variable interpolation --- src/autostake/index.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 7c469a6c7..6f8c50815 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -49,9 +49,9 @@ export default function Autostake(mnemonic, opts) { }) } if (success || skipped) { - return health.success('Autostake finished for ${data.prettyName}') + return health.success(`Autostake finished for ${data.prettyName}`) } else { - return health.failed('Autostake failed for ${data.prettyName}') + return health.failed(`Autostake failed for ${data.prettyName}`) } } }) From e183dc30ee30240fabcba8e791dfa37c5c82aa95 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 8 Apr 2024 14:38:44 +0100 Subject: [PATCH 844/862] Fix network name logging --- src/autostake/index.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 6f8c50815..5be6937b7 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -40,6 +40,7 @@ export default function Autostake(mnemonic, opts) { const health = new Health(data.healthCheck, { dryRun: opts.dryRun, networkName: data.name }) health.started('⚛') const results = await runWithRetry(data, health) + const network = results[0]?.networkRunner?.network const { success, skipped } = results[results.length - 1] || {} if(!skipped && !failed){ health.log(`Autostake ${success ? 'completed' : 'failed'} after ${results.length} attempt(s)`) @@ -49,9 +50,9 @@ export default function Autostake(mnemonic, opts) { }) } if (success || skipped) { - return health.success(`Autostake finished for ${data.prettyName}`) + return health.success(`Autostake finished for ${network?.prettyName || data.name}`) } else { - return health.failed(`Autostake failed for ${data.prettyName}`) + return health.failed(`Autostake failed for ${network?.prettyName || data.name}`) } } }) From 9eaf27927bb8fee7dd8c76e1f47c90e6dfd8dcaf Mon Sep 17 00:00:00 2001 From: Sergey Date: Thu, 2 May 2024 17:27:33 +0300 Subject: [PATCH 845/862] feat: add custom user-agent to queries --- src/utils/CosmosDirectory.mjs | 14 +++++----- src/utils/Helpers.mjs | 27 ++++++++++++++++++++ src/utils/QueryClient.mjs | 48 +++++++++++++++-------------------- src/utils/SigningClient.mjs | 12 ++++----- src/utils/constants.mjs | 1 + 5 files changed, 60 insertions(+), 42 deletions(-) create mode 100644 src/utils/constants.mjs diff --git a/src/utils/CosmosDirectory.mjs b/src/utils/CosmosDirectory.mjs index 69e298995..3eab18273 100644 --- a/src/utils/CosmosDirectory.mjs +++ b/src/utils/CosmosDirectory.mjs @@ -1,4 +1,4 @@ -import axios from 'axios' +import { get } from './Helpers.mjs' function CosmosDirectory(testnet){ const protocol = process.env.DIRECTORY_PROTOCOL || 'https' @@ -19,34 +19,34 @@ function CosmosDirectory(testnet){ } function getChains(){ - return axios.get(chainsUrl) + return get(chainsUrl) .then(res => res.data) .then(data => Array.isArray(data) ? data : data.chains) // deprecate .then(data => data.reduce((a, v) => ({ ...a, [v.path]: v }), {})) } function getChainData(name) { - return axios.get([chainsUrl, name].join('/')) + return get([chainsUrl, name].join('/')) .then(res => res.data.chain) } async function getTokenData(name) { - return axios.get([chainsUrl, name, 'assetlist'].join('/')) + return get([chainsUrl, name, 'assetlist'].join('/')) .then(res => res.data) } function getValidators(chainName){ - return axios.get(validatorsUrl + '/chains/' + chainName) + return get(validatorsUrl + '/chains/' + chainName) .then(res => res.data.validators) } function getRegistryValidator(validatorName) { - return axios.get(validatorsUrl + '/' + validatorName) + return get(validatorsUrl + '/' + validatorName) .then(res => res.data.validator) } function getOperatorAddresses(){ - return axios.get(validatorsUrl) + return get(validatorsUrl) .then(res => res.data) .then(data => Array.isArray(data) ? data : data.validators) // deprecate .then(data => data.reduce((sum, validator) => { diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 8be6f32ed..72cbab7ad 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -1,6 +1,9 @@ import _ from 'lodash' import { format, floor, bignumber } from 'mathjs' import { coin as _coin } from '@cosmjs/stargate' +import axios from 'axios' + +import { RESTAKE_USER_AGENT } from './constants.mjs' export function timeStamp(...args) { console.log('[' + new Date().toISOString().substring(11, 23) + ']', ...args); @@ -129,3 +132,27 @@ export async function executeSync(calls, count) { await mapAsync(batchCall, call => call()) } } + +export async function get(url, opts) { + const headers = opts?.headers ?? {} + + return axios.get(url, { + ...opts, + headers: { + ...headers, + 'User-Agent': RESTAKE_USER_AGENT, + } + }) +} + +export async function post(url, body, opts) { + const headers = opts?.headers ?? {} + + return axios.post(url, body, { + ...opts, + headers: { + ...headers, + 'User-Agent': RESTAKE_USER_AGENT, + } + }) +} diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index cc2c1b195..e5ca69d4e 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -2,6 +2,8 @@ import axios from "axios"; import axiosRetry from 'axios-retry'; import _ from "lodash"; +import { get } from './Helpers.mjs'; + const QueryClient = async (chainId, restUrls, opts) => { const config = _.merge({ connectTimeout: 10000, @@ -29,8 +31,7 @@ const QueryClient = async (chainId, restUrls, opts) => { searchParams.append("pagination.limit", pageSize); if (nextKey) searchParams.append("pagination.key", nextKey); - return axios - .get( + return get( apiUrl('staking', `validators?${searchParams.toString()}`), { timeout: opts.timeout || 10000, }) @@ -57,14 +58,12 @@ const QueryClient = async (chainId, restUrls, opts) => { if (nextKey) searchParams.append("pagination.key", nextKey); - return axios - .get(apiUrl('staking', `validators/${validatorAddress}/delegations?${searchParams.toString()}`), opts) + return get(apiUrl('staking', `validators/${validatorAddress}/delegations?${searchParams.toString()}`), opts) .then((res) => res.data); } function getBalance(address, denom, opts) { - return axios - .get(apiUrl('bank', `balances/${address}`), opts) + return get(apiUrl('bank', `balances/${address}`), opts) .then((res) => res.data) .then((result) => { if (!denom) @@ -78,8 +77,7 @@ const QueryClient = async (chainId, restUrls, opts) => { } function getDelegations(address) { - return axios - .get(apiUrl('staking', `delegations/${address}`)) + return get(apiUrl('staking', `delegations/${address}`)) .then((res) => res.data) .then((result) => { const delegations = result.delegation_responses.reduce( @@ -91,8 +89,7 @@ const QueryClient = async (chainId, restUrls, opts) => { } function getRewards(address, opts) { - return axios - .get(apiUrl('distribution', `delegators/${address}/rewards`), opts) + return get(apiUrl('distribution', `delegators/${address}/rewards`), opts) .then((res) => res.data) .then((result) => { const rewards = result.rewards.reduce( @@ -104,8 +101,7 @@ const QueryClient = async (chainId, restUrls, opts) => { } function getCommission(validatorAddress, opts) { - return axios - .get(apiUrl('distribution', `validators/${validatorAddress}/commission`), opts) + return get(apiUrl('distribution', `validators/${validatorAddress}/commission`), opts) .then((res) => res.data) .then((result) => { return result.commission; @@ -120,8 +116,7 @@ const QueryClient = async (chainId, restUrls, opts) => { if (nextKey) searchParams.append("pagination.key", nextKey); - return axios - .get(apiUrl('gov', `proposals?${searchParams.toString()}`), opts) + return get(apiUrl('gov', `proposals?${searchParams.toString()}`), opts) .then((res) => res.data); }).then((pages) => { return pages.map(el => el.proposals).flat(); @@ -129,14 +124,12 @@ const QueryClient = async (chainId, restUrls, opts) => { } function getProposalTally(proposal_id, opts) { - return axios - .get(apiUrl('gov', `proposals/${proposal_id}/tally`), opts) + return get(apiUrl('gov', `proposals/${proposal_id}/tally`), opts) .then((res) => res.data); } function getProposalVote(proposal_id, address, opts) { - return axios - .get(apiUrl('gov', `proposals/${proposal_id}/votes/${address}`), opts) + return get(apiUrl('gov', `proposals/${proposal_id}/votes/${address}`), opts) .then((res) => res.data); } @@ -148,8 +141,7 @@ const QueryClient = async (chainId, restUrls, opts) => { if (nextKey) searchParams.append("pagination.key", nextKey); - return axios - .get(apiUrl('authz', `grants/grantee/${grantee}?${searchParams.toString()}`), opts) + return get(apiUrl('authz', `grants/grantee/${grantee}?${searchParams.toString()}`), opts) .then((res) => res.data); }, pageCallback).then((pages) => { return pages.map(el => el.grants).flat(); @@ -164,8 +156,7 @@ const QueryClient = async (chainId, restUrls, opts) => { if (nextKey) searchParams.append("pagination.key", nextKey); - return axios - .get(apiUrl('authz', `grants/granter/${granter}?${searchParams.toString()}`), opts) + return get(apiUrl('authz', `grants/granter/${granter}?${searchParams.toString()}`), opts) .then((res) => res.data); }, pageCallback).then((pages) => { return pages.map(el => el.grants).flat(); @@ -178,8 +169,7 @@ const QueryClient = async (chainId, restUrls, opts) => { searchParams.append("grantee", grantee); if (granter) searchParams.append("granter", granter); - return axios - .get(apiUrl('authz', `grants?${searchParams.toString()}`), opts) + return get(apiUrl('authz', `grants?${searchParams.toString()}`), opts) .then((res) => res.data) .then((result) => { return result.grants; @@ -187,8 +177,7 @@ const QueryClient = async (chainId, restUrls, opts) => { } function getWithdrawAddress(address, opts) { - return axios - .get(apiUrl('distribution', `delegators/${address}/withdraw_address`)) + return get(apiUrl('distribution', `delegators/${address}/withdraw_address`)) .then((res) => res.data) .then((result) => { return result.withdraw_address; @@ -205,7 +194,10 @@ const QueryClient = async (chainId, restUrls, opts) => { searchParams.append('pagination.limit', pageSize); if (order) searchParams.append('order_by', order); - const client = axios.create({ baseURL: restUrl }); + const client = axios.create({ + baseURL: restUrl, + headers: { 'User-Agent': RESTAKE_USER_AGENT } + }); axiosRetry(client, { retries: retries || 0, shouldResetTimeout: true, retryCondition: (e) => true }); return client.get(apiPath('tx', `txs?${searchParams.toString()}`), opts).then((res) => res.data); } @@ -249,7 +241,7 @@ const QueryClient = async (chainId, restUrls, opts) => { async function getLatestBlock(url, type, path, opts){ const { timeout } = opts || {} try { - return await axios.get(url + path, { timeout }) + return await get(url + path, { timeout }) .then((res) => res.data) } catch (error) { const fallback = type === 'rest' && '/blocks/latest' diff --git a/src/utils/SigningClient.mjs b/src/utils/SigningClient.mjs index 8683ecec7..ad1c75a4a 100644 --- a/src/utils/SigningClient.mjs +++ b/src/utils/SigningClient.mjs @@ -1,5 +1,4 @@ import _ from 'lodash' -import axios from 'axios' import { multiply, ceil, bignumber } from 'mathjs' import Long from "long"; @@ -15,7 +14,7 @@ import { PubKey } from "cosmjs-types/cosmos/crypto/secp256k1/keys.js"; import { SignMode } from "cosmjs-types/cosmos/tx/signing/v1beta1/signing.js"; import { AuthInfo, Fee, TxBody, TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx.js"; -import { coin } from './Helpers.mjs' +import { coin, get, post } from './Helpers.mjs' function SigningClient(network, signer) { @@ -25,8 +24,7 @@ function SigningClient(network, signer) { const registry = new Registry(defaultStargateTypes); function getAccount(address) { - return axios - .get(restUrl + "/cosmos/auth/v1beta1/accounts/" + address) + return get(restUrl + "/cosmos/auth/v1beta1/accounts/" + address) .then((res) => res.data.account) .then((value) => { // see https://github.com/chainapsis/keplr-wallet/blob/7ca025d32db7873b7a870e69a4a42b525e379132/packages/cosmos/src/account/index.ts#L73 @@ -122,7 +120,7 @@ function SigningClient(network, signer) { } await sleep(pollIntervalMs); try { - const response = await axios.get(restUrl + '/cosmos/tx/v1beta1/txs/' + txId); + const response = await get(restUrl + '/cosmos/tx/v1beta1/txs/' + txId); const result = parseTxResult(response.data.tx_response) return result } catch { @@ -130,7 +128,7 @@ function SigningClient(network, signer) { } }; - const response = await axios.post(restUrl + '/cosmos/tx/v1beta1/txs', { + const response = await post(restUrl + '/cosmos/tx/v1beta1/txs', { tx_bytes: toBase64(TxRaw.encode(txBody).finish()), mode: "BROADCAST_MODE_SYNC" }) @@ -180,7 +178,7 @@ function SigningClient(network, signer) { } try { - const estimate = await axios.post(restUrl + '/cosmos/tx/v1beta1/simulate', { + const estimate = await post(restUrl + '/cosmos/tx/v1beta1/simulate', { tx_bytes: toBase64(TxRaw.encode(txBody).finish()), }).then(el => el.data.gas_info.gas_used) return (parseInt(estimate * (modifier || defaultGasModifier))); diff --git a/src/utils/constants.mjs b/src/utils/constants.mjs new file mode 100644 index 000000000..ba9057731 --- /dev/null +++ b/src/utils/constants.mjs @@ -0,0 +1 @@ +export const RESTAKE_USER_AGENT = 'ReStake' From b927f8c96a7ee49a66331c37ae69c8bb45d3327f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Thu, 2 May 2024 19:42:05 +0100 Subject: [PATCH 846/862] Update src/utils/constants.mjs --- src/utils/constants.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/constants.mjs b/src/utils/constants.mjs index ba9057731..ce43174e8 100644 --- a/src/utils/constants.mjs +++ b/src/utils/constants.mjs @@ -1 +1 @@ -export const RESTAKE_USER_AGENT = 'ReStake' +export const RESTAKE_USER_AGENT = 'REStake' From f7c06943d70d5d9dc5432c463acee2b5dea815b8 Mon Sep 17 00:00:00 2001 From: Sergey Date: Fri, 3 May 2024 19:20:02 +0300 Subject: [PATCH 847/862] feat: add Winston logging --- package-lock.json | 220 +++++++++++++++++++++++++++++++- package.json | 3 +- src/autostake/Health.mjs | 20 +-- src/autostake/NetworkRunner.mjs | 93 +++++++++----- src/autostake/index.mjs | 40 ++++-- src/utils/Helpers.mjs | 33 ++++- 6 files changed, 350 insertions(+), 59 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49d26f7d0..8f8f2b45c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,8 @@ "cosmjs-types": "^0.7.1", "dotenv": "^16.0.3", "lodash": "^4.17.21", - "mathjs": "^11.7.0" + "mathjs": "^11.7.0", + "winston": "^3.13.0" } }, "node_modules/@babel/runtime": { @@ -32,6 +33,14 @@ "node": ">=6.9.0" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@confio/ics23": { "version": "0.6.8", "resolved": "https://registry.npmjs.org/@confio/ics23/-/ics23-0.6.8.tgz", @@ -175,6 +184,16 @@ "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.30.1.tgz", "integrity": "sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g==" }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@ethersproject/abstract-provider": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", @@ -805,6 +824,11 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.10.tgz", "integrity": "sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==" }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, "node_modules/aes-js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", @@ -821,6 +845,11 @@ "node": ">=4" } }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -958,6 +987,15 @@ "node": ">=4" } }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -971,6 +1009,24 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1082,6 +1138,11 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "node_modules/escape-latex": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", @@ -1095,6 +1156,16 @@ "node": ">=0.8.0" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "node_modules/follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -1296,6 +1367,11 @@ "node": ">= 0.10" } }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", @@ -1318,6 +1394,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -1336,6 +1423,11 @@ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "node_modules/libsodium": { "version": "0.7.11", "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.11.tgz", @@ -1368,6 +1460,22 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", @@ -1443,6 +1551,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -1459,6 +1572,14 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1502,6 +1623,19 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readonly-date": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", @@ -1566,6 +1700,14 @@ } ] }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, "node_modules/scrypt-js": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", @@ -1615,6 +1757,30 @@ "node": ">=6" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1645,11 +1811,24 @@ "node": ">=0.10" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "node_modules/tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/typed-function": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.1.0.tgz", @@ -1658,6 +1837,45 @@ "node": ">= 14" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 4c6748125..134e9c523 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "cosmjs-types": "^0.7.1", "dotenv": "^16.0.3", "lodash": "^4.17.21", - "mathjs": "^11.7.0" + "mathjs": "^11.7.0", + "winston": "^3.13.0" }, "scripts": { "dryrun": "node scripts/dryrun.mjs", diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 3521ca462..dffc67852 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -1,6 +1,6 @@ import _ from 'lodash' import axios from 'axios' -import { timeStamp } from '../utils/Helpers.mjs' +import { createLogger } from '../utils/Helpers.mjs' class Health { constructor(config, opts) { @@ -22,26 +22,28 @@ class Health { // https://healthchecks.selfhosted.com/ping/{uuid} rather than https://hc-ping.com/{uuid} this.address = this.address + "/ping" } + + this.logger = createLogger('health') } started(...args) { - timeStamp(...args) - if (this.uuid) timeStamp('Starting health', [this.address, this.uuid].join('/')) + this.logger.info(args.join(' ')) + if (this.uuid) this.logger.info('Starting health', { path: [this.address, this.uuid].join('/') }) return this.ping('start', [args.join(' ')]) } success(...args) { - timeStamp(...args) + this.logger.info(args.join(' ')) return this.ping(undefined, [...this.logs, args.join(' ')]) } failed(...args) { - timeStamp(...args) + this.logger.warn(args.join(' ')) return this.ping('fail', [...this.logs, args.join(' ')]) } log(...args) { - timeStamp(...args) + this.logger.info(args.join(' ')) this.logs = [...this.logs, args.join(' ')] } @@ -67,7 +69,7 @@ class Health { this.uuid = res.data.ping_url.split('/')[4] }); } catch (error) { - timeStamp("Health Check creation failed: " + error) + this.logger.error('Health Check creation failed', { error }) } } @@ -78,14 +80,14 @@ class Health { async ping(action, logs) { if (!this.uuid) return - if (this.dryRun) return timeStamp('DRYRUN: Skipping health check ping') + if (this.dryRun) return this.logger.info('DRYRUN: Skipping health check ping') return axios.request({ method: 'POST', url: _.compact([this.address, this.uuid, action]).join('/'), data: logs.join("\n") }).catch(error => { - timeStamp('Health ping failed', error.message) + this.logger.error('Health ping failed', { error }) }) } } diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index e9725987c..8838ad725 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -3,7 +3,7 @@ import _ from 'lodash' import { add, subtract, bignumber, floor, smaller, smallerEq } from 'mathjs' import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx.js"; -import { coin, timeStamp, mapSync, executeSync, parseGrants } from '../utils/Helpers.mjs' +import { coin, mapSync, executeSync, parseGrants, createLogger } from '../utils/Helpers.mjs' export default class NetworkRunner { constructor(network, operator, signingClient, opts) { @@ -25,6 +25,8 @@ export default class NetworkRunner { this.processed = {} this.errors = {} this.results = [] + + this.logger = createLogger('network_runner').child({ chain: network.name }) } didSucceed() { @@ -52,22 +54,22 @@ export default class NetworkRunner { } setError(address, error) { - timeStamp(address, ':', error) + this.logger.error(error, { address }) this.errors[address] = error } async run(addresses) { try { - timeStamp("Running with options:", this.opts) + this.logger.info('Running with options', this.opts) this.balance = await this.getBalance() || 0 let grantedAddresses = await this.getAddressesFromGrants(addresses) if(grantedAddresses === false){ - timeStamp('All grants query not supported, falling back to checking delegators...') + this.logger.warn('All grants query not supported, falling back to checking delegators...') grantedAddresses = await this.getAddressesFromDelegators(addresses) } - timeStamp("Found", grantedAddresses.length, "addresses with valid grants...") + this.logger.info('Found addresses with valid grants...', { count: grantedAddresses.length}) if (grantedAddresses.length) { await this.autostake(grantedAddresses) } @@ -83,7 +85,7 @@ export default class NetworkRunner { return this.queryClient.getBalance(this.operator.botAddress, this.network.denom, { timeout }) .then( (balance) => { - timeStamp("Bot balance is", balance.amount, balance.denom) + this.logger.info('Fetched bot balance', balance) return balance.amount }, (error) => { @@ -98,9 +100,9 @@ export default class NetworkRunner { let pageSize = this.opts.batchPageSize let allGrants try { - timeStamp('Finding all grants...') + this.logger.info('Finding all grants...') allGrants = await this.queryClient.getGranteeGrants(botAddress, { timeout, pageSize }, (pages) => { - timeStamp("...batch", pages.length) + this.logger.info('...batch', { length: pages.length }) return this.throttleQuery() }) } catch (error) { @@ -110,9 +112,9 @@ export default class NetworkRunner { throw new Error('Failed to load grants') } } - if(addresses){ - timeStamp("Checking", addresses.length, "addresses for grants...") - }else{ + if (addresses){ + this.logger.info('Checking addresses for grants...', { length: addresses.length }) + } else { addresses = allGrants.map(grant => grant.granter) } let addressGrants = _.uniq(addresses).map(item => { @@ -122,8 +124,8 @@ export default class NetworkRunner { } async getAddressesFromDelegators(addresses) { - if(!addresses){ - timeStamp('Finding delegators...') + if (!addresses) { + this.logger.info('Finding delegators...') addresses = await this.getDelegations().then(delegations => { return delegations.map(delegation => { if (delegation.balance.amount === 0) return @@ -131,9 +133,9 @@ export default class NetworkRunner { return delegation.delegation.delegator_address }) }) - timeStamp("Checking", addresses.length, "delegators for grants...") - }else{ - timeStamp("Checking", addresses.length, "addresses for grants...") + this.logger.info('Checking delegators for grants...', { length: addresses.length }) + } else { + this.logger.info('Checking addresses for grants...', { length: addresses.length }) } let grantCalls = _.uniq(addresses).map(item => { return async () => { @@ -145,7 +147,7 @@ export default class NetworkRunner { } }) let grantedAddresses = await mapSync(grantCalls, this.opts.batchQueries, (batch, index) => { - timeStamp('...batch', index + 1) + this.logger.info('...batch', { count: index + 1 }) return this.throttleQuery() }) return _.compact(grantedAddresses.flat()) @@ -155,7 +157,7 @@ export default class NetworkRunner { let timeout = this.opts.delegationsTimeout let pageSize = this.opts.batchPageSize return this.queryClient.getAllValidatorDelegations(this.operator.address, pageSize, { timeout }, (pages) => { - timeStamp("...batch", pages.length) + this.logger.info('...batch', { count: pages.length }) return this.throttleQuery() }).catch(error => { throw new Error(`Failed to get delegations: ${error.message || error}`) @@ -192,7 +194,7 @@ export default class NetworkRunner { grantValidators = [] } if (!grantValidators.includes(validatorAddress)) { - timeStamp(delegatorAddress, "Not autostaking for this validator, skipping") + this.logger.info('Not autostaking for this validator, skipping', { delegatorAddress }) return } maxTokens = result.stakeGrant.authorization.max_tokens @@ -218,7 +220,10 @@ export default class NetworkRunner { return } if (withdrawAddress && withdrawAddress !== address) { - timeStamp(address, 'has a different withdraw address:', withdrawAddress) + this.logger.info('Wallet has a different withdraw address', { + withdrawAddress, + address + }) return } @@ -233,29 +238,45 @@ export default class NetworkRunner { let autostakeAmount = floor(totalRewards) if (smaller(bignumber(autostakeAmount), bignumber(this.operator.minimumReward))) { - timeStamp(address, autostakeAmount, this.network.denom, 'reward is too low, skipping') + this.logger.info('Reward is too low, skipping', { + address, + amount: autostakeAmount, + denom: this.network.denom, + }) return } if (grant.maxTokens) { if (smallerEq(grant.maxTokens, 0)) { - timeStamp(address, grant.maxTokens, this.network.denom, 'grant balance is empty, skipping') + this.logger.info('Grant balance is empty, skipping', { + address, + maxTokens: grant.maxToken, + denom: this.network.denom, + }) return } if (smaller(grant.maxTokens, autostakeAmount)) { autostakeAmount = grant.maxTokens - timeStamp(address, grant.maxTokens, this.network.denom, 'grant balance is too low, using remaining') + this.logger.info('Grant balance is too low, using remaining', { + address, + maxTokens: grant.maxToken, + denom: this.network.denom, + }) } } - timeStamp(address, "Can autostake", autostakeAmount, this.network.denom) + this.logger.info('Can autostake', { + address, + amount: autostakeAmount, + denom: this.network.denom, + }) return this.buildRestakeMessage(address, this.operator.address, autostakeAmount, this.network.denom) } async autostake(grantedAddresses) { let batchSize = this.opts.batchTxs - timeStamp('Calculating and autostaking in batches of', batchSize) + this.logger.info('Calculating and autostaking in batches', { batchSize }) const calls = grantedAddresses.map((item, index) => { return async () => { @@ -289,7 +310,7 @@ export default class NetworkRunner { const messages = [...this.messages] const promise = messages[messages.length - 1] || Promise.resolve() const sendTx = promise.then(() => { - timeStamp('Sending batch', messages.length + 1) + this.logger.info('Sending batch', { batch: messages.length + 1 }) return this.sendMessages(addresses, batch) }) this.messages.push(sendTx) @@ -311,24 +332,36 @@ export default class NetworkRunner { if (this.opts.dryRun) { const message = `DRYRUN: Would send ${messages.length} messages using ${gas} gas` - timeStamp(message) + this.logger.info('DRYRUN: Would send messages using gas', { + messages: messages.length, + gas, + }) this.balance = subtract(bignumber(this.balance), bignumber(fee.amount)) return { message, addresses } } else { return await this.signingClient.signAndBroadcast(this.operator.botAddress, [execMsg], gas, memo).then((response) => { const message = `Sent ${messages.length} messages - ${response.transactionHash}` - timeStamp(message) + this.logger.info('Sent messages', { + transactionHash: response.transactionHash, + length: messages.length, + }) this.balance = subtract(bignumber(this.balance), bignumber(fee.amount)) return { message, addresses } }, (error) => { const message = `Failed ${messages.length} messages - ${error.message}` - timeStamp(message) + this.logger.info('Failed messages', { + error: error.message, + length: messages.length, + }) return { message, addresses, error } }) } } catch (error) { const message = `Failed ${messages.length} messages: ${error.message}` - timeStamp(message) + this.logger.info('Failed messages', { + error: error.message, + length: messages.length, + }) return { message, addresses, error } } } diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 5be6937b7..f5c1ced59 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -12,15 +12,17 @@ import Network from '../utils/Network.mjs' import Wallet from '../utils/Wallet.mjs'; import NetworkRunner from './NetworkRunner.mjs'; import Health from './Health.mjs'; -import { timeStamp, executeSync, overrideNetworks } from '../utils/Helpers.mjs' +import { executeSync, overrideNetworks, createLogger } from '../utils/Helpers.mjs' import EthSigner from '../utils/EthSigner.mjs'; export default function Autostake(mnemonic, opts) { + const mainLogger = createLogger('autostake') + opts = opts || {} let failed = false if (!mnemonic) { - timeStamp('Please provide a MNEMONIC environment variable') + mainLogger.error('Please provide a MNEMONIC environment variable') process.exit() } @@ -124,23 +126,31 @@ export default function Autostake(mnemonic, opts) { throw new Error(`Unable to load network data for ${network.name}`) } - timeStamp('Loaded', network.prettyName) + const logger = mainLogger.child({ chain: network.name }) + + logger.info('Loaded chain', { prettyName: network.prettyName }) const { signer, slip44 } = await getSigner(network) const wallet = new Wallet(network, signer) const botAddress = await wallet.getAddress() - timeStamp('Bot address is', botAddress) + logger.info('Bot address', { address: botAddress }) if (network.slip44 && network.slip44 !== slip44) { - timeStamp("!! You are not using the preferred derivation path !!") - timeStamp("!! You should switch to the correct path unless you have grants. Check the README !!") + logger.warn("!! You are not using the preferred derivation path !!") + logger.warn("!! You should switch to the correct path unless you have grants. Check the README !!") } const operator = network.getOperatorByBotAddress(botAddress) - if (!operator) return timeStamp('Not an operator') + if (!operator) { + logger.info('Not an operator') + return + } - if (!network.authzSupport) return timeStamp('No Authz support') + if (!network.authzSupport) { + logger.info('No Authz support') + return + } await network.connect({ timeout: config.delegationsTimeout || 20000 }) @@ -148,11 +158,11 @@ export default function Autostake(mnemonic, opts) { if (!restUrl) throw new Error('Could not connect to REST API') - timeStamp('Using REST URL', restUrl) + logger.info('Using REST URL', { url: restUrl }) if (usingDirectory) { - timeStamp('You are using public nodes, they may not be reliable. Check the README to use your own') - timeStamp('Delaying briefly and adjusting config to reduce load...') + logger.warn('You are using public nodes, they may not be reliable. Check the README to use your own') + logger.warn('Delaying briefly and adjusting config to reduce load...') config = {...config, batchPageSize: 50, batchQueries: 10, queryThrottle: 2500} await new Promise(r => setTimeout(r, (Math.random() * 31) * 1000)); } @@ -169,9 +179,11 @@ export default function Autostake(mnemonic, opts) { } async function getSigner(network) { + const logger = mainLogger.child({ chain: network.name }) + let slip44 if (network.data.autostake?.correctSlip44 || network.slip44 === 60) { - if (network.slip44 === 60) timeStamp('Found ETH coin type') + if (network.slip44 === 60) logger.info('Found ETH coin type') slip44 = network.slip44 || 118 } else { slip44 = network.data.autostake?.slip44 || 118 @@ -183,7 +195,7 @@ export default function Autostake(mnemonic, opts) { Slip10RawIndex.normal(0), Slip10RawIndex.normal(0), ]; - slip44 != 118 && timeStamp('Using HD Path', pathToString(hdPath)) + slip44 != 118 && logger.info('Using HD Path', { path: pathToString(hdPath) }) let signer = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: network.prefix, @@ -206,7 +218,7 @@ export default function Autostake(mnemonic, opts) { const overrides = overridesData && JSON.parse(overridesData) || {} return overrideNetworks(networks, overrides) } catch (error) { - timeStamp('Failed to parse networks.local.json, check JSON is valid', error.message) + mainLogger.error('Failed to parse networks.local.json, check JSON is valid', { message: error.message }) return networks } } diff --git a/src/utils/Helpers.mjs b/src/utils/Helpers.mjs index 72cbab7ad..849469692 100644 --- a/src/utils/Helpers.mjs +++ b/src/utils/Helpers.mjs @@ -2,13 +2,10 @@ import _ from 'lodash' import { format, floor, bignumber } from 'mathjs' import { coin as _coin } from '@cosmjs/stargate' import axios from 'axios' +import winston from 'winston' import { RESTAKE_USER_AGENT } from './constants.mjs' -export function timeStamp(...args) { - console.log('[' + new Date().toISOString().substring(11, 23) + ']', ...args); -} - export function coin(amount, denom){ return _coin(format(floor(amount), {notation: 'fixed'}), denom) } @@ -156,3 +153,31 @@ export async function post(url, body, opts) { } }) } + +export function createLogger(module) { + return winston.createLogger({ + level: 'debug', + defaultMeta: { module }, + format: winston.format.combine( + winston.format.colorize(), + winston.format.prettyPrint(), + winston.format.timestamp(), + winston.format.splat(), + winston.format.printf(({ + timestamp, + level, + message, + label = '', + ...meta + }) => { + const metaFormatted = Object.entries(meta) + .map(([key, value]) => `${key}=${value}`) + .join(' ') + return `[${timestamp}] ${level}: ${message} ${metaFormatted}` + }) + ), + transports: [ + new winston.transports.Console() + ], + }); +} From 8d0afd4d77db0bc7f7a3f5ee21bbb3fd31a11dbe Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 27 May 2024 11:53:01 +0100 Subject: [PATCH 848/862] Add status to grant failure message --- src/autostake/NetworkRunner.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 8838ad725..6d3e65235 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -109,7 +109,7 @@ export default class NetworkRunner { if(error.response?.status === 501){ return false }else{ - throw new Error('Failed to load grants') + throw new Error(`Failed to load grants: ${error.response?.status}`) } } if (addresses){ @@ -241,7 +241,7 @@ export default class NetworkRunner { this.logger.info('Reward is too low, skipping', { address, amount: autostakeAmount, - denom: this.network.denom, + denom: this.network.denom, }) return } From 41e24c7af124608f95e12d7934227263dbd97142 Mon Sep 17 00:00:00 2001 From: Trinity Stake Date: Tue, 30 Jul 2024 07:24:33 +0000 Subject: [PATCH 849/862] updated Dockerfile and docker-compose.yml --- Dockerfile | 2 +- docker-compose.yml | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 17bdc2b46..cba2d1577 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # dev env -FROM node:18-buster +FROM node:20.15-buster RUN apt-get update && apt-get install -y python3 make g++ diff --git a/docker-compose.yml b/docker-compose.yml index 57a9d24da..ffdc082a1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.3" - services: app: build: . From 4ede3d94fefd4e30a65c8f974fac6350c7a01d63 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 12 Aug 2024 13:47:41 +0100 Subject: [PATCH 850/862] Update Node to v20.x --- .nvmrc | 2 +- README.md | 10 +++++----- README_TURKISH.md | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.nvmrc b/.nvmrc index 3c032078a..209e3ef4b 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18 +20 diff --git a/README.md b/README.md index 379355ec2..b25cd8139 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ A script is also provided which allows a validator to automatically search their - Authz is also not fully supported yet. Many chains are yet to update. The REStake UI will fall back to being a manual staking app with useful manual compounding features. - Currently REStake needs the browser extension version of Keplr, but WalletConnect and Keplr iOS functionality will be added ASAP. -- REStake requires Nodejs version 18.x or later, it will not work with earlier versions. +- REStake requires Nodejs version 20.x or later, it will not work with earlier versions. ## Become an operator @@ -88,18 +88,18 @@ cp .env.sample .env #### Instructions for NPM -##### Install nodejs@v18 +##### Install nodejs@v20 ```bash -curl -sL https://deb.nodesource.com/setup_18.x -o /tmp/nodesource_setup.sh +curl -sL https://deb.nodesource.com/setup_20.x -o /tmp/nodesource_setup.sh # read the script file and when you're sure it's safe run it chmod +x /tmp/nodesource_setup.sh /tmp/nodesource_setup.sh apt install nodejs -y node --version -> v18.15.0 +> v20.9.0 npm --version -> 9.5.0 +> 10.1.0 ``` ##### Clone the repository and build it diff --git a/README_TURKISH.md b/README_TURKISH.md index 2d8884bcf..fb4bbab2f 100644 --- a/README_TURKISH.md +++ b/README_TURKISH.md @@ -86,18 +86,18 @@ cp .env.sample .env #### NPM için Talimatlar -##### nodejs@v18 yükleme +##### nodejs@v20 yükleme ```bash -curl -sL https://deb.nodesource.com/setup_18.x -o /tmp/nodesource_setup.sh +curl -sL https://deb.nodesource.com/setup_20.x -o /tmp/nodesource_setup.sh # read the script file and when you're sure it's safe run it chmod +x /tmp/nodesource_setup.sh /tmp/nodesource_setup.sh apt install nodejs -y node --version -> v18.15.0 +> v20.9.0 npm --version -> 9.5.0 +> 10.1.0 ``` Depoyu klonlama ve yükleme From 5c11f61cea124b4d282eefbf725b5e81785f596c Mon Sep 17 00:00:00 2001 From: ApeironNodes <168997041+ApeironNodes@users.noreply.github.com> Date: Wed, 23 Oct 2024 21:01:59 +0300 Subject: [PATCH 851/862] Update networks.json add dungeonchain on networks.json --- src/networks.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 9294a2f13..9f47f5344 100644 --- a/src/networks.json +++ b/src/networks.json @@ -329,5 +329,9 @@ { "name": "realio", "gasPrice": "1000000000ario" - } + }, + { + "name": "DungeonChain", + "gasPrice": "0.05udgn" + }, ] From 06410fdbf82ede4f0740cd2956a5074f46791029 Mon Sep 17 00:00:00 2001 From: ApeironNodes <168997041+ApeironNodes@users.noreply.github.com> Date: Wed, 23 Oct 2024 21:03:33 +0300 Subject: [PATCH 852/862] Update networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 9f47f5344..b846e0de6 100644 --- a/src/networks.json +++ b/src/networks.json @@ -333,5 +333,5 @@ { "name": "DungeonChain", "gasPrice": "0.05udgn" - }, + } ] From a27a2271441e22cb0f272a733ffe6a2285a15673 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 4 Nov 2024 21:27:46 +0000 Subject: [PATCH 853/862] Update HealthChecks.io API address --- src/autostake/Health.mjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index dffc67852..90c4d1100 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -7,6 +7,7 @@ class Health { const { address, uuid, name, apiKey, timeout, gracePeriod } = config || {} const { dryRun, networkName } = opts || {} this.address = address || 'https://hc-ping.com' + this.apiAddress = apiAddress || 'https://healthchecks.io' this.name = name || networkName this.gracePeriod = gracePeriod || 86400 // default 24 hours this.timeout = timeout || 86400 // default 24 hours @@ -18,7 +19,7 @@ class Health { if (address) { // This is necessary as the default provider - hc-ping.com - has a built in ping mechanism - // whereas providing self-hosted addresses do NOT. + // whereas providing self-hosted addresses do NOT. // https://healthchecks.selfhosted.com/ping/{uuid} rather than https://hc-ping.com/{uuid} this.address = this.address + "/ping" } @@ -65,7 +66,7 @@ class Health { } try { - await axios.post([this.address, 'api/v2/checks/'].join('/'), data, config).then((res) => { + await axios.post([this.apiAddress, 'api/v2/checks/'].join('/'), data, config).then((res) => { this.uuid = res.data.ping_url.split('/')[4] }); } catch (error) { @@ -92,4 +93,4 @@ class Health { } } -export default Health \ No newline at end of file +export default Health From fc8446a016da16cfa09c68c4b3342864a0e39a0f Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 4 Nov 2024 21:29:10 +0000 Subject: [PATCH 854/862] Update src/networks.json --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index b846e0de6..2de382a8e 100644 --- a/src/networks.json +++ b/src/networks.json @@ -331,7 +331,7 @@ "gasPrice": "1000000000ario" }, { - "name": "DungeonChain", + "name": "dungeon", "gasPrice": "0.05udgn" } ] From 483161e44d0b768e9b3804e5d101c7d484bc6d02 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Mon, 4 Nov 2024 21:31:50 +0000 Subject: [PATCH 855/862] Fix healthcheck variable --- src/autostake/Health.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 90c4d1100..a1da61c17 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -4,7 +4,7 @@ import { createLogger } from '../utils/Helpers.mjs' class Health { constructor(config, opts) { - const { address, uuid, name, apiKey, timeout, gracePeriod } = config || {} + const { address, apiAddress, uuid, name, apiKey, timeout, gracePeriod } = config || {} const { dryRun, networkName } = opts || {} this.address = address || 'https://hc-ping.com' this.apiAddress = apiAddress || 'https://healthchecks.io' From a15a28d77ae498a667f73bda127248471a7372e2 Mon Sep 17 00:00:00 2001 From: Trinity Stake <114076168+trinitystake@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:25:22 +0000 Subject: [PATCH 856/862] updated networks.json for sentinel chain --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 2de382a8e..c69b79cd8 100644 --- a/src/networks.json +++ b/src/networks.json @@ -54,7 +54,7 @@ }, { "name": "sentinel", - "gasPrice": "0.02udvpn" + "gasPrice": "0.1udvpn" }, { "name": "dig", From 8a677a8680e106603e6af65d8f39ec8f8f509a8c Mon Sep 17 00:00:00 2001 From: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> Date: Fri, 18 Apr 2025 11:03:05 -0700 Subject: [PATCH 857/862] Fix url building Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> --- src/utils/QueryClient.mjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index e5ca69d4e..dfa38508d 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -253,13 +253,16 @@ const QueryClient = async (chainId, restUrls, opts) => { } function apiUrl(type, path){ - return restUrl + apiPath(type, path) + const normalizedRestUrl = restUrl.replace(/\/$/, '') + return normalizedRestUrl + apiPath(type, path) } function apiPath(type, path){ const versions = config.apiVersions || {} const version = versions[type] || 'v1beta1' - return `/cosmos/${type}/${version}/${path}` + const normalizedType = type.replace(/^\/+/, '') + const normalizedPath = path.replace(/^\/+/, '') + return `/cosmos/${normalizedType}/${version}/${normalizedPath}` } return { From 00f73772e508d9859d295861fe86c5d1356484fd Mon Sep 17 00:00:00 2001 From: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> Date: Fri, 18 Apr 2025 11:04:31 -0700 Subject: [PATCH 858/862] Remove trailing slash Signed-off-by: Dylan Schultz <9121234+dylanschultzie@users.noreply.github.com> --- src/utils/QueryClient.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/QueryClient.mjs b/src/utils/QueryClient.mjs index dfa38508d..633612183 100644 --- a/src/utils/QueryClient.mjs +++ b/src/utils/QueryClient.mjs @@ -225,7 +225,7 @@ const QueryClient = async (chainId, restUrls, opts) => { urls = [urls] } } - const path = type === "rest" ? apiPath('/base/tendermint', 'blocks/latest') : "/block"; + const path = type === "rest" ? apiPath('base/tendermint', 'blocks/latest') : "/block"; return Promise.any(urls.map(async (url) => { url = url.replace(/\/$/, '') try { From 4a982204b7fc499d8bf5c720f1ca7f7f0c604a5a Mon Sep 17 00:00:00 2001 From: Leonoor's Cryptoman <50612432+LeonoorsCryptoman@users.noreply.github.com> Date: Sat, 17 May 2025 20:57:31 +0200 Subject: [PATCH 859/862] Update networks.json --- src/networks.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index c69b79cd8..ec1516d80 100644 --- a/src/networks.json +++ b/src/networks.json @@ -240,7 +240,8 @@ }, { "name": "passage", - "ownerAddress": "pasgvaloper196rujtjehu0dfc7y85lkcaps6tel76g3l9knjy" + "ownerAddress": "pasgvaloper196rujtjehu0dfc7y85lkcaps6tel76g3l9knjy", + "gasPrice": "12.5upasg" }, { "name": "stride", From 8ee6ab0055cc8f11ffcbea501d7e8f1389cb25b2 Mon Sep 17 00:00:00 2001 From: Tom Beynon Date: Tue, 29 Jul 2025 19:28:58 +0100 Subject: [PATCH 860/862] Fix balance check to use gas denom --- src/autostake/NetworkRunner.mjs | 3 ++- src/networks.json | 4 ++++ src/utils/Network.mjs | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/autostake/NetworkRunner.mjs b/src/autostake/NetworkRunner.mjs index 6d3e65235..4da86f820 100644 --- a/src/autostake/NetworkRunner.mjs +++ b/src/autostake/NetworkRunner.mjs @@ -82,7 +82,8 @@ export default class NetworkRunner { getBalance() { let timeout = this.opts.delegationsTimeout - return this.queryClient.getBalance(this.operator.botAddress, this.network.denom, { timeout }) + const denom = this.network.gasPriceDenom || this.network.denom + return this.queryClient.getBalance(this.operator.botAddress, denom, { timeout }) .then( (balance) => { this.logger.info('Fetched bot balance', balance) diff --git a/src/networks.json b/src/networks.json index c69b79cd8..db917e349 100644 --- a/src/networks.json +++ b/src/networks.json @@ -333,5 +333,9 @@ { "name": "dungeon", "gasPrice": "0.05udgn" + }, + { + "name": "atomone", + "gasPrice": "0.225uphoton" } ] diff --git a/src/utils/Network.mjs b/src/utils/Network.mjs index 3a9a1c3ae..8c4f1ff4b 100644 --- a/src/utils/Network.mjs +++ b/src/utils/Network.mjs @@ -91,7 +91,9 @@ class Network { this.defaultGasPrice = this.decimals && format(bignumber(multiply(0.000000025, pow(10, this.decimals))), { notation: 'fixed', precision: 4}) + this.denom this.gasPrice = this.data.gasPrice || this.defaultGasPrice if(this.gasPrice){ - this.gasPriceAmount = GasPrice.fromString(this.gasPrice).amount.toString() + const gasPrice = GasPrice.fromString(this.gasPrice) + this.gasPriceAmount = gasPrice.amount.toString() + this.gasPriceDenom = gasPrice.denom this.gasPriceStep = this.data.gasPriceStep || { "low": multiply(this.gasPriceAmount, 0.5), "average": multiply(this.gasPriceAmount, 1), From 5e45a75af7cc2e02520df3e476d714c7d2f01a59 Mon Sep 17 00:00:00 2001 From: Liver-23 Date: Tue, 29 Jul 2025 22:08:02 +0300 Subject: [PATCH 861/862] Update gasPrice for Kyve Kyve Network changed the default gas price value to 2ukyve --- src/networks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/networks.json b/src/networks.json index 65fb44f08..3709c2dad 100644 --- a/src/networks.json +++ b/src/networks.json @@ -300,7 +300,7 @@ { "name": "kyve", "ownerAddress": "kyvevaloper1egqphd8yjdfv84fl825grwgna0pf2emagdmnz8", - "gasPrice": "0.02ukyve" + "gasPrice": "2ukyve" }, { "name": "quicksilver", From c025aa1ea8d34e93d83b781e979bcee8ae2fe577 Mon Sep 17 00:00:00 2001 From: Aravindan M Date: Wed, 3 Sep 2025 00:03:49 +0530 Subject: [PATCH 862/862] Update Dockerfile --- Dockerfile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index cba2d1577..77b98d8e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,20 @@ -# dev env -FROM node:20.15-buster +FROM node:20-bookworm -RUN apt-get update && apt-get install -y python3 make g++ +RUN apt-get update \ + && apt-get install -y --no-install-recommends python3 build-essential \ + && rm -rf /var/lib/apt/lists/* ENV NODE_ENV=development WORKDIR /usr/src/app + COPY package*.json ./ -RUN npm install -COPY . ./ +RUN npm ci || npm install +COPY . . ENV DIRECTORY_PROTOCOL=https ENV DIRECTORY_DOMAIN=cosmos.directory EXPOSE 3000 -CMD npm run autostake + +CMD ["npm", "run", "autostake"]