Compare commits

...

2 Commits

Author SHA1 Message Date
Aid Thompson b8494eb83a signpost - unfinished just parking changes 2021-11-22 15:08:46 +00:00
Aid Thompson cb549dfe25 1st commit 2021-11-17 11:55:06 +00:00
6 changed files with 9372 additions and 2937 deletions
+5600 -45
View File
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -7,6 +7,7 @@
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"run_cli": "clear && ts-node src/cli.ts",
"test": "ts-mocha tests/**/*.test.ts", "test": "ts-mocha tests/**/*.test.ts",
"coverage": "nyc npm test", "coverage": "nyc npm test",
"lint": "eslint \"**/*.ts\"", "lint": "eslint \"**/*.ts\"",
@@ -22,6 +23,7 @@
"devDependencies": { "devDependencies": {
"@types/chai": "^4.2.15", "@types/chai": "^4.2.15",
"@types/expect": "^24.3.0", "@types/expect": "^24.3.0",
"@types/inquirer": "^8.1.3",
"@types/mocha": "^8.2.1", "@types/mocha": "^8.2.1",
"@typescript-eslint/eslint-plugin": "^4.14.0", "@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0", "@typescript-eslint/parser": "^4.14.0",
@@ -35,10 +37,11 @@
"typescript": "^4.1.3" "typescript": "^4.1.3"
}, },
"dependencies": { "dependencies": {
"axios": "^0.21.1",
"@cosmjs/cosmwasm-stargate": "^0.25.5", "@cosmjs/cosmwasm-stargate": "^0.25.5",
"@cosmjs/stargate": "^0.25.5",
"@cosmjs/math": "^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"
} }
} }
+13 -9
View File
@@ -1,5 +1,5 @@
import { MixNodeBond, PagedMixnodeResponse } from "../types"; import { MixNodeBond, PagedMixnodeResponse } from "../types";
import { INetClient } from "../net-client" import { INetClient } from "../net-client";
import { IQueryClient } from "../query-client"; import { IQueryClient } from "../query-client";
import { VALIDATOR_API_MIXNODES, VALIDATOR_API_PORT } from "../index"; import { VALIDATOR_API_MIXNODES, VALIDATOR_API_PORT } from "../index";
import axios from "axios"; import axios from "axios";
@@ -13,9 +13,9 @@ export { MixnodesCache };
* available for querying. * available for querying.
* */ * */
export default class MixnodesCache { export default class MixnodesCache {
mixNodes: MixNodeBond[] mixNodes: MixNodeBond[];
client: INetClient | IQueryClient client: INetClient | IQueryClient;
perPage: number perPage: number;
constructor(client: INetClient | IQueryClient, perPage: number) { constructor(client: INetClient | IQueryClient, perPage: number) {
this.client = client; this.client = client;
@@ -31,16 +31,20 @@ export default class MixnodesCache {
let response: PagedMixnodeResponse; let response: PagedMixnodeResponse;
let next: string | undefined = undefined; let next: string | undefined = undefined;
for (;;) { for (;;) {
response = await this.client.getMixNodes(contractAddress, this.perPage, next); response = await this.client.getMixNodes(
newMixnodes = newMixnodes.concat(response.nodes) contractAddress,
this.perPage,
next
);
newMixnodes = newMixnodes.concat(response.nodes);
next = response.start_next_after; next = response.start_next_after;
// if `start_next_after` is not set, we're done // if `start_next_after` is not set, we're done
if (!next) { if (!next) {
break break;
} }
} }
this.mixNodes = newMixnodes this.mixNodes = newMixnodes;
return this.mixNodes; return this.mixNodes;
} }
@@ -55,6 +59,6 @@ export default class MixnodesCache {
return response.data; 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");
} }
} }
+317
View File
@@ -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
View File
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import NetClient, { INetClient } from "./net-client"; import NetClient, { INetClient } from "./net-client";
import { import {
StateParams, StateParams,
@@ -8,7 +9,7 @@ import {
MixNode, MixNode,
GatewayBond, GatewayBond,
Gateway, Gateway,
SendRequest SendRequest,
} from "./types"; } from "./types";
import { Bip39, Random } from "@cosmjs/crypto"; import { Bip39, Random } from "@cosmjs/crypto";
import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing"; import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing";
@@ -20,7 +21,7 @@ import {
InstantiateResult, InstantiateResult,
MigrateResult, MigrateResult,
UploadMeta, UploadMeta,
UploadResult UploadResult,
} from "@cosmjs/cosmwasm-stargate"; } from "@cosmjs/cosmwasm-stargate";
import { import {
CoinMap, CoinMap,
@@ -29,7 +30,7 @@ import {
nativeCoinToDisplay, nativeCoinToDisplay,
printableBalance, printableBalance,
printableCoin, printableCoin,
nativeToPrintable nativeToPrintable,
} from "./currency"; } from "./currency";
import GatewaysCache from "./caches/gateways"; import GatewaysCache from "./caches/gateways";
import QueryClient, { IQueryClient } from "./query-client"; import QueryClient, { IQueryClient } from "./query-client";
@@ -50,22 +51,26 @@ export {
printableBalance, printableBalance,
nativeToPrintable, nativeToPrintable,
MappedCoin, MappedCoin,
CoinMap CoinMap,
} };
export {nymGasLimits, nymGasPrice} export { nymGasLimits, nymGasPrice };
export default class ValidatorClient { export default class ValidatorClient {
private readonly client: INetClient | IQueryClient private readonly client: INetClient | IQueryClient;
private readonly contractAddress: string; private readonly contractAddress: string;
private readonly denom: string; private readonly denom: string;
private failedRequests: number = 0; private failedRequests = 0;
private gatewayCache: GatewaysCache private gatewayCache: GatewaysCache;
private mixNodesCache: MixnodesCache; private mixNodesCache: MixnodesCache;
private readonly prefix: string; private readonly prefix: string;
urls: string[]; urls: string[];
private constructor(
private constructor(urls: string[], client: INetClient | IQueryClient, contractAddress: string, prefix: string) { urls: string[],
client: INetClient | IQueryClient,
contractAddress: string,
prefix: string
) {
this.urls = urls; this.urls = urls;
this.client = client; this.client = client;
this.mixNodesCache = new MixnodesCache(client, 100); this.mixNodesCache = new MixnodesCache(client, 100);
@@ -75,73 +80,135 @@ export default class ValidatorClient {
this.denom = "u" + prefix; 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 // allows also entering 'string' by itself for backwards compatibility
static async connect(contractAddress: string, mnemonic: string, urls: string | string[], prefix: string): Promise<ValidatorClient> { static async connect(
const validatorUrls = this.ensureArray(urls) contractAddress: string,
mnemonic: string,
urls: string | string[],
prefix: string
): Promise<ValidatorClient> {
const validatorUrls = this.ensureArray(urls);
const wallet = await ValidatorClient.buildWallet(mnemonic, prefix); 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 we have more than a single validator, try to perform initial connection until we succeed or run out of options
if (validatorUrls.length > 1) { if (validatorUrls.length > 1) {
for (let i = 0; i < validatorUrls.length; i++) { for (let i = 0; i < validatorUrls.length; i++) {
console.log("Attempting initial connection to", validatorUrls[0]) console.log("Attempting initial connection to", validatorUrls[0]);
const netClient = await NetClient.connect(wallet, validatorUrls[0], prefix).catch((_) => ValidatorClient.moveArrayHeadToBack(validatorUrls)) const netClient = await NetClient.connect(
wallet,
validatorUrls[0],
prefix
).catch((_) => ValidatorClient.moveArrayHeadToBack(validatorUrls));
if (netClient !== undefined) { 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 { } else {
const netClient = await NetClient.connect(wallet, validatorUrls[0], prefix) const netClient = await NetClient.connect(
return new ValidatorClient(validatorUrls, netClient, contractAddress, prefix); 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 // allows also entering 'string' by itself for backwards compatibility
static async connectForQuery(contractAddress: string, urls: string | string[], prefix: string): Promise<ValidatorClient> { static async connectForQuery(
const validatorUrls = this.ensureArray(urls) 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 we have more than a single validator, try to perform initial connection until we succeed or run out of options
if (validatorUrls.length > 1) { if (validatorUrls.length > 1) {
for (let i = 0; i < validatorUrls.length; i++) { for (let i = 0; i < validatorUrls.length; i++) {
console.log("Attempting initial connection to", validatorUrls[0]) console.log("Attempting initial connection to", validatorUrls[0]);
const queryClient = await QueryClient.connect(validatorUrls[0]).catch((_) => ValidatorClient.moveArrayHeadToBack(validatorUrls)) const queryClient = await QueryClient.connect(validatorUrls[0]).catch(
(_) => ValidatorClient.moveArrayHeadToBack(validatorUrls)
);
if (queryClient !== undefined) { 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 { } else {
const queryClient = await QueryClient.connect(validatorUrls[0]) const queryClient = await QueryClient.connect(validatorUrls[0]);
return new ValidatorClient(validatorUrls, queryClient, contractAddress, prefix) 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[] { private static ensureArray(urls: string | string[]): string[] {
let validatorsUrls: string[] = [] let validatorsUrls: string[] = [];
if (typeof urls === "string") { if (typeof urls === "string") {
validatorsUrls = [urls] validatorsUrls = [urls];
} else { } else {
// if the array is empty, just blow up // if the array is empty, just blow up
if (urls.length === 0) { 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 // no point in shuffling array of size 1
if (urls.length > 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> { async handleRequestFailure(error: Error): Promise<never> {
// don't bother doing any fancy validator switches if we only have 1 validator to choose from // don't bother doing any fancy validator switches if we only have 1 validator to choose from
if (this.urls.length > 1) { 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 // if we exhausted all of available validators, permute the set, maybe the old ones
// are working again next time we try // are working again next time we try
if (this.failedRequests === this.urls.length) { if (this.failedRequests === this.urls.length) {
this.urls = ValidatorClient.shuffleArray(this.urls) this.urls = ValidatorClient.shuffleArray(this.urls);
} else { } else {
// otherwise change the front validator to a 'fresh' one // otherwise change the front validator to a 'fresh' one
// during construction we assured we don't have an empty array // 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 // and change validator to the front one and rethrow the error
return await this.changeValidator(this.urls[0]).then(() => { return await this.changeValidator(this.urls[0]).then(() => {
throw error throw error;
}) });
} else { } else {
// rethrow the error // 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> { private async changeValidator(newUrl: string): Promise<void> {
console.log("Changing validator to", newUrl) console.log("Changing validator to", newUrl);
return await this.client.changeValidator(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 // adapted from https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array/6274381#6274381
static shuffleArray<T>(arr: T[]): T[] { static shuffleArray<T>(arr: T[]): T[] {
for (let i = arr.length - 1; i > 0; i--) { for (let i = arr.length - 1; i > 0; i--) {
@@ -179,17 +255,23 @@ export default class ValidatorClient {
return arr; 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 // It is responsibility of the caller to ensure the input array is non-empty
private static moveArrayHeadToBack<T>(arr: T[]) { private static moveArrayHeadToBack<T>(arr: T[]) {
const head = <T>arr.shift() const head = <T>arr.shift();
arr.push(head) arr.push(head);
} }
public get address(): string { public get address(): string {
if (this.client instanceof NetClient) { if (this.client instanceof NetClient) {
return this.client.clientAddress return this.client.clientAddress;
} else { } 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. * @param mnemonic A mnemonic from which to generate a public/private keypair.
* @returns the address for this client wallet * @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 wallet = await ValidatorClient.buildWallet(mnemonic, prefix);
const [{address}] = await wallet.getAccounts() const [{ address }] = await wallet.getAccounts();
return address 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 }; const signerOptions = { prefix: prefix };
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, signerOptions); 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> { 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> { 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. * 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 * 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. * just get things strung together and refresh it manually.
*/ */
refreshMixNodes(): Promise<MixNodeBond[]> { async refreshMixNodes(arg: string): Promise<MixNodeBond[]> {
return this.mixNodesCache.refreshMixNodes(this.contractAddress).catch((err) => this.handleRequestFailure(err)); 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 * 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. * just get things strung together and refresh it manually.
*/ */
refreshValidatorAPIMixNodes(): Promise<MixNodeBond[]> { async refreshValidatorAPIMixNodes(): Promise<MixNodeBond[]> {
return this.mixNodesCache.refreshValidatorAPIMixNodes(this.urls).catch((err) => this.handleRequestFailure(err)); 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. * @returns an array containing all `MixNodeBond`s in the client's local cache.
*/ */
getMixNodes(): MixNodeBond[] { 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. * @returns a `Coin` instance containing minimum amount of coins to stake a gateway.
*/ */
async minimumMixnodeBond(): Promise<Coin> { async minimumMixnodeBond(): Promise<Coin> {
const stateParams = await this.getStateParams() const stateParams = await this.getStateParams();
// we trust the contract to return a valid number // 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> { async bondMixnode(mixNode: MixNode, bond: Coin): Promise<ExecuteResult> {
if (this.client instanceof NetClient) { 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)); const result = await this.client
console.log(`account ${this.client.clientAddress} added mixnode with ${mixNode.host}`); .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; return result;
} else { } 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> { async unbondMixnode(): Promise<ExecuteResult> {
if (this.client instanceof NetClient) { 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`); console.log(`account ${this.client.clientAddress} unbonded mixnode`);
return result; return result;
} else { } 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 * @param amount desired amount of coins to delegate to the node
*/ */
// requires coin type to ensure correct denomination ( // 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) { 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("args mixID ", mixIdentity, " amount ", amount);
console.log(`account ${this.client.clientAddress} delegated ${amount} to mixnode ${mixIdentity}`); 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; return result;
} else { } 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> { async removeMixnodeDelegation(mixIdentity: string): Promise<ExecuteResult> {
if (this.client instanceof NetClient) { 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)) const result = await this.client
console.log(`account ${this.client.clientAddress} removed delegation from mixnode ${mixIdentity}`); .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; return result;
} else { } 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 * @param amount desired amount of coins to delegate to the node
*/ */
// requires coin type to ensure correct denomination ( // 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) { 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)) const result = await this.client
console.log(`account ${this.client.clientAddress} delegated ${amount} to gateway ${gatewayIdentity}`); .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; return result;
} else { } 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 * @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) { 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)) const result = await this.client
console.log(`account ${this.client.clientAddress} removed delegation from gateway ${gatewayIdentity}`); .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; return result;
} else { } 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> { async ownsMixNode(): Promise<boolean> {
if (this.client instanceof NetClient) { if (this.client instanceof NetClient) {
const result = await this.client.ownsMixNode(this.contractAddress, this.client.clientAddress).catch((err) => this.handleRequestFailure(err)) const result = await this.client
return result.has_node .ownsMixNode(this.contractAddress, this.client.clientAddress)
.catch((err) => this.handleRequestFailure(err));
return result.has_node;
} else { } 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> { async ownsGateway(): Promise<boolean> {
if (this.client instanceof NetClient) { if (this.client instanceof NetClient) {
const result = await this.client.ownsGateway(this.contractAddress, this.client.clientAddress).catch((err) => this.handleRequestFailure(err)) const result = await this.client
return result.has_gateway .ownsGateway(this.contractAddress, this.client.clientAddress)
.catch((err) => this.handleRequestFailure(err));
return result.has_gateway;
} else { } 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. * TODO: Similarly to mixnode bonds, this should probably be put on a timer somewhere.
*/ */
refreshGateways(): Promise<GatewayBond[]> { 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. * TODO: Similarly to mixnode bonds, this should probably be put on a timer somewhere.
*/ */
refreshValidatorAPIGateways(): Promise<GatewayBond[]> { 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. * @returns an array containing all `GatewayBond`s in the client's local cache.
*/ */
getGateways(): GatewayBond[] { 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. * @returns a `Coin` instance containing minimum amount of coins to stake a gateway.
*/ */
async minimumGatewayBond(): Promise<Coin> { async minimumGatewayBond(): Promise<Coin> {
const stateParams = await this.getStateParams() const stateParams = await this.getStateParams();
// we trust the contract to return a valid number // 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> { async bondGateway(gateway: Gateway, bond: Coin): Promise<ExecuteResult> {
if (this.client instanceof NetClient) { 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)); const result = await this.client
console.log(`account ${this.client.clientAddress} added gateway with ${gateway.host}`); .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; return result;
} else { } 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> { async unbondGateway(): Promise<ExecuteResult> {
if (this.client instanceof NetClient) { 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`); console.log(`account ${this.client.clientAddress} unbonded gateway`);
return result; return result;
} else { } 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> { async updateStateParams(newParams: StateParams): Promise<ExecuteResult> {
if (this.client instanceof NetClient) { 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 { } 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[]> { public async getMixDelegations(mixIdentity: string): Promise<Delegation[]> {
// make this configurable somewhere // make this configurable somewhere
const limit = 500 const limit = 500;
let delegations: Delegation[] = []; let delegations: Delegation[] = [];
let response: PagedMixDelegationsResponse let response: PagedMixDelegationsResponse;
let next: string | undefined = undefined; let next: string | undefined = undefined;
for (;;) { for (;;) {
response = await this.client.getMixDelegations(this.contractAddress, mixIdentity, limit, next) response = await this.client.getMixDelegations(
delegations = delegations.concat(response.delegations) this.contractAddress,
next = response.start_next_after mixIdentity,
limit,
next
);
delegations = delegations.concat(response.delegations);
next = response.start_next_after;
// if `start_next_after` is not set, we're done // if `start_next_after` is not set, we're done
if (!next) { 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 mixIdentity identity of the node to which the delegation was sent
* @param delegatorAddress address of the client who delegated the stake * @param delegatorAddress address of the client who delegated the stake
*/ */
public getMixDelegation(mixIdentity: string, delegatorAddress: string): Promise<Delegation> { public getMixDelegation(
return this.client.getMixDelegation(this.contractAddress, mixIdentity, delegatorAddress); 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 * @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 // make this configurable somewhere
const limit = 500 const limit = 500;
let delegations: Delegation[] = []; let delegations: Delegation[] = [];
let response: PagedGatewayDelegationsResponse let response: PagedGatewayDelegationsResponse;
let next: string | undefined = undefined; let next: string | undefined = undefined;
for (;;) { for (;;) {
response = await this.client.getGatewayDelegations(this.contractAddress, gatewayIdentity, limit, next) response = await this.client.getGatewayDelegations(
delegations = delegations.concat(response.delegations) this.contractAddress,
next = response.start_next_after gatewayIdentity,
limit,
next
);
delegations = delegations.concat(response.delegations);
next = response.start_next_after;
// if `start_next_after` is not set, we're done // if `start_next_after` is not set, we're done
if (!next) { 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 gatewayIdentity identity of the gateway to which the delegation was sent
* @param delegatorAddress address of the client who delegated the stake * @param delegatorAddress address of the client who delegated the stake
*/ */
public getGatewayDelegation(gatewayIdentity: string, delegatorAddress: string): Promise<Delegation> { public getGatewayDelegation(
return this.client.getGatewayDelegation(this.contractAddress, gatewayIdentity, delegatorAddress); 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 // 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. * 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) { 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)) { 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 { } else {
throw new Error("Tried to use send with a query client"); 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. * 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 (this.client instanceof NetClient) {
if (data.length === 1) { 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... // the function to calculate fee for a single entry is not exposed...
console.log(`this.denom is ${this.denom}`); console.log(`this.denom is ${this.denom}`);
const table = buildFeeTable(nymGasPrice(this.prefix), {sendMultiple: nymGasLimits.send * data.length}, {sendMultiple: nymGasLimits.send * data.length}) const table = buildFeeTable(
const fee = table.sendMultiple nymGasPrice(this.prefix),
const result = await this.client.signAndBroadcast(senderAddress, encoded, fee, memo) { 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)) { 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 { } else {
throw new Error("Tried to use sendMultiple with a query client"); 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) { 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)) { 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 { } else {
throw new Error("Tried to use executeCustom with a query client"); 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) { 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 { } else {
throw new Error("Tried to upload with a query client"); 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) { 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 { } else {
throw new Error("Tried to instantiate with a query client"); 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) { 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 { } else {
throw new Error("Tried to migrate with a query client"); throw new Error("Tried to migrate with a query client");
} }
} }
} }
+2517 -2259
View File
File diff suppressed because it is too large Load Diff