Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b8494eb83a | |||
| cb549dfe25 |
Generated
+5600
-45
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"run_cli": "clear && ts-node src/cli.ts",
|
||||
"test": "ts-mocha tests/**/*.test.ts",
|
||||
"coverage": "nyc npm test",
|
||||
"lint": "eslint \"**/*.ts\"",
|
||||
@@ -22,6 +23,7 @@
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.15",
|
||||
"@types/expect": "^24.3.0",
|
||||
"@types/inquirer": "^8.1.3",
|
||||
"@types/mocha": "^8.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.14.0",
|
||||
"@typescript-eslint/parser": "^4.14.0",
|
||||
@@ -35,10 +37,11 @@
|
||||
"typescript": "^4.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"@cosmjs/cosmwasm-stargate": "^0.25.5",
|
||||
"@cosmjs/stargate": "^0.25.5",
|
||||
"@cosmjs/math": "^0.25.5",
|
||||
"@cosmjs/proto-signing": "^0.25.5"
|
||||
"@cosmjs/proto-signing": "^0.25.5",
|
||||
"@cosmjs/stargate": "^0.25.5",
|
||||
"axios": "^0.21.1",
|
||||
"inquirer": "^8.2.0"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MixNodeBond, PagedMixnodeResponse } from "../types";
|
||||
import { INetClient } from "../net-client"
|
||||
import { INetClient } from "../net-client";
|
||||
import { IQueryClient } from "../query-client";
|
||||
import { VALIDATOR_API_MIXNODES, VALIDATOR_API_PORT } from "../index";
|
||||
import axios from "axios";
|
||||
@@ -13,9 +13,9 @@ export { MixnodesCache };
|
||||
* available for querying.
|
||||
* */
|
||||
export default class MixnodesCache {
|
||||
mixNodes: MixNodeBond[]
|
||||
client: INetClient | IQueryClient
|
||||
perPage: number
|
||||
mixNodes: MixNodeBond[];
|
||||
client: INetClient | IQueryClient;
|
||||
perPage: number;
|
||||
|
||||
constructor(client: INetClient | IQueryClient, perPage: number) {
|
||||
this.client = client;
|
||||
@@ -31,16 +31,20 @@ export default class MixnodesCache {
|
||||
let response: PagedMixnodeResponse;
|
||||
let next: string | undefined = undefined;
|
||||
for (;;) {
|
||||
response = await this.client.getMixNodes(contractAddress, this.perPage, next);
|
||||
newMixnodes = newMixnodes.concat(response.nodes)
|
||||
response = await this.client.getMixNodes(
|
||||
contractAddress,
|
||||
this.perPage,
|
||||
next
|
||||
);
|
||||
newMixnodes = newMixnodes.concat(response.nodes);
|
||||
next = response.start_next_after;
|
||||
// if `start_next_after` is not set, we're done
|
||||
if (!next) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.mixNodes = newMixnodes
|
||||
this.mixNodes = newMixnodes;
|
||||
return this.mixNodes;
|
||||
}
|
||||
|
||||
@@ -55,6 +59,6 @@ export default class MixnodesCache {
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
throw new Error("None of the provided validators seem to be alive")
|
||||
throw new Error("None of the provided validators seem to be alive");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
import ValidatorClient from "./index";
|
||||
import inquirer from "inquirer";
|
||||
// This script runs a CLI to consume the Validator and provide mixnet information to the user
|
||||
|
||||
const VALIDATOR_URLS: string[] = [
|
||||
"https://testnet-milhon-validator1.nymtech.net",
|
||||
// "https://testnet-milhon-validator2.nymtech.net", // <-- val 2 doesnt work apparently.
|
||||
];
|
||||
const DENOM = "punk";
|
||||
const MOCK_MNEMONIC =
|
||||
"vault risk throw flat garlic pretty clay senior birth correct panic floor around pen horror mail entry arrest zoo devote message evoke street total";
|
||||
// ^^ addr: punk10dxwmqjy72s9nkm9x9pluyn6pyx0gkptjhs4k9
|
||||
// curr balance: 899999747
|
||||
|
||||
// const MOCK_MNEMONIC =
|
||||
// "oil once motion cute crawl patch happy wave donkey zoo retreat matrix emerge adult very universe aware error snap credit actress couple upset engine";
|
||||
// ^^ addr: punk1yzr7gtmtlfd0s7s9wpexhteeu05y4xlcvh65eh
|
||||
// curr balance: 5045 UPUNK
|
||||
|
||||
// const MOCK_MNEMONIC =
|
||||
// "sample menu edit midnight guard review call record horn antenna stairs awkward fringe document during amazing twelve wise wide escape matter betray staff someone";
|
||||
// ^^ addr: punk1wn8lwxe5hvdtx60c6p7ekskmu75agwfrslf0qs
|
||||
// curr balance:
|
||||
|
||||
type AccountType = {
|
||||
addr: string;
|
||||
client: any;
|
||||
mnemonic?: string;
|
||||
};
|
||||
function validatorCli() {
|
||||
// define funcs to be used in CLI switch-case
|
||||
|
||||
let state: AccountType = {
|
||||
addr: "",
|
||||
client: null,
|
||||
mnemonic: "",
|
||||
};
|
||||
|
||||
function restartApp() {
|
||||
setTimeout(() => {
|
||||
validatorCli();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function generateNewAccount() {
|
||||
const mnemonic = ValidatorClient.randomMnemonic();
|
||||
ValidatorClient.mnemonicToAddress(mnemonic, "punk")
|
||||
.then((address) => {
|
||||
console.log("Your address is: ", address);
|
||||
console.log("Your mnemonic is: ", mnemonic);
|
||||
return address;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("err", err);
|
||||
});
|
||||
restartApp();
|
||||
}
|
||||
|
||||
function sendFundsMenu() {
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
name: "recipient",
|
||||
type: "input",
|
||||
message: "please enter the receipient:",
|
||||
},
|
||||
{
|
||||
name: "amount",
|
||||
type: "input",
|
||||
message: "please enter the amount (UPUNK):",
|
||||
},
|
||||
])
|
||||
.then(async ({ recipient, amount }) => {
|
||||
const { addr, client } = state;
|
||||
console.log(
|
||||
`🔥 Hold Tight - Sending ${amount}UPUNK to ${recipient} 🚀`
|
||||
);
|
||||
|
||||
const res = await client.send(addr, recipient, [
|
||||
{
|
||||
denom: "upunk",
|
||||
amount: amount,
|
||||
},
|
||||
]);
|
||||
console.log("Funds Transfer Response:", res);
|
||||
restartApp();
|
||||
});
|
||||
}
|
||||
|
||||
async function delegateGateway() {
|
||||
console.log(
|
||||
"unfortunately - gateway delegation is switched off at the moment."
|
||||
);
|
||||
startTransactionMenu();
|
||||
// const id = "punk1yzr7gtmtlfd0s7s9wpexhteeu05y4xlcvh65eh";
|
||||
// const gatewayID = "EQhjPpUuy4i1u87nfQMW21WiBT5mJk4dcq4ju7Vct7cB";
|
||||
// const coin = {
|
||||
// denom: "upunk",
|
||||
// amount: "101",
|
||||
// };
|
||||
// const res = await state.client.delegateToMixnode(gatewayID, coin);
|
||||
// console.log("delegateMixnode ==> ", res);
|
||||
}
|
||||
|
||||
async function delegateMixnode() {
|
||||
const mixNodeID = "2cFpCe7yP79CcuRpf6JBRdJaSp7JF5YcA5SHi8JVm1d2";
|
||||
// const mixNodeID = "2Vrr7s2peGiWsPh6xY3ZFEMDRmMNv8xLBUtV5XMyQLSB";
|
||||
const coin = {
|
||||
denom: "upunk",
|
||||
amount: "1001",
|
||||
};
|
||||
const res = await state.client.delegateToMixnode(mixNodeID, coin);
|
||||
console.log("delegate to mixnode response: ", res);
|
||||
}
|
||||
async function findMinimumMixnodeBond() {
|
||||
const res = await state.client.minimumMixnodeBond();
|
||||
console.log("res is back ", res);
|
||||
}
|
||||
|
||||
async function bondMixnode() {
|
||||
state.client.bondMixnode();
|
||||
}
|
||||
|
||||
async function checkOwnsMixnodes() {
|
||||
const res = await state.client.ownsMixNode();
|
||||
console.log("owns mixnode? ", res);
|
||||
}
|
||||
function startTransactionMenu() {
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: "list",
|
||||
name: "task",
|
||||
message: "What now?",
|
||||
choices: [
|
||||
"send_funds",
|
||||
"get_mixnodes",
|
||||
"refresh_mixnodes",
|
||||
"refresh_val_api_mixnodes",
|
||||
"min_mixn_bond",
|
||||
"bond_mixnode",
|
||||
"delegate_mixnode",
|
||||
"delegate_gateway",
|
||||
"check_owns_mixnode",
|
||||
],
|
||||
},
|
||||
])
|
||||
.then(({ task }) => {
|
||||
switch (task) {
|
||||
case "send_funds":
|
||||
sendFundsMenu();
|
||||
break;
|
||||
case "get_mixnodes":
|
||||
getMixnodes();
|
||||
break;
|
||||
case "refresh_mixnodes":
|
||||
refreshMixnodes();
|
||||
break;
|
||||
case "refresh_val_api_mixnodes":
|
||||
refreshValApiMixnodes();
|
||||
break;
|
||||
case "min_mixn_bond":
|
||||
findMinimumMixnodeBond();
|
||||
break;
|
||||
case "bond_mixnode":
|
||||
bondMixnode();
|
||||
break;
|
||||
case "delegate_gateway":
|
||||
delegateGateway();
|
||||
break;
|
||||
case "delegate_mixnode":
|
||||
delegateMixnode();
|
||||
break;
|
||||
case "check_owns_mixnode":
|
||||
checkOwnsMixnodes();
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function queryUserAccount() {
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: "input",
|
||||
name: "query_user",
|
||||
message: "Please enter the public address of user you wish to query",
|
||||
},
|
||||
])
|
||||
.then(async ({ query_user }) => {
|
||||
let response = "";
|
||||
try {
|
||||
const client = await ValidatorClient.connectForQuery(
|
||||
query_user,
|
||||
VALIDATOR_URLS,
|
||||
DENOM
|
||||
);
|
||||
const balance = await client.getBalance(query_user);
|
||||
response = `User ${query_user} has a balance of ${balance?.amount}${balance?.denom}`;
|
||||
console.log(response);
|
||||
return validatorCli();
|
||||
} catch (error) {
|
||||
console.log("error back ", error);
|
||||
return validatorCli();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function refreshMixnodes() {
|
||||
const res = await state.client.refreshMixNodes(
|
||||
"punk1yksauczytk60x5cejaras8w6nwf7r772n3kwkp"
|
||||
);
|
||||
console.log("done:", res);
|
||||
}
|
||||
function connectAccount() {
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
name: "user_mnemonic",
|
||||
type: "input",
|
||||
message: "please enter your mnemonic:",
|
||||
},
|
||||
])
|
||||
.then(async ({ user_mnemonic }) => {
|
||||
console.log("Connecting...");
|
||||
const addr = await ValidatorClient.mnemonicToAddress(
|
||||
MOCK_MNEMONIC,
|
||||
// user_mnemonic,
|
||||
"punk"
|
||||
);
|
||||
|
||||
const client = await ValidatorClient.connect(
|
||||
addr,
|
||||
MOCK_MNEMONIC,
|
||||
VALIDATOR_URLS,
|
||||
DENOM
|
||||
);
|
||||
|
||||
state = {
|
||||
addr,
|
||||
mnemonic: MOCK_MNEMONIC,
|
||||
client,
|
||||
};
|
||||
|
||||
const balance = await client.getBalance(addr);
|
||||
console.log(`connected to validator, our address is ${client.address}`);
|
||||
console.log("connected to validator", client.urls[0]);
|
||||
console.log(
|
||||
`💰 Your balance is ${balance?.amount}${balance?.denom.toUpperCase()}`
|
||||
);
|
||||
|
||||
startTransactionMenu();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("error: ", err);
|
||||
});
|
||||
}
|
||||
function buildAWallet() {
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
message: "enter your mnemonic to build wallet:",
|
||||
type: "input",
|
||||
name: "mnemonic",
|
||||
},
|
||||
])
|
||||
.then(async ({ mnemonic }) => {
|
||||
const res = await ValidatorClient.buildWallet(mnemonic, DENOM);
|
||||
console.log("Build_Wallet Response: ", res);
|
||||
});
|
||||
}
|
||||
async function refreshValApiMixnodes() {
|
||||
const res = await state.client.refreshValidatorAPIMixNodes();
|
||||
console.log("res is back: ", res);
|
||||
}
|
||||
function getMixnodes() {
|
||||
const res = state.client.mixNodesCache;
|
||||
console.log("Mixnodes", res);
|
||||
}
|
||||
// app provides a list of possible tasks
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: "list",
|
||||
name: "task",
|
||||
message: "Yo, What would you like to do today?",
|
||||
choices: [
|
||||
"create_account",
|
||||
"connect_account",
|
||||
"build_wallet",
|
||||
"query_user",
|
||||
],
|
||||
},
|
||||
])
|
||||
.then(({ task }) => {
|
||||
switch (task) {
|
||||
case "create_account":
|
||||
generateNewAccount();
|
||||
break;
|
||||
case "connect_account":
|
||||
connectAccount();
|
||||
break;
|
||||
case "build_wallet":
|
||||
buildAWallet();
|
||||
break;
|
||||
case "query_user":
|
||||
queryUserAccount();
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
validatorCli();
|
||||
+442
-144
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import NetClient, { INetClient } from "./net-client";
|
||||
import {
|
||||
StateParams,
|
||||
@@ -8,7 +9,7 @@ import {
|
||||
MixNode,
|
||||
GatewayBond,
|
||||
Gateway,
|
||||
SendRequest
|
||||
SendRequest,
|
||||
} from "./types";
|
||||
import { Bip39, Random } from "@cosmjs/crypto";
|
||||
import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing";
|
||||
@@ -20,7 +21,7 @@ import {
|
||||
InstantiateResult,
|
||||
MigrateResult,
|
||||
UploadMeta,
|
||||
UploadResult
|
||||
UploadResult,
|
||||
} from "@cosmjs/cosmwasm-stargate";
|
||||
import {
|
||||
CoinMap,
|
||||
@@ -29,7 +30,7 @@ import {
|
||||
nativeCoinToDisplay,
|
||||
printableBalance,
|
||||
printableCoin,
|
||||
nativeToPrintable
|
||||
nativeToPrintable,
|
||||
} from "./currency";
|
||||
import GatewaysCache from "./caches/gateways";
|
||||
import QueryClient, { IQueryClient } from "./query-client";
|
||||
@@ -50,22 +51,26 @@ export {
|
||||
printableBalance,
|
||||
nativeToPrintable,
|
||||
MappedCoin,
|
||||
CoinMap
|
||||
}
|
||||
export {nymGasLimits, nymGasPrice}
|
||||
CoinMap,
|
||||
};
|
||||
export { nymGasLimits, nymGasPrice };
|
||||
|
||||
export default class ValidatorClient {
|
||||
private readonly client: INetClient | IQueryClient
|
||||
private readonly client: INetClient | IQueryClient;
|
||||
private readonly contractAddress: string;
|
||||
private readonly denom: string;
|
||||
private failedRequests: number = 0;
|
||||
private gatewayCache: GatewaysCache
|
||||
private failedRequests = 0;
|
||||
private gatewayCache: GatewaysCache;
|
||||
private mixNodesCache: MixnodesCache;
|
||||
private readonly prefix: string;
|
||||
urls: string[];
|
||||
|
||||
|
||||
private constructor(urls: string[], client: INetClient | IQueryClient, contractAddress: string, prefix: string) {
|
||||
private constructor(
|
||||
urls: string[],
|
||||
client: INetClient | IQueryClient,
|
||||
contractAddress: string,
|
||||
prefix: string
|
||||
) {
|
||||
this.urls = urls;
|
||||
this.client = client;
|
||||
this.mixNodesCache = new MixnodesCache(client, 100);
|
||||
@@ -75,73 +80,135 @@ export default class ValidatorClient {
|
||||
this.denom = "u" + prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contractAddress the user's contract address eg. `punk23o85698370891702470413487`
|
||||
* @param mnemonic A mnemonic string from which to generate a public/private keypair.
|
||||
* @param urls the validator URLs in either array of string format.
|
||||
* @param prefix the denom eg. `punk`
|
||||
* @returns user's instance of the Validator Client.
|
||||
*/
|
||||
// allows also entering 'string' by itself for backwards compatibility
|
||||
static async connect(contractAddress: string, mnemonic: string, urls: string | string[], prefix: string): Promise<ValidatorClient> {
|
||||
const validatorUrls = this.ensureArray(urls)
|
||||
static async connect(
|
||||
contractAddress: string,
|
||||
mnemonic: string,
|
||||
urls: string | string[],
|
||||
prefix: string
|
||||
): Promise<ValidatorClient> {
|
||||
const validatorUrls = this.ensureArray(urls);
|
||||
const wallet = await ValidatorClient.buildWallet(mnemonic, prefix);
|
||||
|
||||
// if we have more than a single validator, try to perform initial connection until we succeed or run out of options
|
||||
if (validatorUrls.length > 1) {
|
||||
for (let i = 0; i < validatorUrls.length; i++) {
|
||||
console.log("Attempting initial connection to", validatorUrls[0])
|
||||
const netClient = await NetClient.connect(wallet, validatorUrls[0], prefix).catch((_) => ValidatorClient.moveArrayHeadToBack(validatorUrls))
|
||||
console.log("Attempting initial connection to", validatorUrls[0]);
|
||||
const netClient = await NetClient.connect(
|
||||
wallet,
|
||||
validatorUrls[0],
|
||||
prefix
|
||||
).catch((_) => ValidatorClient.moveArrayHeadToBack(validatorUrls));
|
||||
if (netClient !== undefined) {
|
||||
return new ValidatorClient(validatorUrls, netClient, contractAddress, prefix);
|
||||
return new ValidatorClient(
|
||||
validatorUrls,
|
||||
netClient,
|
||||
contractAddress,
|
||||
prefix
|
||||
);
|
||||
}
|
||||
console.log("Initial connection to", validatorUrls[0], "failed")
|
||||
console.log("Initial connection to", validatorUrls[0], "failed");
|
||||
}
|
||||
} else {
|
||||
const netClient = await NetClient.connect(wallet, validatorUrls[0], prefix)
|
||||
return new ValidatorClient(validatorUrls, netClient, contractAddress, prefix);
|
||||
const netClient = await NetClient.connect(
|
||||
wallet,
|
||||
validatorUrls[0],
|
||||
prefix
|
||||
);
|
||||
return new ValidatorClient(
|
||||
validatorUrls,
|
||||
netClient,
|
||||
contractAddress,
|
||||
prefix
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error("None of the provided validators seem to be alive")
|
||||
throw new Error("None of the provided validators seem to be alive");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is the same as connect() but doesnt require a mnemonic
|
||||
* as you cannot transfer/withdraw once connected. It is effectively ReadOnly.
|
||||
* @param contractAddress the user's contract address eg. `punk23o85698370891702470413487`
|
||||
* @param urls the validator URLs in either array of string format.
|
||||
* @param prefix the denom eg. `punk`
|
||||
* @returns user's instance of the Validator Client.
|
||||
*/
|
||||
// allows also entering 'string' by itself for backwards compatibility
|
||||
static async connectForQuery(contractAddress: string, urls: string | string[], prefix: string): Promise<ValidatorClient> {
|
||||
const validatorUrls = this.ensureArray(urls)
|
||||
static async connectForQuery(
|
||||
contractAddress: string,
|
||||
urls: string | string[],
|
||||
prefix: string
|
||||
): Promise<ValidatorClient> {
|
||||
const validatorUrls = this.ensureArray(urls);
|
||||
|
||||
// if we have more than a single validator, try to perform initial connection until we succeed or run out of options
|
||||
if (validatorUrls.length > 1) {
|
||||
for (let i = 0; i < validatorUrls.length; i++) {
|
||||
console.log("Attempting initial connection to", validatorUrls[0])
|
||||
const queryClient = await QueryClient.connect(validatorUrls[0]).catch((_) => ValidatorClient.moveArrayHeadToBack(validatorUrls))
|
||||
console.log("Attempting initial connection to", validatorUrls[0]);
|
||||
const queryClient = await QueryClient.connect(validatorUrls[0]).catch(
|
||||
(_) => ValidatorClient.moveArrayHeadToBack(validatorUrls)
|
||||
);
|
||||
if (queryClient !== undefined) {
|
||||
return new ValidatorClient(validatorUrls, queryClient, contractAddress, prefix)
|
||||
return new ValidatorClient(
|
||||
validatorUrls,
|
||||
queryClient,
|
||||
contractAddress,
|
||||
prefix
|
||||
);
|
||||
}
|
||||
console.log("Initial connection to", validatorUrls[0], "failed")
|
||||
console.log("Initial connection to", validatorUrls[0], "failed");
|
||||
}
|
||||
} else {
|
||||
const queryClient = await QueryClient.connect(validatorUrls[0])
|
||||
return new ValidatorClient(validatorUrls, queryClient, contractAddress, prefix)
|
||||
const queryClient = await QueryClient.connect(validatorUrls[0]);
|
||||
return new ValidatorClient(
|
||||
validatorUrls,
|
||||
queryClient,
|
||||
contractAddress,
|
||||
prefix
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error("None of the provided validators seem to be alive")
|
||||
throw new Error("None of the provided validators seem to be alive");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param urls the validator URLs in either array of string format.
|
||||
* @returns a shuffled array of validator URLs.
|
||||
*/
|
||||
private static ensureArray(urls: string | string[]): string[] {
|
||||
let validatorsUrls: string[] = []
|
||||
let validatorsUrls: string[] = [];
|
||||
if (typeof urls === "string") {
|
||||
validatorsUrls = [urls]
|
||||
validatorsUrls = [urls];
|
||||
} else {
|
||||
// if the array is empty, just blow up
|
||||
if (urls.length === 0) {
|
||||
throw new Error("no validator urls provided")
|
||||
throw new Error("no validator urls provided");
|
||||
}
|
||||
|
||||
// no point in shuffling array of size 1
|
||||
if (urls.length > 1) {
|
||||
urls = this.shuffleArray(urls)
|
||||
urls = this.shuffleArray(urls);
|
||||
}
|
||||
validatorsUrls = urls
|
||||
validatorsUrls = urls;
|
||||
}
|
||||
|
||||
return validatorsUrls
|
||||
return validatorsUrls;
|
||||
}
|
||||
|
||||
// an error adapter function that upon an error attempts to switch currently used validator to the next one available
|
||||
// note that it ALWAYS throws an error
|
||||
/**
|
||||
* Error adapter function that - upon an error - attempts to switch currently used validator to the next one available
|
||||
* note that it ALWAYS throws an error
|
||||
* @param error Error thrown by async/netw requests in other methods within this class.
|
||||
* @returns a thrown error, as normal.
|
||||
*/
|
||||
async handleRequestFailure(error: Error): Promise<never> {
|
||||
// don't bother doing any fancy validator switches if we only have 1 validator to choose from
|
||||
if (this.urls.length > 1) {
|
||||
@@ -149,27 +216,36 @@ export default class ValidatorClient {
|
||||
// if we exhausted all of available validators, permute the set, maybe the old ones
|
||||
// are working again next time we try
|
||||
if (this.failedRequests === this.urls.length) {
|
||||
this.urls = ValidatorClient.shuffleArray(this.urls)
|
||||
this.urls = ValidatorClient.shuffleArray(this.urls);
|
||||
} else {
|
||||
// otherwise change the front validator to a 'fresh' one
|
||||
// during construction we assured we don't have an empty array
|
||||
ValidatorClient.moveArrayHeadToBack(this.urls)
|
||||
ValidatorClient.moveArrayHeadToBack(this.urls);
|
||||
}
|
||||
// and change validator to the front one and rethrow the error
|
||||
return await this.changeValidator(this.urls[0]).then(() => {
|
||||
throw error
|
||||
})
|
||||
throw error;
|
||||
});
|
||||
} else {
|
||||
// rethrow the error
|
||||
throw error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* changes the client's validator to the new validator passed in.
|
||||
* @param newUrl the URL of the new/alternative validator.
|
||||
* @returns void
|
||||
*/
|
||||
private async changeValidator(newUrl: string): Promise<void> {
|
||||
console.log("Changing validator to", newUrl)
|
||||
return await this.client.changeValidator(newUrl)
|
||||
console.log("Changing validator to", newUrl);
|
||||
return await this.client.changeValidator(newUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* shuffles the array
|
||||
* @param arr the URL of the new/alternative validator.
|
||||
* @returns array
|
||||
*/
|
||||
// adapted from https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array/6274381#6274381
|
||||
static shuffleArray<T>(arr: T[]): T[] {
|
||||
for (let i = arr.length - 1; i > 0; i--) {
|
||||
@@ -179,17 +255,23 @@ export default class ValidatorClient {
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* utility function that moves the first element (Val url) to the back
|
||||
* of the array again.
|
||||
* @param arr
|
||||
* @returns void
|
||||
*/
|
||||
// It is responsibility of the caller to ensure the input array is non-empty
|
||||
private static moveArrayHeadToBack<T>(arr: T[]) {
|
||||
const head = <T>arr.shift()
|
||||
arr.push(head)
|
||||
const head = <T>arr.shift();
|
||||
arr.push(head);
|
||||
}
|
||||
|
||||
public get address(): string {
|
||||
if (this.client instanceof NetClient) {
|
||||
return this.client.clientAddress
|
||||
return this.client.clientAddress;
|
||||
} else {
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,29 +303,55 @@ export default class ValidatorClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* effectively decodes a mnemonic phrase into a public punk address.
|
||||
* @param mnemonic A mnemonic from which to generate a public/private keypair.
|
||||
* @returns the address for this client wallet
|
||||
*/
|
||||
static async mnemonicToAddress(mnemonic: string, prefix: string): Promise<string> {
|
||||
static async mnemonicToAddress(
|
||||
mnemonic: string,
|
||||
prefix: string
|
||||
): Promise<string> {
|
||||
const wallet = await ValidatorClient.buildWallet(mnemonic, prefix);
|
||||
const [{address}] = await wallet.getAccounts()
|
||||
return address
|
||||
const [{ address }] = await wallet.getAccounts();
|
||||
return address;
|
||||
}
|
||||
|
||||
static async buildWallet(mnemonic: string, prefix: string): Promise<DirectSecp256k1HdWallet> {
|
||||
/**
|
||||
* async func to build/create a NYM wallet.
|
||||
* @param mnemonic
|
||||
* @param prefix
|
||||
* @returns Promise<DirectSecp256k1HdWallet>
|
||||
*/
|
||||
static async buildWallet(
|
||||
mnemonic: string,
|
||||
prefix: string
|
||||
): Promise<DirectSecp256k1HdWallet> {
|
||||
const signerOptions = { prefix: prefix };
|
||||
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, signerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* this method returns a promise which returns the amount/denom of a given user
|
||||
* @param address the user's public contract address eg. `punk23o85698370891702470413487`
|
||||
* @returns user's instance of the Validator Client.
|
||||
*/
|
||||
getBalance(address: string): Promise<Coin | null> {
|
||||
return this.client.getBalance(address, this.denom).catch((err) => this.handleRequestFailure(err));
|
||||
return this.client
|
||||
.getBalance(address, this.denom)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Func calls the client (instance of INetClient / NetClient) method `getStateParams(addr)`
|
||||
* Used in minimumMixnodeBond() and minimumGatewayBond()
|
||||
* @returns Promisified State Params.
|
||||
*/
|
||||
async getStateParams(): Promise<StateParams> {
|
||||
return this.client.getStateParams(this.contractAddress).catch((err) => this.handleRequestFailure(err))
|
||||
return this.client
|
||||
.getStateParams(this.contractAddress)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get or refresh the list of mixnodes in the network.
|
||||
*
|
||||
@@ -252,8 +360,10 @@ export default class ValidatorClient {
|
||||
* TODO: We will want to put this puppy on a timer, but for the moment we can
|
||||
* just get things strung together and refresh it manually.
|
||||
*/
|
||||
refreshMixNodes(): Promise<MixNodeBond[]> {
|
||||
return this.mixNodesCache.refreshMixNodes(this.contractAddress).catch((err) => this.handleRequestFailure(err));
|
||||
async refreshMixNodes(arg: string): Promise<MixNodeBond[]> {
|
||||
return this.mixNodesCache
|
||||
.refreshMixNodes(arg)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,8 +374,10 @@ export default class ValidatorClient {
|
||||
* TODO: We will want to put this puppy on a timer, but for the moment we can
|
||||
* just get things strung together and refresh it manually.
|
||||
*/
|
||||
refreshValidatorAPIMixNodes(): Promise<MixNodeBond[]> {
|
||||
return this.mixNodesCache.refreshValidatorAPIMixNodes(this.urls).catch((err) => this.handleRequestFailure(err));
|
||||
async refreshValidatorAPIMixNodes(): Promise<MixNodeBond[]> {
|
||||
return this.mixNodesCache
|
||||
.refreshValidatorAPIMixNodes(this.urls)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,7 +386,7 @@ export default class ValidatorClient {
|
||||
* @returns an array containing all `MixNodeBond`s in the client's local cache.
|
||||
*/
|
||||
getMixNodes(): MixNodeBond[] {
|
||||
return this.mixNodesCache.mixNodes
|
||||
return this.mixNodesCache.mixNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,9 +395,9 @@ export default class ValidatorClient {
|
||||
* @returns a `Coin` instance containing minimum amount of coins to stake a gateway.
|
||||
*/
|
||||
async minimumMixnodeBond(): Promise<Coin> {
|
||||
const stateParams = await this.getStateParams()
|
||||
const stateParams = await this.getStateParams();
|
||||
// we trust the contract to return a valid number
|
||||
return coin(Number(stateParams.minimum_mixnode_bond), this.prefix)
|
||||
return coin(Number(stateParams.minimum_mixnode_bond), this.prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,13 +405,22 @@ export default class ValidatorClient {
|
||||
*/
|
||||
async bondMixnode(mixNode: MixNode, bond: Coin): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, {bond_mixnode: {mix_node: mixNode}}, "adding mixnode", [bond]).catch((err) => this.handleRequestFailure(err));
|
||||
console.log(`account ${this.client.clientAddress} added mixnode with ${mixNode.host}`);
|
||||
const result = await this.client
|
||||
.executeContract(
|
||||
this.client.clientAddress,
|
||||
this.contractAddress,
|
||||
{ bond_mixnode: { mix_node: mixNode } },
|
||||
"adding mixnode",
|
||||
[bond]
|
||||
)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
console.log(
|
||||
`account ${this.client.clientAddress} added mixnode with ${mixNode.host}`
|
||||
);
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to bond with a query client")
|
||||
throw new Error("Tried to bond with a query client");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,11 +428,15 @@ export default class ValidatorClient {
|
||||
*/
|
||||
async unbondMixnode(): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, {unbond_mixnode: {}}).catch((err) => this.handleRequestFailure(err))
|
||||
const result = await this.client
|
||||
.executeContract(this.client.clientAddress, this.contractAddress, {
|
||||
unbond_mixnode: {},
|
||||
})
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
console.log(`account ${this.client.clientAddress} unbonded mixnode`);
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to unbond with a query client")
|
||||
throw new Error("Tried to unbond with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,13 +447,27 @@ export default class ValidatorClient {
|
||||
* @param amount desired amount of coins to delegate to the node
|
||||
*/
|
||||
// requires coin type to ensure correct denomination (
|
||||
async delegateToMixnode(mixIdentity: string, amount: Coin): Promise<ExecuteResult> {
|
||||
async delegateToMixnode(
|
||||
mixIdentity: string,
|
||||
amount: Coin
|
||||
): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, {delegate_to_mixnode: {mix_identity: mixIdentity}}, `delegating to ${mixIdentity}`, [amount]).catch((err) => this.handleRequestFailure(err))
|
||||
console.log(`account ${this.client.clientAddress} delegated ${amount} to mixnode ${mixIdentity}`);
|
||||
console.log("args mixID ", mixIdentity, " amount ", amount);
|
||||
const result = await this.client
|
||||
.executeContract(
|
||||
this.client.clientAddress,
|
||||
this.contractAddress,
|
||||
{ delegate_to_mixnode: { mix_identity: mixIdentity } },
|
||||
`delegating to ${mixIdentity}`,
|
||||
[amount]
|
||||
)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
console.log(
|
||||
`account ${this.client.clientAddress} delegated ${amount} to mixnode ${mixIdentity}`
|
||||
);
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to delegate stake with a query client")
|
||||
throw new Error("Tried to delegate stake with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,11 +478,17 @@ export default class ValidatorClient {
|
||||
*/
|
||||
async removeMixnodeDelegation(mixIdentity: string): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, {undelegate_from_mixnode: {mix_identity: mixIdentity}}).catch((err) => this.handleRequestFailure(err))
|
||||
console.log(`account ${this.client.clientAddress} removed delegation from mixnode ${mixIdentity}`);
|
||||
const result = await this.client
|
||||
.executeContract(this.client.clientAddress, this.contractAddress, {
|
||||
undelegate_from_mixnode: { mix_identity: mixIdentity },
|
||||
})
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
console.log(
|
||||
`account ${this.client.clientAddress} removed delegation from mixnode ${mixIdentity}`
|
||||
);
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to remove stake delegation with a query client")
|
||||
throw new Error("Tried to remove stake delegation with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,13 +499,26 @@ export default class ValidatorClient {
|
||||
* @param amount desired amount of coins to delegate to the node
|
||||
*/
|
||||
// requires coin type to ensure correct denomination (
|
||||
async delegateToGateway(gatewayIdentity: string, amount: Coin): Promise<ExecuteResult> {
|
||||
async delegateToGateway(
|
||||
gatewayIdentity: string,
|
||||
amount: Coin
|
||||
): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, {delegate_to_gateway: {gateway_identity: gatewayIdentity}}, `delegating to ${gatewayIdentity}`, [amount]).catch((err) => this.handleRequestFailure(err))
|
||||
console.log(`account ${this.client.clientAddress} delegated ${amount} to gateway ${gatewayIdentity}`);
|
||||
const result = await this.client
|
||||
.executeContract(
|
||||
this.client.clientAddress,
|
||||
this.contractAddress,
|
||||
{ delegate_to_gateway: { gateway_identity: gatewayIdentity } },
|
||||
`delegating to ${gatewayIdentity}`,
|
||||
[amount]
|
||||
)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
console.log(
|
||||
`account ${this.client.clientAddress} delegated ${amount} to gateway ${gatewayIdentity}`
|
||||
);
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to delegate stake with a query client")
|
||||
throw new Error("Tried to delegate stake with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,13 +527,21 @@ export default class ValidatorClient {
|
||||
*
|
||||
* @param gatewayIdentity identity of the gateway from which the delegation should get removed
|
||||
*/
|
||||
async removeGatewayDelegation(gatewayIdentity: string): Promise<ExecuteResult> {
|
||||
async removeGatewayDelegation(
|
||||
gatewayIdentity: string
|
||||
): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, {undelegate_from_gateway: {gateway_identity: gatewayIdentity}}).catch((err) => this.handleRequestFailure(err))
|
||||
console.log(`account ${this.client.clientAddress} removed delegation from gateway ${gatewayIdentity}`);
|
||||
const result = await this.client
|
||||
.executeContract(this.client.clientAddress, this.contractAddress, {
|
||||
undelegate_from_gateway: { gateway_identity: gatewayIdentity },
|
||||
})
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
console.log(
|
||||
`account ${this.client.clientAddress} removed delegation from gateway ${gatewayIdentity}`
|
||||
);
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to remove stake delegation with a query client")
|
||||
throw new Error("Tried to remove stake delegation with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,10 +550,14 @@ export default class ValidatorClient {
|
||||
*/
|
||||
async ownsMixNode(): Promise<boolean> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.ownsMixNode(this.contractAddress, this.client.clientAddress).catch((err) => this.handleRequestFailure(err))
|
||||
return result.has_node
|
||||
const result = await this.client
|
||||
.ownsMixNode(this.contractAddress, this.client.clientAddress)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
return result.has_node;
|
||||
} else {
|
||||
throw new Error("tried to check mixnode ownership for an address-less client")
|
||||
throw new Error(
|
||||
"tried to check mixnode ownership for an address-less client"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,10 +566,14 @@ export default class ValidatorClient {
|
||||
*/
|
||||
async ownsGateway(): Promise<boolean> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.ownsGateway(this.contractAddress, this.client.clientAddress).catch((err) => this.handleRequestFailure(err))
|
||||
return result.has_gateway
|
||||
const result = await this.client
|
||||
.ownsGateway(this.contractAddress, this.client.clientAddress)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
return result.has_gateway;
|
||||
} else {
|
||||
throw new Error("tried to check gateway ownership for an address-less client")
|
||||
throw new Error(
|
||||
"tried to check gateway ownership for an address-less client"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,7 +585,9 @@ export default class ValidatorClient {
|
||||
* TODO: Similarly to mixnode bonds, this should probably be put on a timer somewhere.
|
||||
*/
|
||||
refreshGateways(): Promise<GatewayBond[]> {
|
||||
return this.gatewayCache.refreshGateways(this.contractAddress).catch((err) => this.handleRequestFailure(err));
|
||||
return this.gatewayCache
|
||||
.refreshGateways(this.contractAddress)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,7 +598,9 @@ export default class ValidatorClient {
|
||||
* TODO: Similarly to mixnode bonds, this should probably be put on a timer somewhere.
|
||||
*/
|
||||
refreshValidatorAPIGateways(): Promise<GatewayBond[]> {
|
||||
return this.gatewayCache.refreshValidatorAPIGateways(this.urls).catch((err) => this.handleRequestFailure(err));
|
||||
return this.gatewayCache
|
||||
.refreshValidatorAPIGateways(this.urls)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,7 +609,7 @@ export default class ValidatorClient {
|
||||
* @returns an array containing all `GatewayBond`s in the client's local cache.
|
||||
*/
|
||||
getGateways(): GatewayBond[] {
|
||||
return this.gatewayCache.gateways
|
||||
return this.gatewayCache.gateways;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,9 +618,9 @@ export default class ValidatorClient {
|
||||
* @returns a `Coin` instance containing minimum amount of coins to stake a gateway.
|
||||
*/
|
||||
async minimumGatewayBond(): Promise<Coin> {
|
||||
const stateParams = await this.getStateParams()
|
||||
const stateParams = await this.getStateParams();
|
||||
// we trust the contract to return a valid number
|
||||
return coin(Number(stateParams.minimum_gateway_bond), this.prefix)
|
||||
return coin(Number(stateParams.minimum_gateway_bond), this.prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,11 +628,21 @@ export default class ValidatorClient {
|
||||
*/
|
||||
async bondGateway(gateway: Gateway, bond: Coin): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, {bond_gateway: {gateway: gateway}}, "adding gateway", [bond]).catch((err) => this.handleRequestFailure(err));
|
||||
console.log(`account ${this.client.clientAddress} added gateway with ${gateway.host}`);
|
||||
const result = await this.client
|
||||
.executeContract(
|
||||
this.client.clientAddress,
|
||||
this.contractAddress,
|
||||
{ bond_gateway: { gateway: gateway } },
|
||||
"adding gateway",
|
||||
[bond]
|
||||
)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
console.log(
|
||||
`account ${this.client.clientAddress} added gateway with ${gateway.host}`
|
||||
);
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to bond gateway with a query client")
|
||||
throw new Error("Tried to bond gateway with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,19 +651,30 @@ export default class ValidatorClient {
|
||||
*/
|
||||
async unbondGateway(): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.executeContract(this.client.clientAddress, this.contractAddress, {unbond_gateway: {}}).catch((err) => this.handleRequestFailure(err))
|
||||
const result = await this.client
|
||||
.executeContract(this.client.clientAddress, this.contractAddress, {
|
||||
unbond_gateway: {},
|
||||
})
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
console.log(`account ${this.client.clientAddress} unbonded gateway`);
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to unbond gateway with a query client")
|
||||
throw new Error("Tried to unbond gateway with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
async updateStateParams(newParams: StateParams): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
return await this.client.executeContract(this.client.clientAddress, this.contractAddress, {update_state_params: newParams}, "updating contract state").catch((err) => this.handleRequestFailure(err));
|
||||
return await this.client
|
||||
.executeContract(
|
||||
this.client.clientAddress,
|
||||
this.contractAddress,
|
||||
{ update_state_params: newParams },
|
||||
"updating contract state"
|
||||
)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
} else {
|
||||
throw new Error("Tried to update state params with a query client")
|
||||
throw new Error("Tried to update state params with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,22 +685,27 @@ export default class ValidatorClient {
|
||||
*/
|
||||
public async getMixDelegations(mixIdentity: string): Promise<Delegation[]> {
|
||||
// make this configurable somewhere
|
||||
const limit = 500
|
||||
const limit = 500;
|
||||
|
||||
let delegations: Delegation[] = [];
|
||||
let response: PagedMixDelegationsResponse
|
||||
let response: PagedMixDelegationsResponse;
|
||||
let next: string | undefined = undefined;
|
||||
for (;;) {
|
||||
response = await this.client.getMixDelegations(this.contractAddress, mixIdentity, limit, next)
|
||||
delegations = delegations.concat(response.delegations)
|
||||
next = response.start_next_after
|
||||
response = await this.client.getMixDelegations(
|
||||
this.contractAddress,
|
||||
mixIdentity,
|
||||
limit,
|
||||
next
|
||||
);
|
||||
delegations = delegations.concat(response.delegations);
|
||||
next = response.start_next_after;
|
||||
// if `start_next_after` is not set, we're done
|
||||
if (!next) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return delegations
|
||||
return delegations;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -510,8 +714,15 @@ export default class ValidatorClient {
|
||||
* @param mixIdentity identity of the node to which the delegation was sent
|
||||
* @param delegatorAddress address of the client who delegated the stake
|
||||
*/
|
||||
public getMixDelegation(mixIdentity: string, delegatorAddress: string): Promise<Delegation> {
|
||||
return this.client.getMixDelegation(this.contractAddress, mixIdentity, delegatorAddress);
|
||||
public getMixDelegation(
|
||||
mixIdentity: string,
|
||||
delegatorAddress: string
|
||||
): Promise<Delegation> {
|
||||
return this.client.getMixDelegation(
|
||||
this.contractAddress,
|
||||
mixIdentity,
|
||||
delegatorAddress
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,24 +730,31 @@ export default class ValidatorClient {
|
||||
*
|
||||
* @param gatewayIdentity identity of the gateway to which the delegation was sent
|
||||
*/
|
||||
public async getGatewayDelegations(gatewayIdentity: string): Promise<Delegation[]> {
|
||||
public async getGatewayDelegations(
|
||||
gatewayIdentity: string
|
||||
): Promise<Delegation[]> {
|
||||
// make this configurable somewhere
|
||||
const limit = 500
|
||||
const limit = 500;
|
||||
|
||||
let delegations: Delegation[] = [];
|
||||
let response: PagedGatewayDelegationsResponse
|
||||
let response: PagedGatewayDelegationsResponse;
|
||||
let next: string | undefined = undefined;
|
||||
for (;;) {
|
||||
response = await this.client.getGatewayDelegations(this.contractAddress, gatewayIdentity, limit, next)
|
||||
delegations = delegations.concat(response.delegations)
|
||||
next = response.start_next_after
|
||||
response = await this.client.getGatewayDelegations(
|
||||
this.contractAddress,
|
||||
gatewayIdentity,
|
||||
limit,
|
||||
next
|
||||
);
|
||||
delegations = delegations.concat(response.delegations);
|
||||
next = response.start_next_after;
|
||||
// if `start_next_after` is not set, we're done
|
||||
if (!next) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return delegations
|
||||
return delegations;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -545,8 +763,15 @@ export default class ValidatorClient {
|
||||
* @param gatewayIdentity identity of the gateway to which the delegation was sent
|
||||
* @param delegatorAddress address of the client who delegated the stake
|
||||
*/
|
||||
public getGatewayDelegation(gatewayIdentity: string, delegatorAddress: string): Promise<Delegation> {
|
||||
return this.client.getGatewayDelegation(this.contractAddress, gatewayIdentity, delegatorAddress);
|
||||
public getGatewayDelegation(
|
||||
gatewayIdentity: string,
|
||||
delegatorAddress: string
|
||||
): Promise<Delegation> {
|
||||
return this.client.getGatewayDelegation(
|
||||
this.contractAddress,
|
||||
gatewayIdentity,
|
||||
delegatorAddress
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: if we just keep a reference to the SigningCosmWasmClient somewhere we can probably go direct
|
||||
@@ -555,13 +780,22 @@ export default class ValidatorClient {
|
||||
/**
|
||||
* Send funds from one address to another.
|
||||
*/
|
||||
async send(senderAddress: string, recipientAddress: string, coins: readonly Coin[], memo?: string): Promise<BroadcastTxSuccess> {
|
||||
async send(
|
||||
senderAddress: string,
|
||||
recipientAddress: string,
|
||||
coins: readonly Coin[],
|
||||
memo?: string
|
||||
): Promise<BroadcastTxSuccess> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.sendTokens(senderAddress, recipientAddress, coins, memo).catch((err) => this.handleRequestFailure(err));
|
||||
const result = await this.client
|
||||
.sendTokens(senderAddress, recipientAddress, coins, memo)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
if (isBroadcastTxFailure(result)) {
|
||||
throw new Error(`Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`)
|
||||
throw new Error(
|
||||
`Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`
|
||||
);
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to use send with a query client");
|
||||
}
|
||||
@@ -569,63 +803,127 @@ export default class ValidatorClient {
|
||||
|
||||
/**
|
||||
* Send funds multiple times from one address to another in a single block.
|
||||
* @param senderAddress
|
||||
* @param data
|
||||
* @param memo
|
||||
* @returns result
|
||||
*/
|
||||
async sendMultiple(senderAddress: string, data: SendRequest[], memo?: string): Promise<BroadcastTxSuccess> {
|
||||
async sendMultiple(
|
||||
senderAddress: string,
|
||||
data: SendRequest[],
|
||||
memo?: string
|
||||
): Promise<BroadcastTxSuccess> {
|
||||
if (this.client instanceof NetClient) {
|
||||
if (data.length === 1) {
|
||||
return this.send(data[0].senderAddress, data[0].recipientAddress, data[0].transferAmount, memo)
|
||||
return this.send(
|
||||
data[0].senderAddress,
|
||||
data[0].recipientAddress,
|
||||
data[0].transferAmount,
|
||||
memo
|
||||
);
|
||||
}
|
||||
|
||||
const encoded = data.map(req => makeBankMsgSend(req.senderAddress, req.recipientAddress, req.transferAmount));
|
||||
const encoded = data.map((req) =>
|
||||
makeBankMsgSend(
|
||||
req.senderAddress,
|
||||
req.recipientAddress,
|
||||
req.transferAmount
|
||||
)
|
||||
);
|
||||
|
||||
// the function to calculate fee for a single entry is not exposed...
|
||||
console.log(`this.denom is ${this.denom}`);
|
||||
const table = buildFeeTable(nymGasPrice(this.prefix), {sendMultiple: nymGasLimits.send * data.length}, {sendMultiple: nymGasLimits.send * data.length})
|
||||
const fee = table.sendMultiple
|
||||
const result = await this.client.signAndBroadcast(senderAddress, encoded, fee, memo)
|
||||
const table = buildFeeTable(
|
||||
nymGasPrice(this.prefix),
|
||||
{ sendMultiple: nymGasLimits.send * data.length },
|
||||
{ sendMultiple: nymGasLimits.send * data.length }
|
||||
);
|
||||
const fee = table.sendMultiple;
|
||||
const result = await this.client.signAndBroadcast(
|
||||
senderAddress,
|
||||
encoded,
|
||||
fee,
|
||||
memo
|
||||
);
|
||||
if (isBroadcastTxFailure(result)) {
|
||||
throw new Error(`Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`)
|
||||
throw new Error(
|
||||
`Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`
|
||||
);
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to use sendMultiple with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
public async executeCustom(signerAddress: string, messages: readonly EncodeObject[], customFee: StdFee, memo?: string): Promise<BroadcastTxSuccess> {
|
||||
public async executeCustom(
|
||||
signerAddress: string,
|
||||
messages: readonly EncodeObject[],
|
||||
customFee: StdFee,
|
||||
memo?: string
|
||||
): Promise<BroadcastTxSuccess> {
|
||||
if (this.client instanceof NetClient) {
|
||||
const result = await this.client.signAndBroadcast(signerAddress, messages, customFee, memo);
|
||||
const result = await this.client.signAndBroadcast(
|
||||
signerAddress,
|
||||
messages,
|
||||
customFee,
|
||||
memo
|
||||
);
|
||||
if (isBroadcastTxFailure(result)) {
|
||||
throw new Error(`Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`)
|
||||
throw new Error(
|
||||
`Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`
|
||||
);
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Tried to use executeCustom with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
async upload(senderAddress: string, wasmCode: Uint8Array, meta?: UploadMeta, memo?: string): Promise<UploadResult> {
|
||||
async upload(
|
||||
senderAddress: string,
|
||||
wasmCode: Uint8Array,
|
||||
meta?: UploadMeta,
|
||||
memo?: string
|
||||
): Promise<UploadResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
return this.client.upload(senderAddress, wasmCode, meta, memo).catch((err) => this.handleRequestFailure(err));
|
||||
return this.client
|
||||
.upload(senderAddress, wasmCode, meta, memo)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
} else {
|
||||
throw new Error("Tried to upload with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
public instantiate(senderAddress: string, codeId: number, initMsg: Record<string, unknown>, label: string, options?: InstantiateOptions): Promise<InstantiateResult> {
|
||||
public instantiate(
|
||||
senderAddress: string,
|
||||
codeId: number,
|
||||
initMsg: Record<string, unknown>,
|
||||
label: string,
|
||||
options?: InstantiateOptions
|
||||
): Promise<InstantiateResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
return this.client.instantiate(senderAddress, codeId, initMsg, label, options).catch((err) => this.handleRequestFailure(err));
|
||||
return this.client
|
||||
.instantiate(senderAddress, codeId, initMsg, label, options)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
} else {
|
||||
throw new Error("Tried to instantiate with a query client");
|
||||
}
|
||||
}
|
||||
|
||||
public migrate(senderAddress: string, contractAddress: string, codeId: number, migrateMsg: Record<string, unknown>, memo?: string): Promise<MigrateResult> {
|
||||
public migrate(
|
||||
senderAddress: string,
|
||||
contractAddress: string,
|
||||
codeId: number,
|
||||
migrateMsg: Record<string, unknown>,
|
||||
memo?: string
|
||||
): Promise<MigrateResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
return this.client.migrate(senderAddress, contractAddress, codeId, migrateMsg, memo).catch((err) => this.handleRequestFailure(err))
|
||||
return this.client
|
||||
.migrate(senderAddress, contractAddress, codeId, migrateMsg, memo)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
} else {
|
||||
throw new Error("Tried to migrate with a query client");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2517
-2259
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user