Replace RestClient with LcdClient

This commit is contained in:
Simon Warta 2020-07-07 14:49:01 +02:00
parent a317dac01e
commit dc661dfe5c
5 changed files with 80 additions and 52 deletions

View File

@ -1,10 +1,18 @@
/* eslint-disable @typescript-eslint/camelcase */
import { Coin, coins, CosmosSdkTx, isMsgSend, makeSignBytes, MsgSend, Secp256k1Pen } from "@cosmjs/sdk38";
import {
Coin,
coins,
CosmosSdkTx,
isMsgSend,
LcdClient,
makeSignBytes,
MsgSend,
Secp256k1Pen,
} from "@cosmjs/sdk38";
import { assert, sleep } from "@cosmjs/utils";
import { CosmWasmClient, isPostTxFailure } from "./cosmwasmclient";
import { isMsgExecuteContract, isMsgInstantiateContract } from "./msgs";
import { RestClient } from "./restclient";
import { SigningCosmWasmClient } from "./signingcosmwasmclient";
import {
alice,
@ -50,7 +58,7 @@ describe("CosmWasmClient.searchTx", () => {
const transferAmount = coins(1234567, "ucosm");
const result = await client.sendTokens(recipient, transferAmount);
await sleep(75); // wait until tx is indexed
const txDetails = await new RestClient(wasmd.endpoint).txById(result.transactionHash);
const txDetails = await new LcdClient(wasmd.endpoint).txById(result.transactionHash);
sendSuccessful = {
sender: alice.address0,
recipient: recipient,
@ -68,7 +76,7 @@ describe("CosmWasmClient.searchTx", () => {
};
const result = await client.sendTokens(recipient, [transferAmount]);
await sleep(75); // wait until tx is indexed
const txDetails = await new RestClient(wasmd.endpoint).txById(result.transactionHash);
const txDetails = await new LcdClient(wasmd.endpoint).txById(result.transactionHash);
sendSelfSuccessful = {
sender: alice.address0,
recipient: recipient,
@ -132,7 +140,7 @@ describe("CosmWasmClient.searchTx", () => {
};
const result = await client.execute(hashInstance, msg);
await sleep(75); // wait until tx is indexed
const txDetails = await new RestClient(wasmd.endpoint).txById(result.transactionHash);
const txDetails = await new LcdClient(wasmd.endpoint).txById(result.transactionHash);
execute = {
sender: alice.address0,
contract: hashInstance,

View File

@ -54,7 +54,7 @@ describe("CosmWasmClient", () => {
pendingWithoutWasmd();
const client = new CosmWasmClient(wasmd.endpoint);
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const getCodeSpy = spyOn(openedClient.restClient, "nodeInfo").and.callThrough();
const getCodeSpy = spyOn(openedClient.lcdClient, "nodeInfo").and.callThrough();
expect(await client.getChainId()).toEqual(wasmd.chainId); // from network
expect(await client.getChainId()).toEqual(wasmd.chainId); // from cache
@ -68,7 +68,7 @@ describe("CosmWasmClient", () => {
pendingWithoutWasmd();
const client = new CosmWasmClient(wasmd.endpoint);
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough();
const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough();
const height1 = await client.getHeight();
expect(height1).toBeGreaterThan(0);
@ -85,8 +85,8 @@ describe("CosmWasmClient", () => {
const client = new CosmWasmClient(wasmd.endpoint);
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough();
const authAccountsSpy = spyOn(openedClient.restClient, "authAccounts").and.callThrough();
const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough();
const authAccountsSpy = spyOn(openedClient.lcdClient.auth, "account").and.callThrough();
const height1 = await client.getHeight();
expect(height1).toBeGreaterThan(0);
@ -292,7 +292,7 @@ describe("CosmWasmClient", () => {
pendingWithoutWasmd();
const client = new CosmWasmClient(wasmd.endpoint);
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const getCodeSpy = spyOn(openedClient.restClient, "getCode").and.callThrough();
const getCodeSpy = spyOn(openedClient.lcdClient.wasm, "getCode").and.callThrough();
const result1 = await client.getCodeDetails(deployedErc20.codeId); // from network
const result2 = await client.getCodeDetails(deployedErc20.codeId); // from cache

View File

@ -2,17 +2,20 @@ import { Sha256 } from "@cosmjs/crypto";
import { fromBase64, fromHex, toHex } from "@cosmjs/encoding";
import { Uint53 } from "@cosmjs/math";
import {
AuthExtension,
BroadcastMode,
Coin,
CosmosSdkTx,
decodeBech32Pubkey,
IndexedTx,
LcdClient,
PubKey,
setupAuthExtension,
StdTx,
} from "@cosmjs/sdk38";
import { setupWasmExtension, WasmExtension } from "./lcdapi/wasm";
import { Log, parseLogs } from "./logs";
import { RestClient } from "./restclient";
import { JsonObject } from "./types";
export interface GetNonceResult {
@ -160,11 +163,11 @@ export interface Block {
/** Use for testing only */
export interface PrivateCosmWasmClient {
readonly restClient: RestClient;
readonly lcdClient: LcdClient & AuthExtension & WasmExtension;
}
export class CosmWasmClient {
protected readonly restClient: RestClient;
protected readonly lcdClient: LcdClient & AuthExtension & WasmExtension;
/** Any address the chain considers valid (valid bech32 with proper prefix) */
protected anyValidAddress: string | undefined;
@ -181,12 +184,16 @@ export class CosmWasmClient {
* @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns
*/
public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) {
this.restClient = new RestClient(apiUrl, broadcastMode);
this.lcdClient = LcdClient.withExtensions(
{ apiUrl: apiUrl, broadcastMode: broadcastMode },
setupAuthExtension,
setupWasmExtension,
);
}
public async getChainId(): Promise<string> {
if (!this.chainId) {
const response = await this.restClient.nodeInfo();
const response = await this.lcdClient.nodeInfo();
const chainId = response.node_info.network;
if (!chainId) throw new Error("Chain ID must not be empty");
this.chainId = chainId;
@ -197,12 +204,12 @@ export class CosmWasmClient {
public async getHeight(): Promise<number> {
if (this.anyValidAddress) {
const { height } = await this.restClient.authAccounts(this.anyValidAddress);
const { height } = await this.lcdClient.auth.account(this.anyValidAddress);
return parseInt(height, 10);
} else {
// Note: this gets inefficient when blocks contain a lot of transactions since it
// requires downloading and deserializing all transactions in the block.
const latest = await this.restClient.blocksLatest();
const latest = await this.lcdClient.blocksLatest();
return parseInt(latest.block.header.height, 10);
}
}
@ -212,7 +219,7 @@ export class CosmWasmClient {
*/
public async getIdentifier(tx: CosmosSdkTx): Promise<string> {
// We consult the REST API because we don't have a local amino encoder
const response = await this.restClient.encodeTx(tx);
const response = await this.lcdClient.encodeTx(tx);
const hash = new Sha256(fromBase64(response.tx)).digest();
return toHex(hash).toUpperCase();
}
@ -238,7 +245,7 @@ export class CosmWasmClient {
}
public async getAccount(address: string): Promise<Account | undefined> {
const account = await this.restClient.authAccounts(address);
const account = await this.lcdClient.auth.account(address);
const value = account.result.value;
if (value.address === "") {
return undefined;
@ -261,7 +268,7 @@ export class CosmWasmClient {
*/
public async getBlock(height?: number): Promise<Block> {
const response =
height !== undefined ? await this.restClient.blocks(height) : await this.restClient.blocksLatest();
height !== undefined ? await this.lcdClient.blocks(height) : await this.lcdClient.blocksLatest();
return {
id: response.block_id.hash,
@ -333,7 +340,7 @@ export class CosmWasmClient {
}
public async postTx(tx: StdTx): Promise<PostTxResult> {
const result = await this.restClient.postTx(tx);
const result = await this.lcdClient.postTx(tx);
if (!result.txhash.match(/^([0-9A-F][0-9A-F])+$/)) {
throw new Error("Received ill-formatted txhash. Must be non-empty upper-case hex");
}
@ -354,7 +361,7 @@ export class CosmWasmClient {
}
public async getCodes(): Promise<readonly Code[]> {
const result = await this.restClient.listCodeInfo();
const result = await this.lcdClient.wasm.listCodeInfo();
return result.map(
(entry): Code => {
this.anyValidAddress = entry.creator;
@ -373,7 +380,7 @@ export class CosmWasmClient {
const cached = this.codesCache.get(codeId);
if (cached) return cached;
const getCodeResult = await this.restClient.getCode(codeId);
const getCodeResult = await this.lcdClient.wasm.getCode(codeId);
const codeDetails: CodeDetails = {
id: getCodeResult.id,
creator: getCodeResult.creator,
@ -387,7 +394,7 @@ export class CosmWasmClient {
}
public async getContracts(codeId: number): Promise<readonly Contract[]> {
const result = await this.restClient.listContractsByCodeId(codeId);
const result = await this.lcdClient.wasm.listContractsByCodeId(codeId);
return result.map(
(entry): Contract => ({
address: entry.address,
@ -403,7 +410,7 @@ export class CosmWasmClient {
* Throws an error if no contract was found at the address
*/
public async getContract(address: string): Promise<ContractDetails> {
const result = await this.restClient.getContractInfo(address);
const result = await this.lcdClient.wasm.getContractInfo(address);
if (!result) throw new Error(`No contract found at address "${address}"`);
return {
address: result.address,
@ -425,7 +432,7 @@ export class CosmWasmClient {
// just test contract existence
const _info = await this.getContract(address);
return this.restClient.queryContractRaw(address, key);
return this.lcdClient.wasm.queryContractRaw(address, key);
}
/**
@ -437,7 +444,7 @@ export class CosmWasmClient {
*/
public async queryContractSmart(address: string, queryMsg: object): Promise<JsonObject> {
try {
return await this.restClient.queryContractSmart(address, queryMsg);
return await this.lcdClient.wasm.queryContractSmart(address, queryMsg);
} catch (error) {
if (error instanceof Error) {
if (error.message.startsWith("not found: contract")) {
@ -454,7 +461,7 @@ export class CosmWasmClient {
private async txsQuery(query: string): Promise<readonly IndexedTx[]> {
// TODO: we need proper pagination support
const limit = 100;
const result = await this.restClient.txsQuery(`${query}&limit=${limit}`);
const result = await this.lcdClient.txsQuery(`${query}&limit=${limit}`);
const pages = parseInt(result.page_total, 10);
if (pages > 1) {
throw new Error(

View File

@ -1,15 +1,19 @@
import { Sha256 } from "@cosmjs/crypto";
import { toHex } from "@cosmjs/encoding";
import { coin, coins, Secp256k1Pen } from "@cosmjs/sdk38";
import { AuthExtension, coin, coins, LcdClient, Secp256k1Pen, setupAuthExtension } from "@cosmjs/sdk38";
import { assert } from "@cosmjs/utils";
import { isPostTxFailure, PrivateCosmWasmClient } from "./cosmwasmclient";
import { RestClient } from "./restclient";
import { setupWasmExtension, WasmExtension } from "./lcdapi/wasm";
import { SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient";
import { alice, getHackatom, makeRandomAddress, pendingWithoutWasmd, unused } from "./testutils.spec";
const httpUrl = "http://localhost:1317";
function makeWasmClient(apiUrl: string): LcdClient & AuthExtension & WasmExtension {
return LcdClient.withExtensions({ apiUrl }, setupAuthExtension, setupWasmExtension);
}
describe("SigningCosmWasmClient", () => {
describe("makeReadOnly", () => {
it("can be constructed", async () => {
@ -26,8 +30,8 @@ describe("SigningCosmWasmClient", () => {
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough();
const authAccountsSpy = spyOn(openedClient.restClient, "authAccounts").and.callThrough();
const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough();
const authAccountsSpy = spyOn(openedClient.lcdClient.auth, "account").and.callThrough();
const height = await client.getHeight();
expect(height).toBeGreaterThan(0);
@ -97,8 +101,8 @@ describe("SigningCosmWasmClient", () => {
},
);
const rest = new RestClient(httpUrl);
const balance = (await rest.authAccounts(contractAddress)).result.value.coins;
const lcdClient = makeWasmClient(httpUrl);
const balance = (await lcdClient.auth.account(contractAddress)).result.value.coins;
expect(balance).toEqual(transferAmount);
});
@ -119,8 +123,8 @@ describe("SigningCosmWasmClient", () => {
{ admin: unused.address },
);
const rest = new RestClient(httpUrl);
const contract = await rest.getContractInfo(contractAddress);
const lcdClient = makeWasmClient(httpUrl);
const contract = await lcdClient.wasm.getContractInfo(contractAddress);
assert(contract);
expect(contract.admin).toEqual(unused.address);
});
@ -171,14 +175,14 @@ describe("SigningCosmWasmClient", () => {
},
);
const rest = new RestClient(httpUrl);
const state1 = await rest.getContractInfo(contractAddress);
const lcdClient = makeWasmClient(httpUrl);
const state1 = await lcdClient.wasm.getContractInfo(contractAddress);
assert(state1);
expect(state1.admin).toEqual(alice.address0);
await client.updateAdmin(contractAddress, unused.address);
const state2 = await rest.getContractInfo(contractAddress);
const state2 = await lcdClient.wasm.getContractInfo(contractAddress);
assert(state2);
expect(state2.admin).toEqual(unused.address);
});
@ -204,14 +208,14 @@ describe("SigningCosmWasmClient", () => {
},
);
const rest = new RestClient(httpUrl);
const state1 = await rest.getContractInfo(contractAddress);
const lcdClient = makeWasmClient(httpUrl);
const state1 = await lcdClient.wasm.getContractInfo(contractAddress);
assert(state1);
expect(state1.admin).toEqual(alice.address0);
await client.clearAdmin(contractAddress);
const state2 = await rest.getContractInfo(contractAddress);
const state2 = await lcdClient.wasm.getContractInfo(contractAddress);
assert(state2);
expect(state2.admin).toBeUndefined();
});
@ -238,15 +242,15 @@ describe("SigningCosmWasmClient", () => {
},
);
const rest = new RestClient(httpUrl);
const state1 = await rest.getContractInfo(contractAddress);
const lcdClient = makeWasmClient(httpUrl);
const state1 = await lcdClient.wasm.getContractInfo(contractAddress);
assert(state1);
expect(state1.admin).toEqual(alice.address0);
const newVerifier = makeRandomAddress();
await client.migrate(contractAddress, codeId2, { verifier: newVerifier });
const state2 = await rest.getContractInfo(contractAddress);
const state2 = await lcdClient.wasm.getContractInfo(contractAddress);
assert(state2);
expect(state2).toEqual({
...state1,
@ -289,10 +293,10 @@ describe("SigningCosmWasmClient", () => {
});
// Verify token transfer from contract to beneficiary
const rest = new RestClient(httpUrl);
const beneficiaryBalance = (await rest.authAccounts(beneficiaryAddress)).result.value.coins;
const lcdClient = makeWasmClient(httpUrl);
const beneficiaryBalance = (await lcdClient.auth.account(beneficiaryAddress)).result.value.coins;
expect(beneficiaryBalance).toEqual(transferAmount);
const contractBalance = (await rest.authAccounts(contractAddress)).result.value.coins;
const contractBalance = (await lcdClient.auth.account(contractAddress)).result.value.coins;
expect(contractBalance).toEqual([]);
});
});

View File

@ -1,6 +1,15 @@
import { BroadcastMode, Coin, CosmosSdkTx, IndexedTx, PubKey, StdTx } from "@cosmjs/sdk38";
import {
AuthExtension,
BroadcastMode,
Coin,
CosmosSdkTx,
IndexedTx,
LcdClient,
PubKey,
StdTx,
} from "@cosmjs/sdk38";
import { WasmExtension } from "./lcdapi/wasm";
import { Log } from "./logs";
import { RestClient } from "./restclient";
import { JsonObject } from "./types";
export interface GetNonceResult {
readonly accountNumber: number;
@ -114,10 +123,10 @@ export interface Block {
}
/** Use for testing only */
export interface PrivateCosmWasmClient {
readonly restClient: RestClient;
readonly lcdClient: LcdClient & AuthExtension & WasmExtension;
}
export declare class CosmWasmClient {
protected readonly restClient: RestClient;
protected readonly lcdClient: LcdClient & AuthExtension & WasmExtension;
/** Any address the chain considers valid (valid bech32 with proper prefix) */
protected anyValidAddress: string | undefined;
private readonly codesCache;