Feature/validator client upgrade (#675)
* Upgrading cosmos client to v0.25.5 * Formatting * Renamed stakeDenom to prefix * Added a denom field to the client * New version needs a denom in the static methods * ibid * Formatting * Renaming bech32prefix to prefix * Adding a weird 'u' in the nymGasPrice * Changing to use denom instead of prefix in nymGasPrice * Alphabetizing fields * Renaming method to something a bit more sane * Using prefix, not denom, to build gas price * Hardcoding an unavoidable and ugly manual conversion into the stargate helper * denom is correct, for use in getBalance
This commit is contained in:
@@ -33,52 +33,52 @@ export { displayAmountToNative, nativeCoinToDisplay, printableCoin, printableBal
|
||||
export { nymGasLimits, nymGasPrice }
|
||||
|
||||
export default class ValidatorClient {
|
||||
private readonly stakeDenom: string;
|
||||
|
||||
urls: string[];
|
||||
private readonly client: INetClient | IQueryClient
|
||||
private mixNodesCache: MixnodesCache;
|
||||
private gatewayCache: GatewaysCache
|
||||
private readonly contractAddress: string;
|
||||
// for some reason typescript thinks it's better to not be explicit about a trivial type...
|
||||
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
|
||||
private readonly denom: string;
|
||||
private failedRequests: number = 0;
|
||||
private gatewayCache: GatewaysCache
|
||||
private mixNodesCache: MixnodesCache;
|
||||
private readonly prefix: string;
|
||||
urls: string[];
|
||||
|
||||
private constructor(urls: string[], client: INetClient | IQueryClient, contractAddress: string, stakeDenom: string) {
|
||||
|
||||
private constructor(urls: string[], client: INetClient | IQueryClient, contractAddress: string, prefix: string) {
|
||||
this.urls = urls;
|
||||
this.client = client;
|
||||
this.mixNodesCache = new MixnodesCache(client, 100);
|
||||
this.gatewayCache = new GatewaysCache(client, 100);
|
||||
this.contractAddress = contractAddress;
|
||||
this.stakeDenom = stakeDenom;
|
||||
this.prefix = prefix;
|
||||
this.denom = "u" + prefix;
|
||||
}
|
||||
|
||||
// allows also entering 'string' by itself for backwards compatibility
|
||||
static async connect(contractAddress: string, mnemonic: string, urls: string | string[], stakeDenom: string): Promise<ValidatorClient> {
|
||||
const validatorUrls = this.dealWithValidatorUrls(urls)
|
||||
const wallet = await ValidatorClient.buildWallet(mnemonic);
|
||||
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], stakeDenom).catch((_) => ValidatorClient.moveArrayHeadToBack(validatorUrls))
|
||||
const netClient = await NetClient.connect(wallet, validatorUrls[0], prefix).catch((_) => ValidatorClient.moveArrayHeadToBack(validatorUrls))
|
||||
if (netClient !== undefined) {
|
||||
return new ValidatorClient(validatorUrls, netClient, contractAddress, stakeDenom);
|
||||
return new ValidatorClient(validatorUrls, netClient, contractAddress, prefix);
|
||||
}
|
||||
console.log("Initial connection to", validatorUrls[0], "failed")
|
||||
}
|
||||
} else {
|
||||
const netClient = await NetClient.connect(wallet, validatorUrls[0], stakeDenom)
|
||||
return new ValidatorClient(validatorUrls, netClient, contractAddress, stakeDenom);
|
||||
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")
|
||||
}
|
||||
|
||||
// allows also entering 'string' by itself for backwards compatibility
|
||||
static async connectForQuery(contractAddress: string, urls: string | string[], stakeDenom: string): Promise<ValidatorClient> {
|
||||
const validatorUrls = this.dealWithValidatorUrls(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) {
|
||||
@@ -86,19 +86,19 @@ export default class ValidatorClient {
|
||||
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, stakeDenom)
|
||||
return new ValidatorClient(validatorUrls, queryClient, contractAddress, prefix)
|
||||
}
|
||||
console.log("Initial connection to", validatorUrls[0], "failed")
|
||||
}
|
||||
} else {
|
||||
const queryClient = await QueryClient.connect(validatorUrls[0])
|
||||
return new ValidatorClient(validatorUrls, queryClient, contractAddress, stakeDenom)
|
||||
return new ValidatorClient(validatorUrls, queryClient, contractAddress, prefix)
|
||||
}
|
||||
|
||||
throw new Error("None of the provided validators seem to be alive")
|
||||
}
|
||||
|
||||
private static dealWithValidatorUrls(urls: string | string[]): string[] {
|
||||
private static ensureArray(urls: string | string[]): string[] {
|
||||
let validatorsUrls: string[] = []
|
||||
if (typeof urls === "string") {
|
||||
validatorsUrls = [urls]
|
||||
@@ -134,7 +134,7 @@ export default class ValidatorClient {
|
||||
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})
|
||||
return await this.changeValidator(this.urls[0]).then(() => { throw error })
|
||||
} else {
|
||||
// rethrow the error
|
||||
throw error
|
||||
@@ -157,7 +157,7 @@ export default class ValidatorClient {
|
||||
|
||||
// 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()
|
||||
const head = <T>arr.shift()
|
||||
arr.push(head)
|
||||
}
|
||||
|
||||
@@ -200,18 +200,19 @@ export default class ValidatorClient {
|
||||
* @param mnemonic A mnemonic from which to generate a public/private keypair.
|
||||
* @returns the address for this client wallet
|
||||
*/
|
||||
static async mnemonicToAddress(mnemonic: string): Promise<string> {
|
||||
const wallet = await ValidatorClient.buildWallet(mnemonic);
|
||||
const [{address}] = await wallet.getAccounts()
|
||||
static async mnemonicToAddress(mnemonic: string, prefix: string): Promise<string> {
|
||||
const wallet = await ValidatorClient.buildWallet(mnemonic, prefix);
|
||||
const [{ address }] = await wallet.getAccounts()
|
||||
return address
|
||||
}
|
||||
|
||||
static async buildWallet(mnemonic: string): Promise<DirectSecp256k1HdWallet> {
|
||||
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, undefined, "hal");
|
||||
static async buildWallet(mnemonic: string, prefix: string): Promise<DirectSecp256k1HdWallet> {
|
||||
const signerOptions = { prefix: prefix };
|
||||
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, signerOptions);
|
||||
}
|
||||
|
||||
getBalance(address: string): Promise<Coin | null> {
|
||||
return this.client.getBalance(address, this.stakeDenom).catch((err) => this.handleRequestFailure(err));
|
||||
return this.client.getBalance(address, this.denom).catch((err) => this.handleRequestFailure(err));
|
||||
}
|
||||
|
||||
async getStateParams(): Promise<StateParams> {
|
||||
@@ -248,7 +249,7 @@ export default class ValidatorClient {
|
||||
async minimumMixnodeBond(): Promise<Coin> {
|
||||
const stateParams = await this.getStateParams()
|
||||
// we trust the contract to return a valid number
|
||||
return coin(Number(stateParams.minimum_mixnode_bond), this.stakeDenom)
|
||||
return coin(Number(stateParams.minimum_mixnode_bond), this.prefix)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -394,7 +395,7 @@ export default class ValidatorClient {
|
||||
async minimumGatewayBond(): Promise<Coin> {
|
||||
const stateParams = await this.getStateParams()
|
||||
// we trust the contract to return a valid number
|
||||
return coin(Number(stateParams.minimum_gateway_bond), this.stakeDenom)
|
||||
return coin(Number(stateParams.minimum_gateway_bond), this.prefix)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -402,7 +403,7 @@ 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));
|
||||
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 {
|
||||
@@ -415,7 +416,7 @@ 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 {
|
||||
@@ -425,7 +426,7 @@ export default class ValidatorClient {
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -443,7 +444,7 @@ export default class ValidatorClient {
|
||||
let delegations: Delegation[] = [];
|
||||
let response: PagedMixDelegationsResponse
|
||||
let next: string | undefined = undefined;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
response = await this.client.getMixDelegations(this.contractAddress, mixIdentity, limit, next)
|
||||
delegations = delegations.concat(response.delegations)
|
||||
next = response.start_next_after
|
||||
@@ -478,7 +479,7 @@ export default class ValidatorClient {
|
||||
let delegations: Delegation[] = [];
|
||||
let response: PagedGatewayDelegationsResponse
|
||||
let next: string | undefined = undefined;
|
||||
for (;;) {
|
||||
for (; ;) {
|
||||
response = await this.client.getGatewayDelegations(this.contractAddress, gatewayIdentity, limit, next)
|
||||
delegations = delegations.concat(response.delegations)
|
||||
next = response.start_next_after
|
||||
@@ -531,7 +532,8 @@ export default class ValidatorClient {
|
||||
const encoded = data.map(req => makeBankMsgSend(req.senderAddress, req.recipientAddress, req.transferAmount));
|
||||
|
||||
// the function to calculate fee for a single entry is not exposed...
|
||||
const table = buildFeeTable(nymGasPrice(this.stakeDenom), {sendMultiple: nymGasLimits.send * data.length}, {sendMultiple: nymGasLimits.send * data.length})
|
||||
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)
|
||||
if (isBroadcastTxFailure(result)) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import { ExecuteResult, InstantiateOptions, InstantiateResult, MigrateResult, Up
|
||||
export interface INetClient {
|
||||
clientAddress: string;
|
||||
|
||||
getBalance(address: string, stakeDenom: string): Promise<Coin | null>;
|
||||
getBalance(address: string, denom: string): Promise<Coin | null>;
|
||||
getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedMixnodeResponse>;
|
||||
getGateways(contractAddress: string, limit: number, start_after?: string): Promise<PagedGatewayResponse>;
|
||||
getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse>
|
||||
@@ -57,10 +57,10 @@ export default class NetClient implements INetClient {
|
||||
this.signerOptions = signerOptions;
|
||||
}
|
||||
|
||||
public static async connect(wallet: DirectSecp256k1HdWallet, url: string, stakeDenom: string): Promise<INetClient> {
|
||||
public static async connect(wallet: DirectSecp256k1HdWallet, url: string, prefix: string): Promise<INetClient> {
|
||||
const [{ address }] = await wallet.getAccounts();
|
||||
const signerOptions: SigningCosmWasmClientOptions = {
|
||||
gasPrice: nymGasPrice(stakeDenom),
|
||||
gasPrice: nymGasPrice(prefix),
|
||||
gasLimits: nymGasLimits,
|
||||
};
|
||||
const client = await SigningCosmWasmClient.connectWithSigner(url, wallet, signerOptions);
|
||||
@@ -91,7 +91,7 @@ export default class NetClient implements INetClient {
|
||||
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegations: { mix_identity: mixIdentity, limit } });
|
||||
} else {
|
||||
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegations: { mix_identity: mixIdentity, limit, start_after } });
|
||||
return this.cosmClient.queryContractSmart(contractAddress, { get_mix_delegations: { mix_identity: mixIdentity, limit, start_after } });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ export default class NetClient implements INetClient {
|
||||
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegations: { gateway_identity: gatewayIdentity, limit } });
|
||||
} else {
|
||||
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegations: { gateway_identity: gatewayIdentity, limit, start_after } });
|
||||
return this.cosmClient.queryContractSmart(contractAddress, { get_gateway_delegations: { gateway_identity: gatewayIdentity, limit, start_after } });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,12 +119,12 @@ export default class NetClient implements INetClient {
|
||||
return this.cosmClient.queryContractSmart(contractAddress, { owns_gateway: { address } });
|
||||
}
|
||||
|
||||
public getBalance(address: string, stakeDenom: string): Promise<Coin | null> {
|
||||
return this.cosmClient.getBalance(address, stakeDenom);
|
||||
public getBalance(address: string, denom: string): Promise<Coin | null> {
|
||||
return this.cosmClient.getBalance(address, denom);
|
||||
}
|
||||
|
||||
public getStateParams(contractAddress: string): Promise<StateParams> {
|
||||
return this.cosmClient.queryContractSmart(contractAddress, { state_params: { } });
|
||||
return this.cosmClient.queryContractSmart(contractAddress, { state_params: {} });
|
||||
}
|
||||
|
||||
public executeContract(senderAddress: string, contractAddress: string, handleMsg: Record<string, unknown>, memo?: string, transferAmount?: readonly Coin[]): Promise<ExecuteResult> {
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface Options {
|
||||
httpUrl: string;
|
||||
networkId: string;
|
||||
gasPrice: number;
|
||||
bech32prefix: string;
|
||||
prefix: string;
|
||||
}
|
||||
|
||||
export const nymGasLimits: GasLimits<CosmWasmFeeTable> = {
|
||||
@@ -19,13 +19,15 @@ export const nymGasLimits: GasLimits<CosmWasmFeeTable> = {
|
||||
changeAdmin: 80_000,
|
||||
};
|
||||
|
||||
export const nymGasPrice: (stakeDenom: string) => GasPrice = (stakeDenom: string) => GasPrice.fromString(`0.025${stakeDenom}`);
|
||||
export function nymGasPrice(prefix: string): GasPrice {
|
||||
return GasPrice.fromString(`0.025u${prefix}`); // TODO: ideally this ugly conversion shouldn't be hardcoded here.
|
||||
};
|
||||
|
||||
export const defaultOptions: Options = {
|
||||
httpUrl: "http://localhost:26657",
|
||||
networkId: "nymnet",
|
||||
gasPrice: 0.025,
|
||||
bech32prefix: "hal",
|
||||
prefix: "hal",
|
||||
};
|
||||
|
||||
export const downloadWasm = async (url: string): Promise<Uint8Array> => {
|
||||
|
||||
Reference in New Issue
Block a user