Skip to content

Commit

Permalink
Merge pull request #1564 from cosmos/wasmd-0.50
Browse files Browse the repository at this point in the history
Adapt codebase to work with wasmd 0.50 (Cosmos SDK 0.50)
  • Loading branch information
webmaster128 committed Mar 8, 2024
2 parents d7d9b7e + 5cfd592 commit f94b230
Show file tree
Hide file tree
Showing 22 changed files with 226 additions and 203 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@ and this project adheres to
import { parseCoins } from "@cosmjs/amino";
```

- @cosmjs/stargate: Let `parseRawLog` gracefully handle empty strings to better
support Cosmos SDK 0.50 inputs. ([#1564])

[#1564]: https://github.com/cosmos/cosmjs/pull/1564

### Fixed

- @cosmjs/encoding: Avoid using replacement character in doc comment to make
external tools happy. ([#1570])
- @cosmjs/cosmwasm-stargate: Use events instead of log parsing to receive
information in SigningCosmWasmClient. This is required to support Cosmos SDK
0.50+ where the `rawLog` field is empty. ([#1564])

[#1564]: https://github.com/cosmos/cosmjs/pull/1564
[#1570]: https://github.com/cosmos/cosmjs/pull/1570

## [0.32.2] - 2023-12-19
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/examples/cosmwasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async function main(hackatomWasmPath: string) {
// Execute contract
const executeFee = calculateFee(300_000, gasPrice);
const result = await client.execute(alice.address0, contractAddress, { release: {} }, executeFee);
const wasmEvent = result.logs[0].events.find((e) => e.type === "wasm");
const wasmEvent = result.events.find((e) => e.type === "wasm");
console.info("The `wasm` event emitted by the contract execution:", wasmEvent);
}

Expand Down
10 changes: 6 additions & 4 deletions packages/cosmwasm-stargate/src/cosmwasmclient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Registry,
TxBodyEncodeObject,
} from "@cosmjs/proto-signing";
import { assertIsDeliverTxSuccess, coins, logs, MsgSendEncodeObject, StdFee } from "@cosmjs/stargate";
import { assertIsDeliverTxSuccess, coins, MsgSendEncodeObject, StdFee } from "@cosmjs/stargate";
import { assert, sleep } from "@cosmjs/utils";
import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx";
import { ReadonlyDate } from "readonly-date";
Expand Down Expand Up @@ -188,7 +188,6 @@ describe("CosmWasmClient", () => {
amount: coins(5000, "ucosm"),
gas: "890000",
};

const chainId = await client.getChainId();
const sequenceResponse = await client.getSequence(alice.address0);
assert(sequenceResponse);
Expand Down Expand Up @@ -222,8 +221,11 @@ describe("CosmWasmClient", () => {
const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish());
const result = await client.broadcastTx(signedTx);
assertIsDeliverTxSuccess(result);
const amountAttr = logs.findAttribute(logs.parseRawLog(result.rawLog), "transfer", "amount");
expect(amountAttr.value).toEqual("1234567ucosm");
const amountAttrs = result.events
.filter((e) => e.type == "transfer")
.flatMap((e) => e.attributes.filter((a) => a.key == "amount"));
expect(amountAttrs[0].value).toEqual("5000ucosm"); // fee
expect(amountAttrs[1].value).toEqual("1234567ucosm"); // MsgSend amount
expect(result.transactionHash).toMatch(/^[0-9A-F]{64}$/);
});
});
Expand Down
23 changes: 11 additions & 12 deletions packages/cosmwasm-stargate/src/modules/wasm/queries.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import {
coin,
coins,
DeliverTxResponse,
logs,
SigningStargateClient,
StdFee,
} from "@cosmjs/stargate";
import { assert, assertDefined } from "@cosmjs/utils";
import { MsgExecuteContract, MsgInstantiateContract, MsgStoreCode } from "cosmjs-types/cosmwasm/wasm/v1/tx";
import { AbsoluteTxPosition, ContractCodeHistoryOperationType } from "cosmjs-types/cosmwasm/wasm/v1/types";

import { SigningCosmWasmClient } from "../../signingcosmwasmclient";
import { findAttribute, SigningCosmWasmClient } from "../../signingcosmwasmclient";
import {
alice,
bech32AddressMatcher,
Expand Down Expand Up @@ -385,12 +384,11 @@ describe("WasmExtension", () => {
{
const result = await uploadContract(wallet, getHackatom());
assertIsDeliverTxSuccess(result);
const parsedLogs = logs.parseLogs(logs.parseRawLog(result.rawLog));
const codeIdAttr = logs.findAttribute(parsedLogs, "store_code", "code_id");
const codeIdAttr = findAttribute(result.events, "store_code", "code_id");
codeId = Number.parseInt(codeIdAttr.value, 10);
expect(codeId).toBeGreaterThanOrEqual(1);
expect(codeId).toBeLessThanOrEqual(200);
const actionAttr = logs.findAttribute(parsedLogs, "message", "module");
const actionAttr = findAttribute(result.events, "message", "module");
expect(actionAttr.value).toEqual("wasm");
}

Expand All @@ -400,12 +398,14 @@ describe("WasmExtension", () => {
{
const result = await instantiateContract(wallet, codeId, beneficiaryAddress, funds);
assertIsDeliverTxSuccess(result);
const parsedLogs = logs.parseLogs(logs.parseRawLog(result.rawLog));
const contractAddressAttr = logs.findAttribute(parsedLogs, "instantiate", "_contract_address");
const contractAddressAttr = findAttribute(result.events, "instantiate", "_contract_address");
contractAddress = contractAddressAttr.value;
const amountAttr = logs.findAttribute(parsedLogs, "transfer", "amount");
expect(amountAttr.value).toEqual("1234ucosm,321ustake");
const actionAttr = logs.findAttribute(parsedLogs, "message", "module");
const amountAttrs = result.events
.filter((e) => e.type == "transfer")
.flatMap((e) => e.attributes.filter((a) => a.key == "amount"));
expect(amountAttrs[0].value).toEqual("5000000ucosm"); // fee
expect(amountAttrs[1].value).toEqual("1234ucosm,321ustake"); // instantiate funds
const actionAttr = findAttribute(result.events, "message", "module");
expect(actionAttr.value).toEqual("wasm");

const balanceUcosm = await client.bank.balance(contractAddress, "ucosm");
Expand All @@ -418,8 +418,7 @@ describe("WasmExtension", () => {
{
const result = await executeContract(wallet, contractAddress, { release: {} });
assertIsDeliverTxSuccess(result);
const parsedLogs = logs.parseLogs(logs.parseRawLog(result.rawLog));
const wasmEvent = parsedLogs.find(() => true)?.events.find((e) => e.type === "wasm");
const wasmEvent = result.events.find((e) => e.type === "wasm");
assert(wasmEvent, "Event of type wasm expected");
expect(wasmEvent.attributes).toContain({ key: "action", value: "release" });
expect(wasmEvent.attributes).toContain({
Expand Down
28 changes: 18 additions & 10 deletions packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ describe("SigningCosmWasmClient", () => {

it("works with legacy Amino signer access type", async () => {
pendingWithoutWasmd();
pending("wasmd 0.50 does not work with Amino JSON signing");
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix });
const options = { ...defaultSigningClientOptions, prefix: wasmd.prefix };
const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options);
Expand Down Expand Up @@ -263,6 +264,7 @@ describe("SigningCosmWasmClient", () => {

it("works with legacy Amino signer", async () => {
pendingWithoutWasmd();
pending("wasmd 0.50 does not work with Amino JSON signing");
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix });
const client = await SigningCosmWasmClient.connectWithSigner(
wasmd.endpoint,
Expand Down Expand Up @@ -313,7 +315,7 @@ describe("SigningCosmWasmClient", () => {
const { codeId } = await client.upload(alice.address0, getHackatom().data, defaultUploadFee);
const funds = [coin(1234, "ucosm"), coin(321, "ustake")];
const beneficiaryAddress = makeRandomAddress();
const salt = Uint8Array.from([0x01]);
const salt = Random.getBytes(64); // different salt every time we run the test to avoid address collision erors
const wasm = getHackatom().data;
const msg = {
verifier: alice.address0,
Expand Down Expand Up @@ -346,6 +348,7 @@ describe("SigningCosmWasmClient", () => {

it("works with Amino JSON signing", async () => {
pendingWithoutWasmd();
pending("wasmd 0.50 does not work with Amino JSON signing");
const aminoJsonWallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, {
prefix: wasmd.prefix,
});
Expand Down Expand Up @@ -527,6 +530,7 @@ describe("SigningCosmWasmClient", () => {

it("works with legacy Amino signer", async () => {
pendingWithoutWasmd();
pending("wasmd 0.50 does not work with Amino JSON signing");
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix });
const client = await SigningCosmWasmClient.connectWithSigner(
wasmd.endpoint,
Expand Down Expand Up @@ -607,7 +611,7 @@ describe("SigningCosmWasmClient", () => {
expect(result.height).toBeGreaterThan(0);
expect(result.gasWanted).toBeGreaterThan(0);
expect(result.gasUsed).toBeGreaterThan(0);
const wasmEvent = result.logs[0].events.find((e) => e.type === "wasm");
const wasmEvent = result.events.find((e) => e.type === "wasm");
assert(wasmEvent, "Event of type wasm expected");
expect(wasmEvent.attributes).toContain({ key: "action", value: "release" });
expect(wasmEvent.attributes).toContain({
Expand All @@ -630,6 +634,7 @@ describe("SigningCosmWasmClient", () => {

it("works with legacy Amino signer", async () => {
pendingWithoutWasmd();
pending("wasmd 0.50 does not work with Amino JSON signing");
const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix });
const client = await SigningCosmWasmClient.connectWithSigner(
wasmd.endpoint,
Expand Down Expand Up @@ -660,7 +665,7 @@ describe("SigningCosmWasmClient", () => {
{ release: {} },
defaultExecuteFee,
);
const wasmEvent = result.logs[0].events.find((e) => e.type === "wasm");
const wasmEvent = result.events.find((e) => e.type === "wasm");
assert(wasmEvent, "Event of type wasm expected");
expect(wasmEvent.attributes).toContain({ key: "action", value: "release" });
expect(wasmEvent.attributes).toContain({
Expand Down Expand Up @@ -727,16 +732,17 @@ describe("SigningCosmWasmClient", () => {
],
"auto",
);
expect(result.logs.length).toEqual(2);
const wasmEvent1 = result.logs[0].events.find((e) => e.type === "wasm");
assert(wasmEvent1, "Event of type wasm expected");
const { events } = result;
const wasmEvents = events.filter((e) => e.type == "wasm");
expect(wasmEvents.length).toEqual(2);
const [wasmEvent1, wasmEvent2] = wasmEvents;
expect(wasmEvent1.type).toEqual("wasm");
expect(wasmEvent1.attributes).toContain({ key: "action", value: "release" });
expect(wasmEvent1.attributes).toContain({
key: "destination",
value: beneficiaryAddress1,
});
const wasmEvent2 = result.logs[1].events.find((e) => e.type === "wasm");
assert(wasmEvent2, "Event of type wasm expected");
expect(wasmEvent2.type).toEqual("wasm");
expect(wasmEvent2.attributes).toContain({ key: "action", value: "release" });
expect(wasmEvent2.attributes).toContain({
key: "destination",
Expand Down Expand Up @@ -777,7 +783,8 @@ describe("SigningCosmWasmClient", () => {
memo,
);
assertIsDeliverTxSuccess(result);
expect(result.rawLog).toBeTruthy();
expect(result.rawLog).toEqual(""); // empty for wasmd >= 0.50.0 (https://github.com/cosmos/cosmos-sdk/pull/15845)
expect(result.events.length).toBeGreaterThanOrEqual(1);

// got tokens
const after = await client.getBalance(beneficiaryAddress, "ucosm");
Expand Down Expand Up @@ -816,7 +823,8 @@ describe("SigningCosmWasmClient", () => {
memo,
);
assertIsDeliverTxSuccess(result);
expect(result.rawLog).toBeTruthy();
expect(result.rawLog).toEqual(""); // empty for wasmd >= 0.50.0 (https://github.com/cosmos/cosmos-sdk/pull/15845)
expect(result.events.length).toBeGreaterThanOrEqual(1);

// got tokens
const after = await client.getBalance(beneficiaryAddress, "ucosm");
Expand Down
39 changes: 30 additions & 9 deletions packages/cosmwasm-stargate/src/signingcosmwasmclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from "@cosmjs/proto-signing";
import {
AminoTypes,
Attribute,
calculateFee,
Coin,
createDefaultAminoConverters,
Expand Down Expand Up @@ -72,6 +73,7 @@ export interface UploadResult {
readonly compressedSize: number;
/** The ID of the code asigned by the chain */
readonly codeId: number;
/** @deprecated Not filled in Cosmos SDK >= 0.50. Use events instead. */
readonly logs: readonly logs.Log[];
/** Block height in which the transaction is included */
readonly height: number;
Expand Down Expand Up @@ -106,6 +108,7 @@ export interface InstantiateOptions {
export interface InstantiateResult {
/** The address of the newly instantiated contract */
readonly contractAddress: string;
/** @deprecated Not filled in Cosmos SDK >= 0.50. Use events instead. */
readonly logs: readonly logs.Log[];
/** Block height in which the transaction is included */
readonly height: number;
Expand All @@ -120,6 +123,7 @@ export interface InstantiateResult {
* Result type of updateAdmin and clearAdmin
*/
export interface ChangeAdminResult {
/** @deprecated Not filled in Cosmos SDK >= 0.50. Use events instead. */
readonly logs: readonly logs.Log[];
/** Block height in which the transaction is included */
readonly height: number;
Expand All @@ -131,6 +135,7 @@ export interface ChangeAdminResult {
}

export interface MigrateResult {
/** @deprecated Not filled in Cosmos SDK >= 0.50. Use events instead. */
readonly logs: readonly logs.Log[];
/** Block height in which the transaction is included */
readonly height: number;
Expand All @@ -148,6 +153,7 @@ export interface ExecuteInstruction {
}

export interface ExecuteResult {
/** @deprecated Not filled in Cosmos SDK >= 0.50. Use events instead. */
readonly logs: readonly logs.Log[];
/** Block height in which the transaction is included */
readonly height: number;
Expand All @@ -158,6 +164,24 @@ export interface ExecuteResult {
readonly gasUsed: bigint;
}

/**
* Searches in events for an event of the given event type which contains an
* attribute for with the given key.
*
* Throws if the attribute was not found.
*/
export function findAttribute(events: readonly Event[], eventType: string, attrKey: string): Attribute {
// all attributes from events with the right event type
const attributes = events.filter((event) => event.type === eventType).flatMap((e) => e.attributes);
const out = attributes.find((attr) => attr.key === attrKey);
if (!out) {
throw new Error(
`Could not find attribute '${attrKey}' in first event of type '${eventType}' in first log.`,
);
}
return out;
}

function createDeliverTxResponseErrorMessage(result: DeliverTxResponse): string {
return `Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`;
}
Expand Down Expand Up @@ -288,14 +312,13 @@ export class SigningCosmWasmClient extends CosmWasmClient {
if (isDeliverTxFailure(result)) {
throw new Error(createDeliverTxResponseErrorMessage(result));
}
const parsedLogs = logs.parseRawLog(result.rawLog);
const codeIdAttr = logs.findAttribute(parsedLogs, "store_code", "code_id");
const codeIdAttr = findAttribute(result.events, "store_code", "code_id");
return {
checksum: toHex(sha256(wasmCode)),
originalSize: wasmCode.length,
compressedSize: compressed.length,
codeId: Number.parseInt(codeIdAttr.value, 10),
logs: parsedLogs,
logs: logs.parseRawLog(result.rawLog),
height: result.height,
transactionHash: result.transactionHash,
events: result.events,
Expand Down Expand Up @@ -327,11 +350,10 @@ export class SigningCosmWasmClient extends CosmWasmClient {
if (isDeliverTxFailure(result)) {
throw new Error(createDeliverTxResponseErrorMessage(result));
}
const parsedLogs = logs.parseRawLog(result.rawLog);
const contractAddressAttr = logs.findAttribute(parsedLogs, "instantiate", "_contract_address");
const contractAddressAttr = findAttribute(result.events, "instantiate", "_contract_address");
return {
contractAddress: contractAddressAttr.value,
logs: parsedLogs,
logs: logs.parseRawLog(result.rawLog),
height: result.height,
transactionHash: result.transactionHash,
events: result.events,
Expand Down Expand Up @@ -366,11 +388,10 @@ export class SigningCosmWasmClient extends CosmWasmClient {
if (isDeliverTxFailure(result)) {
throw new Error(createDeliverTxResponseErrorMessage(result));
}
const parsedLogs = logs.parseRawLog(result.rawLog);
const contractAddressAttr = logs.findAttribute(parsedLogs, "instantiate", "_contract_address");
const contractAddressAttr = findAttribute(result.events, "instantiate", "_contract_address");
return {
contractAddress: contractAddressAttr.value,
logs: parsedLogs,
logs: logs.parseRawLog(result.rawLog),
height: result.height,
transactionHash: result.transactionHash,
events: result.events,
Expand Down
8 changes: 1 addition & 7 deletions packages/cosmwasm-stargate/src/testutils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,12 @@ export const unused = {
};

export const validator = {
/**
* delegator_address from /cosmos.staking.v1beta1.MsgCreateValidator in scripts/wasmd/template/.wasmd/config/genesis.json
*
* `jq ".app_state.genutil.gen_txs[0].body.messages[0].delegator_address" scripts/wasmd/template/.wasmd/config/genesis.json`
*/
delegatorAddress: "wasm1g6kvj7w4c8g0vhl35kjgpe3jmuauet0e5tnevj",
/**
* validator_address from /cosmos.staking.v1beta1.MsgCreateValidator in scripts/wasmd/template/.wasmd/config/genesis.json
*
* `jq ".app_state.genutil.gen_txs[0].body.messages[0].validator_address" scripts/wasmd/template/.wasmd/config/genesis.json`
*/
validatorAddress: "wasmvaloper1g6kvj7w4c8g0vhl35kjgpe3jmuauet0ephx9zg",
validatorAddress: "wasmvaloper1k2vfqeu2upskfv7awn29g5kvxxnmugkzy6rch0",
accountNumber: 0,
sequence: 1,
};
Expand Down
5 changes: 4 additions & 1 deletion packages/stargate/src/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ export function parseLogs(input: unknown): readonly Log[] {
return input.map(parseLog);
}

export function parseRawLog(input = "[]"): readonly Log[] {
export function parseRawLog(input: string | undefined): readonly Log[] {
// Cosmos SDK >= 0.50 gives us an empty string here. This should be handled like undefined.
if (!input) return [];

const logsToParse = JSON.parse(input).map(({ events }: { events: readonly unknown[] }, i: number) => ({
msg_index: i,
events,
Expand Down
2 changes: 2 additions & 0 deletions packages/stargate/src/signingstargateclient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ describe("SigningStargateClient", () => {
} else {
expect(result.rawLog).toBeTruthy();
}
expect(result.events.length).toBeGreaterThanOrEqual(1);

// got tokens
const after = await client.getBalance(beneficiaryAddress, "ucosm");
Expand Down Expand Up @@ -167,6 +168,7 @@ describe("SigningStargateClient", () => {
} else {
expect(result.rawLog).toBeTruthy();
}
expect(result.events.length).toBeGreaterThanOrEqual(1);

// got tokens
const after = await client.getBalance(beneficiaryAddress, "ucosm");
Expand Down

0 comments on commit f94b230

Please sign in to comment.