Remove contract functionality from sdk38

This commit is contained in:
Simon Warta 2020-06-03 08:41:51 +02:00
parent 9d054c6dd3
commit 54cff0367e
9 changed files with 6 additions and 388 deletions

View File

@ -76,7 +76,7 @@ describe("CosmosClient", () => {
const height1 = await client.getHeight();
expect(height1).toBeGreaterThan(0);
await client.getCodes(); // warm up the client
await client.getAccount(guest.address); // warm up the client
const height2 = await client.getHeight();
expect(height2).toBeGreaterThan(0);
@ -85,7 +85,7 @@ describe("CosmosClient", () => {
expect(height3).toEqual(height2 + 1);
expect(blockLatestSpy).toHaveBeenCalledTimes(1);
expect(authAccountsSpy).toHaveBeenCalledTimes(2);
expect(authAccountsSpy).toHaveBeenCalledTimes(3);
});
});
@ -232,65 +232,4 @@ describe("CosmosClient", () => {
expect(transactionHash).toMatch(/^[0-9A-F]{64}$/);
});
});
describe("getContracts", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = new CosmosClient(wasmd.endpoint);
const result = await client.getContracts(1);
expect(result.length).toBeGreaterThanOrEqual(3);
const [hash, isa, jade] = result;
expect(hash).toEqual({
address: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5",
codeId: 1,
creator: faucet.address,
label: "HASH",
});
expect(isa).toEqual({
address: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd",
codeId: 1,
creator: faucet.address,
label: "ISA",
});
expect(jade).toEqual({
address: "cosmos18r5szma8hm93pvx6lwpjwyxruw27e0k5uw835c",
codeId: 1,
creator: faucet.address,
label: "JADE",
});
});
});
describe("getContract", () => {
it("works for HASH instance", async () => {
pendingWithoutWasmd();
const client = new CosmosClient(wasmd.endpoint);
const hash = await client.getContract("cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5");
expect(hash).toEqual({
address: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5",
codeId: 1,
creator: faucet.address,
label: "HASH",
initMsg: {
decimals: 5,
name: "Hash token",
symbol: "HASH",
initial_balances: jasmine.arrayContaining([
{
address: faucet.address,
amount: "11",
},
{
address: unused.address,
amount: "12812345",
},
{
address: guest.address,
amount: "22004000000",
},
]),
},
});
});
});
});

View File

@ -5,7 +5,7 @@ import { Coin } from "./coins";
import { Log, parseLogs } from "./logs";
import { decodeBech32Pubkey } from "./pubkey";
import { BroadcastMode, RestClient } from "./restclient";
import { CosmosSdkTx, JsonObject, PubKey, StdTx } from "./types";
import { CosmosSdkTx, PubKey, StdTx } from "./types";
export interface GetNonceResult {
readonly accountNumber: number;
@ -75,34 +75,6 @@ export interface SearchTxFilter {
readonly maxHeight?: number;
}
export interface Code {
readonly id: number;
/** Bech32 account address */
readonly creator: string;
/** Hex-encoded sha256 hash of the code stored here */
readonly checksum: string;
readonly source?: string;
readonly builder?: string;
}
export interface CodeDetails extends Code {
/** The original wasm bytes */
readonly data: Uint8Array;
}
export interface Contract {
readonly address: string;
readonly codeId: number;
/** Bech32 account address */
readonly creator: string;
readonly label: string;
}
export interface ContractDetails extends Contract {
/** Argument passed on initialization of the contract */
readonly initMsg: object;
}
/** A transaction that is indexed as part of the transaction history */
export interface IndexedTx {
readonly height: number;
@ -150,7 +122,6 @@ export class CosmosClient {
/** Any address the chain considers valid (valid bech32 with proper prefix) */
protected anyValidAddress: string | undefined;
private readonly codesCache = new Map<number, CodeDetails>();
private chainId: string | undefined;
/**
@ -318,102 +289,6 @@ export class CosmosClient {
};
}
public async getCodes(): Promise<readonly Code[]> {
const result = await this.restClient.listCodeInfo();
return result.map(
(entry): Code => {
this.anyValidAddress = entry.creator;
return {
id: entry.id,
creator: entry.creator,
checksum: Encoding.toHex(Encoding.fromHex(entry.data_hash)),
source: entry.source || undefined,
builder: entry.builder || undefined,
};
},
);
}
public async getCodeDetails(codeId: number): Promise<CodeDetails> {
const cached = this.codesCache.get(codeId);
if (cached) return cached;
const getCodeResult = await this.restClient.getCode(codeId);
const codeDetails: CodeDetails = {
id: getCodeResult.id,
creator: getCodeResult.creator,
checksum: Encoding.toHex(Encoding.fromHex(getCodeResult.data_hash)),
source: getCodeResult.source || undefined,
builder: getCodeResult.builder || undefined,
data: Encoding.fromBase64(getCodeResult.data),
};
this.codesCache.set(codeId, codeDetails);
return codeDetails;
}
public async getContracts(codeId: number): Promise<readonly Contract[]> {
const result = await this.restClient.listContractsByCodeId(codeId);
return result.map(
(entry): Contract => ({
address: entry.address,
codeId: entry.code_id,
creator: entry.creator,
label: entry.label,
}),
);
}
/**
* 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);
if (!result) throw new Error(`No contract found at address "${address}"`);
return {
address: result.address,
codeId: result.code_id,
creator: result.creator,
label: result.label,
initMsg: result.init_msg,
};
}
/**
* Returns the data at the key if present (raw contract dependent storage data)
* or null if no data at this key.
*
* Promise is rejected when contract does not exist.
*/
public async queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null> {
// just test contract existence
const _info = await this.getContract(address);
return this.restClient.queryContractRaw(address, key);
}
/**
* Makes a smart query on the contract, returns the parsed JSON document.
*
* Promise is rejected when contract does not exist.
* Promise is rejected for invalid query format.
* Promise is rejected for invalid response format.
*/
public async queryContractSmart(address: string, queryMsg: object): Promise<JsonObject> {
try {
return await this.restClient.queryContractSmart(address, queryMsg);
} catch (error) {
if (error instanceof Error) {
if (error.message.startsWith("not found: contract")) {
throw new Error(`No contract found at address "${address}"`);
} else {
throw error;
}
} else {
throw error;
}
}
}
private async txsQuery(query: string): Promise<readonly IndexedTx[]> {
// TODO: we need proper pagination support
const limit = 100;

View File

@ -8,10 +8,6 @@ export {
Account,
Block,
BlockHeader,
Code,
CodeDetails,
Contract,
ContractDetails,
CosmosClient,
GetNonceResult,
IndexedTx,

View File

@ -2,9 +2,7 @@ import { Encoding, isNonNullObject } from "@iov/encoding";
import axios, { AxiosError, AxiosInstance } from "axios";
import { Coin } from "./coins";
import { CosmosSdkTx, JsonObject, Model, parseWasmData, StdTx, WasmData } from "./types";
const { fromBase64, fromUtf8, toHex, toUtf8 } = Encoding;
import { CosmosSdkTx, StdTx } from "./types";
export interface CosmosSdkAccount {
/** Bech32 account address */
@ -210,13 +208,6 @@ type RestClientResponse =
| WasmResponse<ContractInfo[] | null>
| WasmResponse<ContractDetails | null>;
/** Unfortunately, Cosmos SDK encodes empty arrays as null */
type CosmosSdkArray<T> = ReadonlyArray<T> | null;
function normalizeArray<T>(backend: CosmosSdkArray<T>): ReadonlyArray<T> {
return backend || [];
}
/**
* The mode used to send transaction
*
@ -231,17 +222,6 @@ export enum BroadcastMode {
Async = "async",
}
function isWasmError<T>(resp: WasmResponse<T>): resp is WasmError {
return (resp as WasmError).error !== undefined;
}
function unwrapWasmResponse<T>(response: WasmResponse<T>): T {
if (isWasmError(response)) {
throw new Error(response.error);
}
return response.result;
}
// We want to get message data from 500 errors
// https://stackoverflow.com/questions/56577124/how-to-handle-500-error-message-with-axios
// this should be chained to catch one error and throw a more informative one
@ -391,67 +371,4 @@ export class RestClient {
}
return responseData as PostTxsResponse;
}
// The /wasm endpoints
// wasm rest queries are listed here: https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27
public async listCodeInfo(): Promise<readonly CodeInfo[]> {
const path = `/wasm/code`;
const responseData = (await this.get(path)) as WasmResponse<CosmosSdkArray<CodeInfo>>;
return normalizeArray(unwrapWasmResponse(responseData));
}
// this will download the original wasm bytecode by code id
// throws error if no code with this id
public async getCode(id: number): Promise<CodeDetails> {
const path = `/wasm/code/${id}`;
const responseData = (await this.get(path)) as WasmResponse<CodeDetails>;
return unwrapWasmResponse(responseData);
}
public async listContractsByCodeId(id: number): Promise<readonly ContractInfo[]> {
const path = `/wasm/code/${id}/contracts`;
const responseData = (await this.get(path)) as WasmResponse<CosmosSdkArray<ContractInfo>>;
return normalizeArray(unwrapWasmResponse(responseData));
}
/**
* Returns null when contract was not found at this address.
*/
public async getContractInfo(address: string): Promise<ContractDetails | null> {
const path = `/wasm/contract/${address}`;
const response = (await this.get(path)) as WasmResponse<ContractDetails | null>;
return unwrapWasmResponse(response);
}
// Returns all contract state.
// This is an empty array if no such contract, or contract has no data.
public async getAllContractState(address: string): Promise<readonly Model[]> {
const path = `/wasm/contract/${address}/state`;
const responseData = (await this.get(path)) as WasmResponse<CosmosSdkArray<WasmData>>;
return normalizeArray(unwrapWasmResponse(responseData)).map(parseWasmData);
}
// Returns the data at the key if present (unknown decoded json),
// or null if no data at this (contract address, key) pair
public async queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null> {
const hexKey = toHex(key);
const path = `/wasm/contract/${address}/raw/${hexKey}?encoding=hex`;
const responseData = (await this.get(path)) as WasmResponse<WasmData[]>;
const data = unwrapWasmResponse(responseData);
return data.length === 0 ? null : fromBase64(data[0].val);
}
/**
* Makes a smart query on the contract and parses the reponse as JSON.
* Throws error if no such contract exists, the query format is invalid or the response is invalid.
*/
public async queryContractSmart(address: string, query: object): Promise<JsonObject> {
const encoded = toHex(toUtf8(JSON.stringify(query)));
const path = `/wasm/contract/${address}/smart/${encoded}?encoding=hex`;
const responseData = (await this.get(path)) as WasmResponse<SmartQueryResponse>;
const result = unwrapWasmResponse(responseData);
// By convention, smart queries must return a valid JSON document (see https://github.com/CosmWasm/cosmwasm/issues/144)
return JSON.parse(fromUtf8(fromBase64(result.smart)));
}
}

View File

@ -1,9 +1,5 @@
import { Encoding } from "@iov/encoding";
import { Coin } from "./coins";
const { fromBase64, fromHex } = Encoding;
/** An Amino/Cosmos SDK StdTx */
export interface StdTx {
readonly msg: ReadonlyArray<Msg>;
@ -75,29 +71,3 @@ export const pubkeyType = {
};
export const pubkeyTypes: readonly string[] = [pubkeyType.secp256k1, pubkeyType.ed25519, pubkeyType.sr25519];
export interface WasmData {
// key is hex-encoded
readonly key: string;
// value is base64 encoded
readonly val: string;
}
// Model is a parsed WasmData object
export interface Model {
readonly key: Uint8Array;
readonly val: Uint8Array;
}
export function parseWasmData({ key, val }: WasmData): Model {
return {
key: fromHex(key),
val: fromBase64(val),
};
}
/**
* An object containing a parsed JSON document. The result of JSON.parse().
* This doen't privide any type safety over `any` but expresses intent in the code.
*/
export type JsonObject = any;

View File

@ -1,7 +1,7 @@
import { Coin } from "./coins";
import { Log } from "./logs";
import { BroadcastMode, RestClient } from "./restclient";
import { CosmosSdkTx, JsonObject, PubKey, StdTx } from "./types";
import { CosmosSdkTx, PubKey, StdTx } from "./types";
export interface GetNonceResult {
readonly accountNumber: number;
readonly sequence: number;
@ -48,30 +48,6 @@ export interface SearchTxFilter {
readonly minHeight?: number;
readonly maxHeight?: number;
}
export interface Code {
readonly id: number;
/** Bech32 account address */
readonly creator: string;
/** Hex-encoded sha256 hash of the code stored here */
readonly checksum: string;
readonly source?: string;
readonly builder?: string;
}
export interface CodeDetails extends Code {
/** The original wasm bytes */
readonly data: Uint8Array;
}
export interface Contract {
readonly address: string;
readonly codeId: number;
/** Bech32 account address */
readonly creator: string;
readonly label: string;
}
export interface ContractDetails extends Contract {
/** Argument passed on initialization of the contract */
readonly initMsg: object;
}
/** A transaction that is indexed as part of the transaction history */
export interface IndexedTx {
readonly height: number;
@ -114,7 +90,6 @@ export declare class CosmosClient {
protected readonly restClient: RestClient;
/** Any address the chain considers valid (valid bech32 with proper prefix) */
protected anyValidAddress: string | undefined;
private readonly codesCache;
private chainId;
/**
* Creates a new client to interact with a CosmWasm blockchain.
@ -149,27 +124,5 @@ export declare class CosmosClient {
getBlock(height?: number): Promise<Block>;
searchTx(query: SearchTxQuery, filter?: SearchTxFilter): Promise<readonly IndexedTx[]>;
postTx(tx: StdTx): Promise<PostTxResult>;
getCodes(): Promise<readonly Code[]>;
getCodeDetails(codeId: number): Promise<CodeDetails>;
getContracts(codeId: number): Promise<readonly Contract[]>;
/**
* Throws an error if no contract was found at the address
*/
getContract(address: string): Promise<ContractDetails>;
/**
* Returns the data at the key if present (raw contract dependent storage data)
* or null if no data at this key.
*
* Promise is rejected when contract does not exist.
*/
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
/**
* Makes a smart query on the contract, returns the parsed JSON document.
*
* Promise is rejected when contract does not exist.
* Promise is rejected for invalid query format.
* Promise is rejected for invalid response format.
*/
queryContractSmart(address: string, queryMsg: object): Promise<JsonObject>;
private txsQuery;
}

View File

@ -6,10 +6,6 @@ export {
Account,
Block,
BlockHeader,
Code,
CodeDetails,
Contract,
ContractDetails,
CosmosClient,
GetNonceResult,
IndexedTx,

View File

@ -1,5 +1,5 @@
import { Coin } from "./coins";
import { CosmosSdkTx, JsonObject, Model, StdTx } from "./types";
import { CosmosSdkTx, StdTx } from "./types";
export interface CosmosSdkAccount {
/** Bech32 account address */
readonly address: string;
@ -213,19 +213,5 @@ export declare class RestClient {
* @param tx a signed transaction as StdTx (i.e. not wrapped in type/value container)
*/
postTx(tx: StdTx): Promise<PostTxsResponse>;
listCodeInfo(): Promise<readonly CodeInfo[]>;
getCode(id: number): Promise<CodeDetails>;
listContractsByCodeId(id: number): Promise<readonly ContractInfo[]>;
/**
* Returns null when contract was not found at this address.
*/
getContractInfo(address: string): Promise<ContractDetails | null>;
getAllContractState(address: string): Promise<readonly Model[]>;
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
/**
* Makes a smart query on the contract and parses the reponse as JSON.
* Throws error if no such contract exists, the query format is invalid or the response is invalid.
*/
queryContractSmart(address: string, query: object): Promise<JsonObject>;
}
export {};

View File

@ -48,17 +48,3 @@ export declare const pubkeyType: {
sr25519: "tendermint/PubKeySr25519";
};
export declare const pubkeyTypes: readonly string[];
export interface WasmData {
readonly key: string;
readonly val: string;
}
export interface Model {
readonly key: Uint8Array;
readonly val: Uint8Array;
}
export declare function parseWasmData({ key, val }: WasmData): Model;
/**
* An object containing a parsed JSON document. The result of JSON.parse().
* This doen't privide any type safety over `any` but expresses intent in the code.
*/
export declare type JsonObject = any;