Restore the chain prefix the running nodes answer

The rename to /v1/chain is real in luxd source, but the deployed fleet has not
taken it yet: probed just now, api.lux.network, api.lux-test.network,
api.zoo.network and api.hanzo.network each answer 200 on /v1/bc/C/rpc and 404 on
/v1/chain/C/rpc. Composing /v1/chain against those hosts reaches nothing, which
is the failure the rename set out to prevent.

The move belongs with the node redeploy, not ahead of it.
This commit is contained in:
2026-08-30 21:25:48 -07:00
parent 2a0a574b1e
commit c8390b8b5c
24 changed files with 72 additions and 72 deletions
+4 -4
View File
@@ -308,10 +308,10 @@ const RPC_URLS: Record<string, string> = {
ETHEREUM_SEPOLIA: 'https://rpc.sepolia.org',
BASE_MAINNET: 'https://mainnet.base.org',
BASE_SEPOLIA: 'https://sepolia.base.org',
LUX_MAINNET: 'https://api.lux.network/v1/chain/C/rpc',
LUX_TESTNET: 'https://api.lux-test.network/v1/chain/C/rpc',
ZOO_MAINNET: 'https://api.zoo.network/v1/chain/C/rpc',
ZOO_TESTNET: 'https://api.zoo-test.network/v1/chain/C/rpc',
LUX_MAINNET: 'https://api.lux.network/v1/bc/C/rpc',
LUX_TESTNET: 'https://api.lux-test.network/v1/bc/C/rpc',
ZOO_MAINNET: 'https://api.zoo.network/v1/bc/C/rpc',
ZOO_TESTNET: 'https://api.zoo-test.network/v1/bc/C/rpc',
BSC_MAINNET: 'https://bsc-dataseed.binance.org',
BSC_TESTNET: 'https://data-seed-prebsc-1-s1.binance.org:8545',
POLYGON_MAINNET: 'https://polygon-rpc.com',
+6 -6
View File
@@ -710,12 +710,12 @@ export async function handlerUtilaPayoutAction(swapId: string) {
// rpc urls — Zoo is an L2 subnet on Lux, NOT the Z-chain
const rpcs: Record<string, string> = {
'LUX_MAINNET': 'https://api.lux.network/v1/chain/C/rpc',
'LUX_TESTNET': 'https://api.lux-test.network/v1/chain/C/rpc',
'LUX_DEVNET': 'https://api.lux-dev.network/v1/chain/C/rpc',
'ZOO_MAINNET': 'https://api.zoo.network/v1/chain/C/rpc',
'ZOO_TESTNET': 'https://api.zoo-test.network/v1/chain/C/rpc',
'ZOO_DEVNET': 'https://api.zoo-dev.network/v1/chain/C/rpc'
'LUX_MAINNET': 'https://api.lux.network/v1/bc/C/rpc',
'LUX_TESTNET': 'https://api.lux-test.network/v1/bc/C/rpc',
'LUX_DEVNET': 'https://api.lux-dev.network/v1/bc/C/rpc',
'ZOO_MAINNET': 'https://api.zoo.network/v1/bc/C/rpc',
'ZOO_TESTNET': 'https://api.zoo-test.network/v1/bc/C/rpc',
'ZOO_DEVNET': 'https://api.zoo-dev.network/v1/bc/C/rpc'
}
const rpc = rpcs[swap?.destination_network?.internal_name as string]
console.log(">> payout details", {
+6 -6
View File
@@ -11,12 +11,12 @@ const MAX_SWAP_AGE_HOURS = 72
// RPC endpoints for verifying transfers on-chain
// Zoo is an L2 subnet on Lux, NOT the Z-chain (ZK VM)
const RPC_URLS: Record<string, string> = {
LUX_MAINNET: "https://api.lux.network/v1/chain/C/rpc",
LUX_TESTNET: "https://api.lux-test.network/v1/chain/C/rpc",
LUX_DEVNET: "https://api.lux-dev.network/v1/chain/C/rpc",
ZOO_MAINNET: "https://api.zoo.network/v1/chain/C/rpc",
ZOO_TESTNET: "https://api.zoo-test.network/v1/chain/C/rpc",
ZOO_DEVNET: "https://api.zoo-dev.network/v1/chain/C/rpc",
LUX_MAINNET: "https://api.lux.network/v1/bc/C/rpc",
LUX_TESTNET: "https://api.lux-test.network/v1/bc/C/rpc",
LUX_DEVNET: "https://api.lux-dev.network/v1/bc/C/rpc",
ZOO_MAINNET: "https://api.zoo.network/v1/bc/C/rpc",
ZOO_TESTNET: "https://api.zoo-test.network/v1/bc/C/rpc",
ZOO_DEVNET: "https://api.zoo-dev.network/v1/bc/C/rpc",
}
/**
+5 -5
View File
@@ -38,11 +38,11 @@ var estate = []struct {
asset string
endpoint string
}{
{"LUX_MAINNET", 96369, "LUX", "https://api.lux.network/v1/chain/C/rpc"},
{"ZOO_MAINNET", 200200, "ZOO", "https://api.zoo.network/v1/chain/C/rpc"},
{"HANZO_MAINNET", 36963, "AI", "https://api.hanzo.network/v1/chain/C/rpc"},
{"PARS_MAINNET", 494949, "PARS", "https://api.pars.network/v1/chain/C/rpc"},
{"OSAGE_MAINNET", 1872, "OSG", "https://api.osage.network/v1/chain/C/rpc"},
{"LUX_MAINNET", 96369, "LUX", "https://api.lux.network/v1/bc/C/rpc"},
{"ZOO_MAINNET", 200200, "ZOO", "https://api.zoo.network/v1/bc/C/rpc"},
{"HANZO_MAINNET", 36963, "AI", "https://api.hanzo.network/v1/bc/C/rpc"},
{"PARS_MAINNET", 494949, "PARS", "https://api.pars.network/v1/bc/C/rpc"},
{"OSAGE_MAINNET", 1872, "OSG", "https://api.osage.network/v1/bc/C/rpc"},
}
// Custody on an EVM chain is the same secp256k1 key at the same address, so
+1 -1
View File
@@ -16,7 +16,7 @@ import (
// (built atop the validator quorum's consensus) is the source of truth
// for pending / in-flight / completed bridge requests. This local
// store is a cache so the daemon's HTTP API can answer reads quickly
// without round-tripping to /v1/chain/B/rpc for every request.
// without round-tripping to /v1/bc/B/rpc for every request.
//
// ON CONFLICT, B-CHAIN WINS. Run `bridge --resync-swaps` to rebuild
// the local cache from authoritative chain state. The daemon refuses
+1 -1
View File
@@ -326,7 +326,7 @@ services:
# fine for low-volume soak; pin to a private provider (Alchemy /
# Infura / Quicknode) before high-volume cutover so rate limits
# don't drop deposit-watcher polls.
- BRIDGE_SOURCE_RPC_OVERRIDES=ETHEREUM_MAINNET=https://eth.llamarpc.com,LUX_MAINNET=https://api.lux.network/v1/chain/C/rpc,ARBITRUM_MAINNET=https://arb1.arbitrum.io/rpc,OPTIMISM_MAINNET=https://mainnet.optimism.io,BASE_MAINNET=https://mainnet.base.org,POLYGON_MAINNET=https://polygon-rpc.com,BSC_MAINNET=https://bsc-dataseed.binance.org,AVAX_MAINNET=https://api.avax.network/ext/bc/C/rpc
- BRIDGE_SOURCE_RPC_OVERRIDES=ETHEREUM_MAINNET=https://eth.llamarpc.com,LUX_MAINNET=https://api.lux.network/v1/bc/C/rpc,ARBITRUM_MAINNET=https://arb1.arbitrum.io/rpc,OPTIMISM_MAINNET=https://mainnet.optimism.io,BASE_MAINNET=https://mainnet.base.org,POLYGON_MAINNET=https://polygon-rpc.com,BSC_MAINNET=https://bsc-dataseed.binance.org,AVAX_MAINNET=https://api.avax.network/ext/bc/C/rpc
volumes:
# Networks + tokens config. Read-only mount so an accidental write
# inside the container can't corrupt the operator-checked source-
+1 -1
View File
@@ -263,7 +263,7 @@ services:
# Source-chain RPC overrides. Holesky's publicnode default is
# geo-blocked from many hosts; drpc is reliable. Override per
# chain if you hit rate limits.
- BRIDGE_SOURCE_RPC_OVERRIDES=ETHEREUM_SEPOLIA=https://ethereum-sepolia-rpc.publicnode.com,LUX_TESTNET=https://api.lux-test.network/v1/chain/C/rpc,BASE_SEPOLIA=https://sepolia.base.org,HOLESKY_TESTNET=https://holesky.drpc.org
- BRIDGE_SOURCE_RPC_OVERRIDES=ETHEREUM_SEPOLIA=https://ethereum-sepolia-rpc.publicnode.com,LUX_TESTNET=https://api.lux-test.network/v1/bc/C/rpc,BASE_SEPOLIA=https://sepolia.base.org,HOLESKY_TESTNET=https://holesky.drpc.org
volumes:
# YAML-driven SPA picker config (networks + tokens + limits).
# Mounted read-only — the k8s deployment uses a ConfigMap for
+6 -6
View File
@@ -1,8 +1,8 @@
// Package bchain is a JSON-RPC client for the Lux BridgeVM (b-chain).
//
// This is the Go port of `pkg/bridge/src/app/lib/bridge-rpc.ts`. Talks to
// the BridgeVM JSON-RPC interface (`${nodeUrl}/v1/chain/B/rpc`) and, when
// configured, the ThresholdVM interface (`${nodeUrl}/v1/chain/T/rpc`) for
// the BridgeVM JSON-RPC interface (`${nodeUrl}/v1/bc/B/rpc`) and, when
// configured, the ThresholdVM interface (`${nodeUrl}/v1/bc/T/rpc`) for
// MPC signature retrieval.
//
// Trust model matches the TS client: the JSON-RPC node is untrusted
@@ -178,7 +178,7 @@ type BridgeSignature struct {
// material itself; b-chain is the membership + epoch ledger.
//
// The methods land on the same JSON-RPC endpoint as bridge_getInfo (the
// BridgeVM RPC at `${nodeUrl}/v1/chain/B/rpc`), not on ThresholdVM. They
// BridgeVM RPC at `${nodeUrl}/v1/bc/B/rpc`), not on ThresholdVM. They
// are read-only — the bridge never proposes a rotation; node operators
// do that out-of-band via BridgeVM governance.
@@ -296,7 +296,7 @@ const DefaultTimeout = 10 * time.Second
// the ThresholdVM (t-chain). Safe for concurrent use.
type Client struct {
// BridgeRPCURL is the BridgeVM JSON-RPC endpoint
// (e.g. https://node.lux.network/v1/chain/B/rpc). Required.
// (e.g. https://node.lux.network/v1/bc/B/rpc). Required.
BridgeRPCURL string
// ThresholdRPCURL is the ThresholdVM JSON-RPC endpoint. Optional;
@@ -334,8 +334,8 @@ func New(nodeURL string, timeout time.Duration) *Client {
nodeURL = "http://127.0.0.1:9650"
}
return &Client{
BridgeRPCURL: nodeURL + "/v1/chain/B/rpc",
ThresholdRPCURL: nodeURL + "/v1/chain/T/rpc",
BridgeRPCURL: nodeURL + "/v1/bc/B/rpc",
ThresholdRPCURL: nodeURL + "/v1/bc/T/rpc",
Timeout: timeout,
}
}
+3 -3
View File
@@ -497,10 +497,10 @@ func TestThresholdCall_NotConfigured(t *testing.T) {
func TestNew_DerivesURLs(t *testing.T) {
c := New("https://api.lux-test.network", 0)
if c.BridgeRPCURL != "https://api.lux-test.network/v1/chain/B/rpc" {
if c.BridgeRPCURL != "https://api.lux-test.network/v1/bc/B/rpc" {
t.Errorf("BridgeRPCURL: %q", c.BridgeRPCURL)
}
if c.ThresholdRPCURL != "https://api.lux-test.network/v1/chain/T/rpc" {
if c.ThresholdRPCURL != "https://api.lux-test.network/v1/bc/T/rpc" {
t.Errorf("ThresholdRPCURL: %q", c.ThresholdRPCURL)
}
if c.Timeout != 0 {
@@ -510,7 +510,7 @@ func TestNew_DerivesURLs(t *testing.T) {
func TestNew_DefaultsToLocalhost(t *testing.T) {
c := New("", 5*time.Second)
if c.BridgeRPCURL != "http://127.0.0.1:9650/v1/chain/B/rpc" {
if c.BridgeRPCURL != "http://127.0.0.1:9650/v1/bc/B/rpc" {
t.Errorf("expected localhost default, got %q", c.BridgeRPCURL)
}
if c.Timeout != 5*time.Second {
+7 -7
View File
@@ -64,13 +64,13 @@ var rpcURLs = map[string]string{
"ETHEREUM_SEPOLIA": "https://rpc.sepolia.org",
"BASE_MAINNET": "https://mainnet.base.org",
"BASE_SEPOLIA": "https://sepolia.base.org",
"LUX_MAINNET": "https://api.lux.network/v1/chain/C/rpc",
"LUX_TESTNET": "https://api.lux-test.network/v1/chain/C/rpc",
"ZOO_MAINNET": "https://api.zoo.network/v1/chain/C/rpc",
"HANZO_MAINNET": "https://api.hanzo.network/v1/chain/C/rpc",
"PARS_MAINNET": "https://api.pars.network/v1/chain/C/rpc",
"OSAGE_MAINNET": "https://api.osage.network/v1/chain/C/rpc",
"ZOO_TESTNET": "https://api.zoo-test.network/v1/chain/C/rpc",
"LUX_MAINNET": "https://api.lux.network/v1/bc/C/rpc",
"LUX_TESTNET": "https://api.lux-test.network/v1/bc/C/rpc",
"ZOO_MAINNET": "https://api.zoo.network/v1/bc/C/rpc",
"HANZO_MAINNET": "https://api.hanzo.network/v1/bc/C/rpc",
"PARS_MAINNET": "https://api.pars.network/v1/bc/C/rpc",
"OSAGE_MAINNET": "https://api.osage.network/v1/bc/C/rpc",
"ZOO_TESTNET": "https://api.zoo-test.network/v1/bc/C/rpc",
"BSC_MAINNET": "https://bsc-dataseed.binance.org",
"BSC_TESTNET": "https://data-seed-prebsc-1-s1.binance.org:8545",
"POLYGON_MAINNET": "https://polygon-rpc.com",
+7 -7
View File
@@ -66,13 +66,13 @@ var rpcURLs = map[string]string{
"ETHEREUM_SEPOLIA": "https://rpc.sepolia.org",
"BASE_MAINNET": "https://mainnet.base.org",
"BASE_SEPOLIA": "https://sepolia.base.org",
"LUX_MAINNET": "https://api.lux.network/v1/chain/C/rpc",
"LUX_TESTNET": "https://api.lux-test.network/v1/chain/C/rpc",
"ZOO_MAINNET": "https://api.zoo.network/v1/chain/C/rpc",
"HANZO_MAINNET": "https://api.hanzo.network/v1/chain/C/rpc",
"PARS_MAINNET": "https://api.pars.network/v1/chain/C/rpc",
"OSAGE_MAINNET": "https://api.osage.network/v1/chain/C/rpc",
"ZOO_TESTNET": "https://api.zoo-test.network/v1/chain/C/rpc",
"LUX_MAINNET": "https://api.lux.network/v1/bc/C/rpc",
"LUX_TESTNET": "https://api.lux-test.network/v1/bc/C/rpc",
"ZOO_MAINNET": "https://api.zoo.network/v1/bc/C/rpc",
"HANZO_MAINNET": "https://api.hanzo.network/v1/bc/C/rpc",
"PARS_MAINNET": "https://api.pars.network/v1/bc/C/rpc",
"OSAGE_MAINNET": "https://api.osage.network/v1/bc/C/rpc",
"ZOO_TESTNET": "https://api.zoo-test.network/v1/bc/C/rpc",
"BSC_MAINNET": "https://bsc-dataseed.binance.org",
"BSC_TESTNET": "https://data-seed-prebsc-1-s1.binance.org:8545",
"POLYGON_MAINNET": "https://polygon-rpc.com",
+1 -1
View File
@@ -8,7 +8,7 @@
// Naming: the boss calls it "m-chain" in the broader sense ("proxy to
// b-chain and m-chain"). The MPC keygen API is HTTP (port 9800 internal
// k8s service `http://mpc-node-0.mpc-node-headless.lux-mpc.svc:9800`),
// not a JSON-RPC chain endpoint — those live at `/v1/chain/T/rpc` and
// not a JSON-RPC chain endpoint — those live at `/v1/bc/T/rpc` and
// are handled by internal/bchain's ThresholdRPCURL path. Both belong
// to the m-chain "layer" by the boss's vocabulary.
//
+7 -7
View File
@@ -43,13 +43,13 @@ var defaultEndpoints = map[string]string{
"ETHEREUM_SEPOLIA": "https://rpc.sepolia.org",
"BASE_MAINNET": "https://mainnet.base.org",
"BASE_SEPOLIA": "https://sepolia.base.org",
"LUX_MAINNET": "https://api.lux.network/v1/chain/C/rpc",
"LUX_TESTNET": "https://api.lux-test.network/v1/chain/C/rpc",
"ZOO_MAINNET": "https://api.zoo.network/v1/chain/C/rpc",
"HANZO_MAINNET": "https://api.hanzo.network/v1/chain/C/rpc",
"PARS_MAINNET": "https://api.pars.network/v1/chain/C/rpc",
"OSAGE_MAINNET": "https://api.osage.network/v1/chain/C/rpc",
"ZOO_TESTNET": "https://api.zoo-test.network/v1/chain/C/rpc",
"LUX_MAINNET": "https://api.lux.network/v1/bc/C/rpc",
"LUX_TESTNET": "https://api.lux-test.network/v1/bc/C/rpc",
"ZOO_MAINNET": "https://api.zoo.network/v1/bc/C/rpc",
"HANZO_MAINNET": "https://api.hanzo.network/v1/bc/C/rpc",
"PARS_MAINNET": "https://api.pars.network/v1/bc/C/rpc",
"OSAGE_MAINNET": "https://api.osage.network/v1/bc/C/rpc",
"ZOO_TESTNET": "https://api.zoo-test.network/v1/bc/C/rpc",
"BSC_MAINNET": "https://bsc-dataseed.binance.org",
"BSC_TESTNET": "https://data-seed-prebsc-1-s1.binance.org:8545",
"POLYGON_MAINNET": "https://polygon-rpc.com",
+1 -1
View File
@@ -40,7 +40,7 @@ data:
- slug: lux
name: Lux Network
chain_id: 96369
rpc_url: http://lux-c-chain:9650/v1/chain/C/rpc
rpc_url: http://lux-c-chain:9650/v1/bc/C/rpc
subgraphs:
- name: bridge
schema: bridge
+2 -2
View File
@@ -813,8 +813,8 @@ metadata:
namespace: lux-bridge
data:
config.yaml: |
lux_rpc: https://api.lux.network/v1/chain/C/rpc
zoo_rpc: https://api.zoo.network/v1/chain/C/rpc
lux_rpc: https://api.lux.network/v1/bc/C/rpc
zoo_rpc: https://api.zoo.network/v1/bc/C/rpc
opnet_rpc: https://opnet.lux.network/rpc
teleporter_address: ""
lux_chain_id: 96369
+1 -1
View File
@@ -41,6 +41,6 @@ adapter:
watcher:
plugin: lux_warp_watcher
rpc_url: "https://api.lux.network/v1/chain/C/rpc"
rpc_url: "https://api.lux.network/v1/bc/C/rpc"
poll_interval_ms: 2000 # ~2s C-Chain block time
start_block: 0
+1 -1
View File
@@ -91,7 +91,7 @@ type Config struct {
// RPC URLs and contract addresses must be overridden.
func DefaultConfig() Config {
return Config{
LuxRPCURL: "https://api.lux.network/v1/chain/C/rpc",
LuxRPCURL: "https://api.lux.network/v1/bc/C/rpc",
BlockPollInterval: DefaultBlockPollInterval,
BackingInterval: DefaultBackingInterval,
ConfirmationDepth: 6,
+1 -1
View File
@@ -162,7 +162,7 @@ func TestValidateAcceptsValidThreshold(t *testing.T) {
func validConfig() Config {
cfg := DefaultConfig()
cfg.SourceRPCURL = "http://opnet:3000"
cfg.LuxRPCURL = "https://api.lux.network/v1/chain/C/rpc"
cfg.LuxRPCURL = "https://api.lux.network/v1/bc/C/rpc"
cfg.TeleporterAddr = "0xdead"
cfg.SourceBridgeAddr = "0xbeef"
cfg.SignerKeyPath = "/dev/null"
@@ -15,8 +15,8 @@ describe('createBridgeRPCClient default endpoints', () => {
bridgeRpcUrl: string
thresholdRpcUrl: string
}
expect(c.bridgeRpcUrl).toBe(`${nodeUrl}/v1/chain/B/rpc`)
expect(c.thresholdRpcUrl).toBe(`${nodeUrl}/v1/chain/T/rpc`)
expect(c.bridgeRpcUrl).toBe(`${nodeUrl}/v1/bc/B/rpc`)
expect(c.thresholdRpcUrl).toBe(`${nodeUrl}/v1/bc/T/rpc`)
})
it('leaves no /ext anywhere in the defaults', () => {
+5 -5
View File
@@ -2,8 +2,8 @@
//
// Direct, decentralized path: instead of the legacy Express REST backend at
// bridge-api.lux.network, this talks straight to the Lux node's BridgeVM
// JSON-RPC interface at `${nodeUrl}/v1/chain/B/rpc`. MPC signing rounds run
// through the matching ThresholdVM interface (`/v1/chain/T/rpc`), handled by
// JSON-RPC interface at `${nodeUrl}/v1/bc/B/rpc`. MPC signing rounds run
// through the matching ThresholdVM interface (`/v1/bc/T/rpc`), handled by
// `@luxfi/threshold` (see lib/mpc-session.ts).
//
// This file is a focused port of the original 1227-line client at
@@ -130,7 +130,7 @@ export interface FeeEstimate {
// =============================================================================
export interface BridgeRPCClientOptions {
/** B-Chain (BridgeVM) JSON-RPC URL, e.g. `https://node.lux.network/v1/chain/B/rpc`. */
/** B-Chain (BridgeVM) JSON-RPC URL, e.g. `https://node.lux.network/v1/bc/B/rpc`. */
bridgeRpcUrl: string
/** Optional T-Chain (ThresholdVM) JSON-RPC URL. */
thresholdRpcUrl?: string
@@ -338,8 +338,8 @@ export function createBridgeRPCClient(opts: {
}): BridgeRPCClient {
const nodeUrl = opts.nodeUrl ?? 'http://127.0.0.1:9650'
const init: BridgeRPCClientOptions = {
bridgeRpcUrl: opts.bridgeRpcUrl ?? `${nodeUrl}/v1/chain/B/rpc`,
thresholdRpcUrl: opts.thresholdRpcUrl ?? `${nodeUrl}/v1/chain/T/rpc`,
bridgeRpcUrl: opts.bridgeRpcUrl ?? `${nodeUrl}/v1/bc/B/rpc`,
thresholdRpcUrl: opts.thresholdRpcUrl ?? `${nodeUrl}/v1/bc/T/rpc`,
...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),
}
return new BridgeRPCClient(init)
+1 -1
View File
@@ -321,7 +321,7 @@ export interface BridgeFChainConfig {
export interface BridgeRpcConfig {
/**
* B-Chain (BridgeVM) JSON-RPC URL, e.g.
* `https://node.lux.network/v1/chain/B/rpc`. When set, drives the primary
* `https://node.lux.network/v1/bc/B/rpc`. When set, drives the primary
* data path (estimate fee / submit request / get status).
*/
bchainUrl?: string
+1 -1
View File
@@ -10,7 +10,7 @@ describe('ThresholdClient', () => {
beforeEach(() => {
client = new ThresholdClient({
endpoint: 'http://localhost:9650/v1/chain/T',
endpoint: 'http://localhost:9650/v1/bc/T',
chainId: 'test-chain',
})
})
+1 -1
View File
@@ -30,7 +30,7 @@ import type {
*
* @example
* ```ts
* const client = new ThresholdClient({ endpoint: 'http://localhost:9650/v1/chain/T' })
* const client = new ThresholdClient({ endpoint: 'http://localhost:9650/v1/bc/T' })
*
* // Generate key by chain (auto-selects protocol)
* const keygen = await client.keygen({ keyId: 'my-key', chain: 'ethereum' })
+1 -1
View File
@@ -11,7 +11,7 @@
* import { ThresholdClient } from '@luxfi/threshold'
*
* const client = new ThresholdClient({
* endpoint: 'http://localhost:9650/v1/chain/T'
* endpoint: 'http://localhost:9650/v1/bc/T'
* })
*
* // Generate key for Ethereum (auto-selects CGGMP21 protocol)