diff --git a/wallet/public/tokens/stosmo.svg b/wallet/public/tokens/stosmo.svg
new file mode 100644
index 00000000..cd1ed2d0
--- /dev/null
+++ b/wallet/public/tokens/stosmo.svg
@@ -0,0 +1,9 @@
+
diff --git a/wallet/public/tokens/sttia.svg b/wallet/public/tokens/sttia.svg
new file mode 100644
index 00000000..10bd37de
--- /dev/null
+++ b/wallet/public/tokens/sttia.svg
@@ -0,0 +1,4 @@
+
diff --git a/wallet/src/components/ConnectionSettingsDialog.tsx b/wallet/src/components/ConnectionSettingsDialog.tsx
index 99f90b2d..0e89bc56 100644
--- a/wallet/src/components/ConnectionSettingsDialog.tsx
+++ b/wallet/src/components/ConnectionSettingsDialog.tsx
@@ -8,9 +8,13 @@ import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import TextField from '@mui/material/TextField';
import { makeStyles } from '@mui/styles';
-import { useMemo, useState } from 'react';
+import { useMemo, useState, useEffect } from 'react';
import { withApplicationContext } from '../contexts/Application';
-import { NetworkConfigSource, networkConfigUrl } from '../util/connections';
+import {
+ ConnectionConfig,
+ NetworkConfigSource,
+ networkConfigUrl,
+} from '../util/connections';
import isEqual from 'lodash-es/isEqual';
import { maybeSave } from '../util/storage';
@@ -43,6 +47,43 @@ const ErrorLabel = ({ children }) => {
);
};
+const useNodeSuggestions = (networkConfigHref: string) => {
+ const [apiSuggestions, setApiSuggestions] = useState([]);
+ const [rpcSuggestions, setRpcSuggestions] = useState([]);
+
+ useEffect(() => {
+ let didConfigChange = false;
+ setRpcSuggestions([]);
+ setApiSuggestions([]);
+
+ const updateSuggestions = async () => {
+ const url = new URL(networkConfigHref);
+ const res = await fetch(url);
+ if (!res.ok) {
+ throw Error(`Cannot fetch network: ${res.status}`);
+ }
+ const networkConfig = await res.json();
+
+ if (didConfigChange) return;
+ setApiSuggestions(networkConfig.apiAddrs ?? []);
+ setRpcSuggestions(networkConfig.rpcAddrs ?? []);
+ };
+
+ updateSuggestions().catch(() =>
+ console.error(
+ 'error fetching node suggestions from config',
+ networkConfigHref,
+ ),
+ );
+
+ return () => {
+ didConfigChange = true;
+ };
+ }, [networkConfigHref]);
+
+ return { apiSuggestions, rpcSuggestions };
+};
+
const ConnectionSettingsDialog = ({
onClose,
open,
@@ -60,10 +101,16 @@ const ConnectionSettingsDialog = ({
networkConfigUrl.toSource(connectionConfig.href),
);
- const [config, setConfig] = useState(
- connectionConfig || { href: networkConfigUrl.fromSource(configSource) },
+ const [config, setConfig] = useState(
+ connectionConfig || {
+ href: networkConfigUrl.fromSource(configSource),
+ rpc: undefined,
+ api: undefined,
+ },
);
+ const { apiSuggestions, rpcSuggestions } = useNodeSuggestions(config.href);
+
const errors = new Set();
try {
@@ -93,10 +140,10 @@ const ConnectionSettingsDialog = ({
}
setConnectionConfig(config);
disconnect(true);
- const { href } = config;
+ const { href, rpc, api } = config;
const isKnown = allConnectionConfigs.some(c => c.href === href);
if (!isKnown) {
- setAllConnectionConfigs(conns => [{ href }, ...conns]);
+ setAllConnectionConfigs(conns => [{ href, rpc, api }, ...conns]);
}
}
onClose();
@@ -119,11 +166,13 @@ const ConnectionSettingsDialog = ({
case 'testnet':
case 'devnet':
setConfig({
- href: `https://${value}.agoric.net/network-config`,
+ ...config,
+ href: networkConfigUrl.fromSource(value),
});
break;
- case 'localhost':
+ case 'local':
setConfig({
+ ...config,
href: `${window.location.origin}/wallet/network-config`,
});
break;
@@ -134,9 +183,9 @@ const ConnectionSettingsDialog = ({
}}
>
-
+
-
+
@@ -149,7 +198,9 @@ const ConnectionSettingsDialog = ({
options={smartConnectionHrefs}
sx={{ width: 360, mt: 2 }}
onChange={(_, newValue) =>
+ newValue &&
setConfig({
+ ...config,
href: newValue,
})
}
@@ -164,12 +215,71 @@ const ConnectionSettingsDialog = ({
onChange={ev =>
ev.target.value !== config.href &&
setConfig({
+ ...config,
href: ev.target.value,
})
}
/>
)}
/>
+
+ setConfig({
+ ...config,
+ rpc: newValue ?? undefined,
+ })
+ }
+ renderOption={(props, option) => {option}}
+ freeSolo
+ selectOnFocus
+ handleHomeEndKeys
+ renderInput={params => (
+
+ ev.target.value !== config.rpc &&
+ setConfig({
+ ...config,
+ rpc: ev.target.value,
+ })
+ }
+ />
+ )}
+ />
+
+ setConfig({
+ ...config,
+ api: newValue ?? undefined,
+ })
+ }
+ renderOption={(props, option) => {option}}
+ freeSolo
+ selectOnFocus
+ handleHomeEndKeys
+ renderInput={params => (
+
+ ev.target.value !== config.api &&
+ setConfig({
+ ...config,
+ api: ev.target.value,
+ })
+ }
+ />
+ )}
+ />
{errors.has(Errors.INVALID_URL) ? 'Enter a valid URL' : ''}
diff --git a/wallet/src/components/tests/ConnectionSettingsDialog.test.tsx b/wallet/src/components/tests/ConnectionSettingsDialog.test.tsx
index 5fb1d510..6161fb60 100644
--- a/wallet/src/components/tests/ConnectionSettingsDialog.test.tsx
+++ b/wallet/src/components/tests/ConnectionSettingsDialog.test.tsx
@@ -59,9 +59,7 @@ describe('Connection setting dialog', () => {
);
const networkSelect = component.find(Select).first();
- act(() =>
- networkSelect.props().onChange({ target: { value: 'localhost' } }),
- );
+ act(() => networkSelect.props().onChange({ target: { value: 'local' } }));
component.update();
const textField = component.find(TextField).first();
diff --git a/wallet/src/contexts/Provider.tsx b/wallet/src/contexts/Provider.tsx
index 7c82ab32..00df603a 100644
--- a/wallet/src/contexts/Provider.tsx
+++ b/wallet/src/contexts/Provider.tsx
@@ -33,6 +33,7 @@ export type KeplrUtils = {
interactiveSigner: InteractiveSigner;
backgroundSigner: BackgroundSigner;
};
+ rpc: string;
};
const useDebugLogging = (state, watch) => {
@@ -212,7 +213,7 @@ const Provider = ({ children }) => {
assert(keplr, 'Missing window.keplr');
const { getBytes } = Random;
- const chainInfo = await suggestChain(connectionConfig.href, {
+ const chainInfo = await suggestChain(connectionConfig, {
fetch,
keplr,
random: Math.random,
@@ -236,7 +237,6 @@ const Provider = ({ children }) => {
address: accounts[0]?.address,
signers: { interactiveSigner, backgroundSigner },
chainId: chainInfo.chainId,
- // @ts-expect-error used?
rpc: chainInfo.rpc,
});
};
diff --git a/wallet/src/util/Icons.ts b/wallet/src/util/Icons.ts
index 3f1b38b5..93b41ca1 100644
--- a/wallet/src/util/Icons.ts
+++ b/wallet/src/util/Icons.ts
@@ -12,6 +12,8 @@ export const icons = {
stATOM: 'tokens/statom.svg',
DAI_grv: 'tokens/DAI_grv.png',
USDT: 'tokens/USDT.png',
+ stTIA: 'tokens/sttia.svg',
+ stOSMO: 'tokens/stosmo.svg',
};
export const defaultIcon = 'tokens/default.png';
diff --git a/wallet/src/util/SuggestChain.ts b/wallet/src/util/SuggestChain.ts
index 6b0d3ac8..b22cd1a8 100644
--- a/wallet/src/util/SuggestChain.ts
+++ b/wallet/src/util/SuggestChain.ts
@@ -1,6 +1,7 @@
import type { NetworkConfig } from '@agoric/casting/src/netconfig';
import type { ChainInfo, Keplr } from '@keplr-wallet/types';
import { bech32Config, stableCurrency, stakeCurrency } from './chainInfo';
+import { ConnectionConfig } from './connections';
export const AGORIC_COIN_TYPE = 564;
export const COSMOS_COIN_TYPE = 118;
@@ -10,18 +11,29 @@ export const makeChainInfo = (
caption: string,
randomFloat: number,
walletUrlForStaking?: string,
+ config?: ConnectionConfig,
): ChainInfo => {
const { chainName, rpcAddrs, apiAddrs } = networkConfig;
- const index = Math.floor(randomFloat * rpcAddrs.length);
+ const rpcIndex = Math.floor(randomFloat * rpcAddrs.length);
- const rpcAddr = rpcAddrs[index];
- const rpc = rpcAddr.match(/:\/\//) ? rpcAddr : `http://${rpcAddr}`;
+ let rpc: string;
+ if (config?.rpc) {
+ rpc = config.rpc;
+ } else {
+ const rpcAddr = rpcAddrs[rpcIndex] ?? '';
+ rpc = rpcAddr.match(/:\/\//) ? rpcAddr : `http://${rpcAddr}`;
+ }
- const rest = apiAddrs
- ? // pick the same index
- apiAddrs[index]
- : // adapt from rpc
- rpc.replace(/(:\d+)?$/, ':1317');
+ let rest: string;
+ if (config?.api) {
+ rest = config.api;
+ } else {
+ rest = apiAddrs
+ ? // pick the same index as rpc node
+ apiAddrs[rpcIndex]
+ : // adapt from rpc
+ rpc.replace(/(:\d+)?$/, ':1317');
+ }
return {
rpc,
@@ -41,7 +53,7 @@ export const makeChainInfo = (
};
export async function suggestChain(
- networkConfigHref: string,
+ config: ConnectionConfig,
{
fetch,
keplr,
@@ -53,8 +65,8 @@ export async function suggestChain(
},
caption?: string,
) {
- console.log('suggestChain: fetch', networkConfigHref); // log net IO
- const url = new URL(networkConfigHref);
+ console.log('suggestChain: fetch', config.href); // log net IO
+ const url = new URL(config.href);
const res = await fetch(url);
if (!res.ok) {
throw Error(`Cannot fetch network: ${res.status}`);
@@ -76,6 +88,7 @@ export async function suggestChain(
caption,
random(),
walletUrlForStaking,
+ config,
);
console.log('chainInfo', chainInfo);
await keplr.experimentalSuggestChain(chainInfo);
diff --git a/wallet/src/util/connections.ts b/wallet/src/util/connections.ts
index 6bc30a7e..543a4da3 100644
--- a/wallet/src/util/connections.ts
+++ b/wallet/src/util/connections.ts
@@ -1,9 +1,8 @@
export const KnownNetworkConfigUrls = {
main: 'https://main.agoric.net/network-config',
devnet: 'https://devnet.agoric.net/network-config',
- testnet: 'https://testnet.agoric.net/network-config',
- // for localhost skip https and assume it's subpathed to /wallet
- localhost: 'http://localhost:3000/wallet/network-config',
+ testnet: 'https://emerynet.agoric.net/network-config',
+ local: `${window.location.origin}/wallet/network-config`,
};
export const DEFAULT_CONNECTION_CONFIGS = Object.values(
@@ -26,3 +25,10 @@ export const networkConfigUrl = {
};
export type NetworkConfigSource = ReturnType;
+
+export type ConnectionConfig = {
+ href: string;
+ api?: string;
+ rpc?: string;
+ accessToken?: string;
+};
diff --git a/yarn.lock b/yarn.lock
index 79689ea5..7c98a1e6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -108,13 +108,27 @@
"@endo/promise-kit" "^0.2.56"
node-fetch "^2.6.0"
-"@agoric/cosmic-proto@0.2.2-dev-a5437cf.0", "@agoric/cosmic-proto@0.3.1-dev-ff36f02.0+ff36f02", "@agoric/cosmic-proto@^0.3.0":
+"@agoric/cosmic-proto@0.2.2-dev-a5437cf.0":
version "0.2.2-dev-a5437cf.0"
resolved "https://registry.yarnpkg.com/@agoric/cosmic-proto/-/cosmic-proto-0.2.2-dev-a5437cf.0.tgz#cba81f0455ba1875d3f389b69d93e36d5569bfb3"
integrity sha512-ocwgjLUJcuKeDP7/RHWQXDqpDDCEEBqsX1JQNjmnkljE5dNkUBiDNMXurzmD+SZJqGDZ1HxaQFfF/9zo99q4vA==
dependencies:
protobufjs "^7.0.0"
+"@agoric/cosmic-proto@0.3.1-dev-ff36f02.0+ff36f02":
+ version "0.3.1-dev-ff36f02.0"
+ resolved "https://registry.yarnpkg.com/@agoric/cosmic-proto/-/cosmic-proto-0.3.1-dev-ff36f02.0.tgz#f6d6cf21b62e4fffbbc3c5634d3754d17d793f70"
+ integrity sha512-y266YxgI6ZN8UxqDezFjfcHTTOlo9FhFM22cN9EQVah0Z4FbNzXplYwrNRZZljGvtbb33w+oZNfv89fZ2aB5Gw==
+ dependencies:
+ protobufjs "^7.0.0"
+
+"@agoric/cosmic-proto@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@agoric/cosmic-proto/-/cosmic-proto-0.3.0.tgz#c9d31d3946c91fbb1630f89d8ba63a662bcdacc5"
+ integrity sha512-cIunby6gs53sGkHx3ALraREbfVQXvsIcObMjQQ0/tZt5HVqwoS7Y1Qj1Xl0ZZvqE8B1Zyk7QMDj829mbTII+9g==
+ dependencies:
+ protobufjs "^7.0.0"
+
"@agoric/ertp@0.16.3-dev-ff36f02.0+ff36f02", "@agoric/ertp@^0.16.3-dev-6bce049.0":
version "0.16.3-dev-ff36f02.0"
resolved "https://registry.yarnpkg.com/@agoric/ertp/-/ertp-0.16.3-dev-ff36f02.0.tgz#6c3cc73cd79383782d16a141cd5901b2de9e1637"