diff --git a/CHANGELOG.md b/CHANGELOG.md index da09a6b9..44bbe903 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ and this project adheres to connection tx, as well as Tendermint. - @cosmjs/stargate: Add support for IBC message types in `SigningStargateClient`. +- @cosmjs/stargate: Added new `logs` export with all the functionality from + @cosmjs/launchpad. +- @cosmjs/stargate: Added new `Coin`, `coin`, `coins` and `parseCoins` exports + which have the same functionality as already existed in @cosmjs/launchpad. - @cosmjs/amino: New package created that contains the shared amino signing functionality for @cosmjs/launchpad and @cosmjs/stargate. - @cosmjs/amino: Split public key interfaces into `Pubkey`, `SinglePubkey` and @@ -31,6 +35,9 @@ and this project adheres to single secp256k1 pubkey. - @cosmjs/utils: The new `arrayContentStartsWith` works similar to `arrayContentEquals` but only checks the start of an array. +- @cosmjs/proto-signing: Added new `Coin`, `coin`, `coins` and `parseCoins` + exports which have the same functionality as already existed in + @cosmjs/launchpad. ### Changed @@ -52,6 +59,7 @@ and this project adheres to - @cosmjs/launchpad: `rawSecp256k1PubkeyToAddress` was removed. Instead use `Bech32.encode(prefix, rawSecp256k1PubkeyToRawAddress(pubkeyRaw))` with `rawSecp256k1PubkeyToRawAddress` from @cosmjs/amino. +- @cosmjs/stargate: `parseRawLog` is now nested under the `logs` export. ### Deprecated diff --git a/packages/amino/package.json b/packages/amino/package.json index 0df89f2b..5242f1f5 100644 --- a/packages/amino/package.json +++ b/packages/amino/package.json @@ -42,8 +42,8 @@ "dependencies": { "@cosmjs/crypto": "^0.25.0-alpha.0", "@cosmjs/encoding": "^0.25.0-alpha.0", + "@cosmjs/math": "^0.25.0-alpha.0", "@cosmjs/utils": "^0.25.0-alpha.0" }, - "devDependencies": { - } + "devDependencies": {} } diff --git a/packages/cosmwasm-stargate/src/aminotypes.spec.ts b/packages/cosmwasm-stargate/src/aminotypes.spec.ts index c182fe8f..2ea1f32a 100644 --- a/packages/cosmwasm-stargate/src/aminotypes.spec.ts +++ b/packages/cosmwasm-stargate/src/aminotypes.spec.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { fromBase64, toUtf8 } from "@cosmjs/encoding"; -import { coins } from "@cosmjs/launchpad"; -import { AminoTypes } from "@cosmjs/stargate"; +import { AminoTypes, coins } from "@cosmjs/stargate"; import Long from "long"; import { diff --git a/packages/cosmwasm-stargate/src/aminotypes.ts b/packages/cosmwasm-stargate/src/aminotypes.ts index 55553fc7..ce4bff4e 100644 --- a/packages/cosmwasm-stargate/src/aminotypes.ts +++ b/packages/cosmwasm-stargate/src/aminotypes.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { fromBase64, fromUtf8, toBase64, toUtf8 } from "@cosmjs/encoding"; -import { Coin } from "@cosmjs/launchpad"; -import { AminoConverter, coinFromProto } from "@cosmjs/stargate"; +import { AminoConverter, Coin, coinFromProto } from "@cosmjs/stargate"; import { assertDefinedAndNotNull } from "@cosmjs/utils"; import Long from "long"; diff --git a/packages/cosmwasm-stargate/src/cosmwasmclient.searchtx.spec.ts b/packages/cosmwasm-stargate/src/cosmwasmclient.searchtx.spec.ts index fb054e1d..36c215d2 100644 --- a/packages/cosmwasm-stargate/src/cosmwasmclient.searchtx.spec.ts +++ b/packages/cosmwasm-stargate/src/cosmwasmclient.searchtx.spec.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { fromBase64, toBase64 } from "@cosmjs/encoding"; -import { Coin, coins } from "@cosmjs/launchpad"; import { DirectSecp256k1HdWallet, encodePubkey, @@ -8,7 +7,13 @@ import { makeSignDoc, Registry, } from "@cosmjs/proto-signing"; -import { BroadcastTxResponse, isBroadcastTxFailure, isBroadcastTxSuccess } from "@cosmjs/stargate"; +import { + BroadcastTxResponse, + Coin, + coins, + isBroadcastTxFailure, + isBroadcastTxSuccess, +} from "@cosmjs/stargate"; import { Tx, TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx"; import { assert, sleep } from "@cosmjs/utils"; diff --git a/packages/cosmwasm-stargate/src/cosmwasmclient.spec.ts b/packages/cosmwasm-stargate/src/cosmwasmclient.spec.ts index 25104ebb..ef941b9c 100644 --- a/packages/cosmwasm-stargate/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm-stargate/src/cosmwasmclient.spec.ts @@ -2,7 +2,7 @@ import { Code } from "@cosmjs/cosmwasm-launchpad"; import { sha256 } from "@cosmjs/crypto"; import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64 } from "@cosmjs/encoding"; -import { coins, logs, StdFee } from "@cosmjs/launchpad"; +import { StdFee } from "@cosmjs/launchpad"; import { Int53 } from "@cosmjs/math"; import { DirectSecp256k1HdWallet, @@ -11,7 +11,7 @@ import { makeSignDoc, Registry, } from "@cosmjs/proto-signing"; -import { assertIsBroadcastTxSuccess, parseRawLog } from "@cosmjs/stargate"; +import { assertIsBroadcastTxSuccess, coins, logs } from "@cosmjs/stargate"; import { TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx"; import { assert, sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; @@ -210,7 +210,7 @@ describe("CosmWasmClient", () => { const signedTx = Uint8Array.from(TxRaw.encode(txRaw).finish()); const result = await client.broadcastTx(signedTx); assertIsBroadcastTxSuccess(result); - const amountAttr = logs.findAttribute(parseRawLog(result.rawLog), "transfer", "amount"); + const amountAttr = logs.findAttribute(logs.parseRawLog(result.rawLog), "transfer", "amount"); expect(amountAttr.value).toEqual("1234567ucosm"); expect(result.transactionHash).toMatch(/^[0-9A-F]{64}$/); }); diff --git a/packages/cosmwasm-stargate/src/cosmwasmclient.ts b/packages/cosmwasm-stargate/src/cosmwasmclient.ts index 3d7ec1e9..1669bd19 100644 --- a/packages/cosmwasm-stargate/src/cosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/cosmwasmclient.ts @@ -9,7 +9,6 @@ import { import { fromAscii, toHex } from "@cosmjs/encoding"; import { Block, - Coin, isSearchByHeightQuery, isSearchBySentFromOrToQuery, isSearchByTagsQuery, @@ -23,6 +22,7 @@ import { AuthExtension, BankExtension, BroadcastTxResponse, + Coin, coinFromProto, IndexedTx, QueryClient, diff --git a/packages/cosmwasm-stargate/src/queries/wasm.spec.ts b/packages/cosmwasm-stargate/src/queries/wasm.spec.ts index 8adcbb88..fd647cd8 100644 --- a/packages/cosmwasm-stargate/src/queries/wasm.spec.ts +++ b/packages/cosmwasm-stargate/src/queries/wasm.spec.ts @@ -1,12 +1,15 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { sha256 } from "@cosmjs/crypto"; import { fromAscii, fromHex, toAscii, toHex } from "@cosmjs/encoding"; -import { Coin, coin, coins, logs, StdFee } from "@cosmjs/launchpad"; +import { StdFee } from "@cosmjs/launchpad"; import { DirectSecp256k1HdWallet, OfflineDirectSigner, Registry } from "@cosmjs/proto-signing"; import { assertIsBroadcastTxSuccess, BroadcastTxResponse, - parseRawLog, + Coin, + coin, + coins, + logs, SigningStargateClient, } from "@cosmjs/stargate"; import { assert, assertDefined } from "@cosmjs/utils"; @@ -392,7 +395,7 @@ describe("WasmExtension", () => { { const result = await uploadContract(wallet, getHackatom()); assertIsBroadcastTxSuccess(result); - const parsedLogs = logs.parseLogs(parseRawLog(result.rawLog)); + const parsedLogs = logs.parseLogs(logs.parseRawLog(result.rawLog)); const codeIdAttr = logs.findAttribute(parsedLogs, "message", "code_id"); codeId = Number.parseInt(codeIdAttr.value, 10); expect(codeId).toBeGreaterThanOrEqual(1); @@ -412,7 +415,7 @@ describe("WasmExtension", () => { { const result = await instantiateContract(wallet, codeId, beneficiaryAddress, transferAmount); assertIsBroadcastTxSuccess(result); - const parsedLogs = logs.parseLogs(parseRawLog(result.rawLog)); + const parsedLogs = logs.parseLogs(logs.parseRawLog(result.rawLog)); const contractAddressAttr = logs.findAttribute(parsedLogs, "message", "contract_address"); contractAddress = contractAddressAttr.value; const amountAttr = logs.findAttribute(parsedLogs, "transfer", "amount"); @@ -437,7 +440,7 @@ describe("WasmExtension", () => { { const result = await executeContract(wallet, contractAddress, { release: {} }); assertIsBroadcastTxSuccess(result); - const parsedLogs = logs.parseLogs(parseRawLog(result.rawLog)); + const parsedLogs = logs.parseLogs(logs.parseRawLog(result.rawLog)); const wasmEvent = parsedLogs.find(() => true)?.events.find((e) => e.type === "wasm"); assert(wasmEvent, "Event of type wasm expected"); expect(wasmEvent.attributes).toContain({ key: "action", value: "release" }); diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts index baf2788e..3d8c818c 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts @@ -2,15 +2,9 @@ import { UploadMeta } from "@cosmjs/cosmwasm-launchpad"; import { sha256 } from "@cosmjs/crypto"; import { toHex } from "@cosmjs/encoding"; -import { - coin, - coins, - GasPrice, - MsgDelegate as LaunchpadMsgDelegate, - Secp256k1HdWallet, -} from "@cosmjs/launchpad"; +import { GasPrice, MsgDelegate as LaunchpadMsgDelegate, Secp256k1HdWallet } from "@cosmjs/launchpad"; import { DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing"; -import { AminoTypes, assertIsBroadcastTxSuccess } from "@cosmjs/stargate"; +import { AminoTypes, assertIsBroadcastTxSuccess, coin, coins } from "@cosmjs/stargate"; import { DeepPartial, MsgSend } from "@cosmjs/stargate/build/codec/cosmos/bank/v1beta1/tx"; import { Coin } from "@cosmjs/stargate/build/codec/cosmos/base/v1beta1/coin"; import { MsgDelegate } from "@cosmjs/stargate/build/codec/cosmos/staking/v1beta1/tx"; diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts index 7b008c5e..5890cbd8 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts @@ -15,11 +15,9 @@ import { sha256 } from "@cosmjs/crypto"; import { fromBase64, toHex, toUtf8 } from "@cosmjs/encoding"; import { buildFeeTable, - Coin, CosmosFeeTable, GasLimits, GasPrice, - logs, makeSignDoc as makeSignDocAmino, StdFee, } from "@cosmjs/launchpad"; @@ -37,9 +35,10 @@ import { AminoTypes, BroadcastTxFailure, BroadcastTxResponse, + Coin, defaultRegistryTypes, isBroadcastTxFailure, - parseRawLog, + logs, } from "@cosmjs/stargate"; import { SignMode } from "@cosmjs/stargate/build/codec/cosmos/tx/signing/v1beta1/signing"; import { TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx"; @@ -165,7 +164,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { if (isBroadcastTxFailure(result)) { throw new Error(createBroadcastTxErrorMessage(result)); } - const parsedLogs = parseRawLog(result.rawLog); + const parsedLogs = logs.parseRawLog(result.rawLog); const codeIdAttr = logs.findAttribute(parsedLogs, "message", "code_id"); return { originalSize: wasmCode.length, @@ -200,7 +199,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { if (isBroadcastTxFailure(result)) { throw new Error(createBroadcastTxErrorMessage(result)); } - const parsedLogs = parseRawLog(result.rawLog); + const parsedLogs = logs.parseRawLog(result.rawLog); const contractAddressAttr = logs.findAttribute(parsedLogs, "message", "contract_address"); return { contractAddress: contractAddressAttr.value, @@ -228,7 +227,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { throw new Error(createBroadcastTxErrorMessage(result)); } return { - logs: parseRawLog(result.rawLog), + logs: logs.parseRawLog(result.rawLog), transactionHash: result.transactionHash, }; } @@ -250,7 +249,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { throw new Error(createBroadcastTxErrorMessage(result)); } return { - logs: parseRawLog(result.rawLog), + logs: logs.parseRawLog(result.rawLog), transactionHash: result.transactionHash, }; } @@ -276,7 +275,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { throw new Error(createBroadcastTxErrorMessage(result)); } return { - logs: parseRawLog(result.rawLog), + logs: logs.parseRawLog(result.rawLog), transactionHash: result.transactionHash, }; } @@ -302,7 +301,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { throw new Error(createBroadcastTxErrorMessage(result)); } return { - logs: parseRawLog(result.rawLog), + logs: logs.parseRawLog(result.rawLog), transactionHash: result.transactionHash, }; } diff --git a/packages/cosmwasm-stargate/src/testutils.spec.ts b/packages/cosmwasm-stargate/src/testutils.spec.ts index 704b6aae..d89ff171 100644 --- a/packages/cosmwasm-stargate/src/testutils.spec.ts +++ b/packages/cosmwasm-stargate/src/testutils.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Bip39, EnglishMnemonic, Random, Secp256k1, Slip10, Slip10Curve } from "@cosmjs/crypto"; import { Bech32, fromBase64 } from "@cosmjs/encoding"; -import { AminoSignResponse, coins, Secp256k1HdWallet, StdSignDoc } from "@cosmjs/launchpad"; +import { AminoSignResponse, Secp256k1HdWallet, StdSignDoc } from "@cosmjs/launchpad"; import { DirectSecp256k1HdWallet, DirectSignResponse, @@ -11,6 +11,7 @@ import { import { AuthExtension, BankExtension, + coins, QueryClient, setupAuthExtension, setupBankExtension, diff --git a/packages/proto-signing/src/coins.spec.ts b/packages/proto-signing/src/coins.spec.ts new file mode 100644 index 00000000..5f74c4cb --- /dev/null +++ b/packages/proto-signing/src/coins.spec.ts @@ -0,0 +1,107 @@ +import { coin, coins, parseCoins } from "./coins"; + +describe("coins", () => { + describe("coin", () => { + it("works for basic values", () => { + expect(coin(123, "utoken")).toEqual({ amount: "123", denom: "utoken" }); + expect(coin(123.0, "utoken")).toEqual({ amount: "123", denom: "utoken" }); + expect(coin(Number.MAX_SAFE_INTEGER, "utoken")).toEqual({ + amount: "9007199254740991", + denom: "utoken", + }); + expect(coin(+0, "utoken")).toEqual({ amount: "0", denom: "utoken" }); + expect(coin(-0, "utoken")).toEqual({ amount: "0", denom: "utoken" }); + }); + + it("throws for non-safe-integer values", () => { + expect(() => coin(1.23, "utoken")).toThrow(); + expect(() => coin(NaN, "utoken")).toThrow(); + expect(() => coin(Number.POSITIVE_INFINITY, "utoken")).toThrow(); + expect(() => coin(Number.MAX_SAFE_INTEGER + 1, "utoken")).toThrow(); + }); + + it("throws for negative values", () => { + expect(() => coin(-1, "utoken")).toThrow(); + expect(() => coin(Number.MIN_SAFE_INTEGER, "utoken")).toThrow(); + expect(() => coin(Number.NEGATIVE_INFINITY, "utoken")).toThrow(); + }); + }); + + describe("coins", () => { + it("returns one element array of coin", () => { + expect(coins(123, "utoken")).toEqual([{ amount: "123", denom: "utoken" }]); + }); + }); + + describe("parseCoins", () => { + it("works for empty", () => { + expect(parseCoins("")).toEqual([]); + }); + + it("works for one element", () => { + expect(parseCoins("7643ureef")).toEqual([ + { + amount: "7643", + denom: "ureef", + }, + ]); + }); + + it("works for two", () => { + expect(parseCoins("819966000ucosm,700000000ustake")).toEqual([ + { + amount: "819966000", + denom: "ucosm", + }, + { + amount: "700000000", + denom: "ustake", + }, + ]); + }); + + it("ignores empty elements", () => { + // start + expect(parseCoins(",819966000ucosm,700000000ustake")).toEqual([ + { + amount: "819966000", + denom: "ucosm", + }, + { + amount: "700000000", + denom: "ustake", + }, + ]); + // middle + expect(parseCoins("819966000ucosm,,700000000ustake")).toEqual([ + { + amount: "819966000", + denom: "ucosm", + }, + { + amount: "700000000", + denom: "ustake", + }, + ]); + // end + expect(parseCoins("819966000ucosm,700000000ustake,")).toEqual([ + { + amount: "819966000", + denom: "ucosm", + }, + { + amount: "700000000", + denom: "ustake", + }, + ]); + }); + + it("throws for invalid inputs", () => { + // denom missing + expect(() => parseCoins("3456")).toThrowError(/invalid coin string/i); + + // amount missing + expect(() => parseCoins("ucosm")).toThrowError(/invalid coin string/i); + }); + }); +}); diff --git a/packages/proto-signing/src/coins.ts b/packages/proto-signing/src/coins.ts new file mode 100644 index 00000000..ec6881c0 --- /dev/null +++ b/packages/proto-signing/src/coins.ts @@ -0,0 +1,47 @@ +import { Uint53, Uint64 } from "@cosmjs/math"; + +/** + * This is the same as Coin from @cosmjs/launchpad but those might diverge in the future. + */ +export interface Coin { + readonly denom: string; + readonly amount: string; +} + +/** + * Creates a coin. + * + * This is the same as coin from @cosmjs/launchpad but those might diverge in the future. + */ +export function coin(amount: number, denom: string): Coin { + return { amount: new Uint53(amount).toString(), denom: denom }; +} + +/** + * Creates a list of coins with one element + * + * This is the same as coins from @cosmjs/launchpad but those might diverge in the future. + */ +export function coins(amount: number, denom: string): Coin[] { + return [coin(amount, denom)]; +} + +/** + * Takes a coins list like "819966000ucosm,700000000ustake" and parses it + * + * This is the same as parseCoins from @cosmjs/launchpad but those might diverge in the future. + */ +export function parseCoins(input: string): Coin[] { + return input + .replace(/\s/g, "") + .split(",") + .filter(Boolean) + .map((part) => { + const match = part.match(/^([0-9]+)([a-zA-Z]+)/); + if (!match) throw new Error("Got an invalid coin string"); + return { + amount: Uint64.fromString(match[1]).toString(), + denom: match[2], + }; + }); +} diff --git a/packages/proto-signing/src/index.ts b/packages/proto-signing/src/index.ts index 0b11ea43..56518dd2 100644 --- a/packages/proto-signing/src/index.ts +++ b/packages/proto-signing/src/index.ts @@ -1,3 +1,4 @@ +export { Coin, coin, coins, parseCoins } from "./coins"; export { isPbjsGeneratedType, isTsProtoGeneratedType, diff --git a/packages/stargate/src/aminotypes.spec.ts b/packages/stargate/src/aminotypes.spec.ts index bf598e3c..abc71747 100644 --- a/packages/stargate/src/aminotypes.spec.ts +++ b/packages/stargate/src/aminotypes.spec.ts @@ -2,8 +2,6 @@ import { encodeBech32Pubkey } from "@cosmjs/amino"; import { fromBase64 } from "@cosmjs/encoding"; import { - coin, - coins, MsgBeginRedelegate as LaunchpadMsgBeginRedelegate, MsgCreateValidator as LaunchpadMsgCreateValidator, MsgDelegate as LaunchpadMsgDelegate, @@ -16,6 +14,7 @@ import { MsgWithdrawDelegatorReward as LaunchpadMsgWithdrawDelegatorReward, MsgWithdrawValidatorCommission as LaunchpadMsgWithdrawValidatorCommission, } from "@cosmjs/launchpad"; +import { coin, coins } from "@cosmjs/proto-signing"; import { AminoTypes } from "./aminotypes"; import { MsgMultiSend, MsgSend } from "./codec/cosmos/bank/v1beta1/tx"; diff --git a/packages/stargate/src/index.ts b/packages/stargate/src/index.ts index c3ae5f49..c05e786f 100644 --- a/packages/stargate/src/index.ts +++ b/packages/stargate/src/index.ts @@ -1,6 +1,8 @@ +export { Coin, coin, coins, parseCoins } from "@cosmjs/proto-signing"; + export { Account, accountFromAny } from "./accounts"; export { AminoConverter, AminoTypes } from "./aminotypes"; -export { parseRawLog } from "./logs"; +export * as logs from "./logs"; export { AuthExtension, BankExtension, diff --git a/packages/stargate/src/logs.ts b/packages/stargate/src/logs.ts index 2738b412..91be9109 100644 --- a/packages/stargate/src/logs.ts +++ b/packages/stargate/src/logs.ts @@ -1,11 +1,91 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { logs } from "@cosmjs/launchpad"; +import { isNonNullObject } from "@cosmjs/utils"; -export function parseRawLog(input = "[]"): readonly logs.Log[] { +export interface Attribute { + readonly key: string; + readonly value: string; +} + +export interface Event { + readonly type: string; + readonly attributes: readonly Attribute[]; +} + +export interface Log { + readonly msg_index: number; + readonly log: string; + readonly events: readonly Event[]; +} + +export function parseAttribute(input: unknown): Attribute { + if (!isNonNullObject(input)) throw new Error("Attribute must be a non-null object"); + const { key, value } = input as any; + if (typeof key !== "string" || !key) throw new Error("Attribute's key must be a non-empty string"); + if (typeof value !== "string" && typeof value !== "undefined") { + throw new Error("Attribute's value must be a string or unset"); + } + + return { + key: key, + value: value || "", + }; +} + +export function parseEvent(input: unknown): Event { + if (!isNonNullObject(input)) throw new Error("Event must be a non-null object"); + const { type, attributes } = input as any; + if (typeof type !== "string" || type === "") { + throw new Error(`Event type must be a non-empty string`); + } + if (!Array.isArray(attributes)) throw new Error("Event's attributes must be an array"); + return { + type: type, + attributes: attributes.map(parseAttribute), + }; +} + +export function parseLog(input: unknown): Log { + if (!isNonNullObject(input)) throw new Error("Log must be a non-null object"); + const { msg_index, log, events } = input as any; + if (typeof msg_index !== "number") throw new Error("Log's msg_index must be a number"); + if (typeof log !== "string") throw new Error("Log's log must be a string"); + if (!Array.isArray(events)) throw new Error("Log's events must be an array"); + return { + msg_index: msg_index, + log: log, + events: events.map(parseEvent), + }; +} + +export function parseLogs(input: unknown): readonly Log[] { + if (!Array.isArray(input)) throw new Error("Logs must be an array"); + return input.map(parseLog); +} + +export function parseRawLog(input = "[]"): readonly Log[] { const logsToParse = JSON.parse(input).map(({ events }: { events: readonly unknown[] }, i: number) => ({ msg_index: i, events, log: "", })); - return logs.parseLogs(logsToParse); + return parseLogs(logsToParse); +} + +/** + * Searches in logs for the first event of the given event type and in that event + * for the first first attribute with the given attribute key. + * + * Throws if the attribute was not found. + */ +export function findAttribute(logs: readonly Log[], eventType: string, attrKey: string): Attribute { + const firstLogs = logs.find(() => true); + const out = firstLogs?.events + .find((event) => event.type === eventType) + ?.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; } diff --git a/packages/stargate/src/queries/distribution.spec.ts b/packages/stargate/src/queries/distribution.spec.ts index c35c969c..880b14bb 100644 --- a/packages/stargate/src/queries/distribution.spec.ts +++ b/packages/stargate/src/queries/distribution.spec.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { coin, coins } from "@cosmjs/launchpad"; -import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; +import { coin, coins, DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import { Tendermint34Client } from "@cosmjs/tendermint-rpc"; import { sleep } from "@cosmjs/utils"; diff --git a/packages/stargate/src/queries/staking.spec.ts b/packages/stargate/src/queries/staking.spec.ts index f925e680..b624becf 100644 --- a/packages/stargate/src/queries/staking.spec.ts +++ b/packages/stargate/src/queries/staking.spec.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { coin, coins } from "@cosmjs/launchpad"; -import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; +import { coin, coins, DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import { Tendermint34Client } from "@cosmjs/tendermint-rpc"; import { sleep } from "@cosmjs/utils"; diff --git a/packages/stargate/src/signingstargateclient.spec.ts b/packages/stargate/src/signingstargateclient.spec.ts index 9db79896..bed17b38 100644 --- a/packages/stargate/src/signingstargateclient.spec.ts +++ b/packages/stargate/src/signingstargateclient.spec.ts @@ -1,12 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention,no-bitwise */ -import { - coin, - coins, - GasPrice, - MsgDelegate as LaunchpadMsgDelegate, - Secp256k1HdWallet, -} from "@cosmjs/launchpad"; -import { DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing"; +import { GasPrice, MsgDelegate as LaunchpadMsgDelegate, Secp256k1HdWallet } from "@cosmjs/launchpad"; +import { coin, coins, DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing"; import { assert, sleep } from "@cosmjs/utils"; import protobuf from "protobufjs/minimal"; diff --git a/packages/stargate/src/signingstargateclient.ts b/packages/stargate/src/signingstargateclient.ts index 08d70484..5df8ba42 100644 --- a/packages/stargate/src/signingstargateclient.ts +++ b/packages/stargate/src/signingstargateclient.ts @@ -2,7 +2,6 @@ import { encodeSecp256k1Pubkey } from "@cosmjs/amino"; import { fromBase64 } from "@cosmjs/encoding"; import { buildFeeTable, - Coin, CosmosFeeTable, GasLimits, GasPrice, @@ -24,6 +23,7 @@ import { Tendermint34Client } from "@cosmjs/tendermint-rpc"; import { AminoTypes } from "./aminotypes"; import { MsgMultiSend } from "./codec/cosmos/bank/v1beta1/tx"; +import { Coin } from "./codec/cosmos/base/v1beta1/coin"; import { MsgFundCommunityPool, MsgSetWithdrawAddress, diff --git a/packages/stargate/src/stargateclient.searchtx.spec.ts b/packages/stargate/src/stargateclient.searchtx.spec.ts index a43dcde2..bc4da931 100644 --- a/packages/stargate/src/stargateclient.searchtx.spec.ts +++ b/packages/stargate/src/stargateclient.searchtx.spec.ts @@ -1,6 +1,6 @@ import { fromBase64, toBase64 } from "@cosmjs/encoding"; -import { Coin, coins } from "@cosmjs/launchpad"; import { + coins, DirectSecp256k1HdWallet, encodePubkey, makeAuthInfoBytes, @@ -9,6 +9,7 @@ import { } from "@cosmjs/proto-signing"; import { assert, sleep } from "@cosmjs/utils"; +import { Coin } from "./codec/cosmos/base/v1beta1/coin"; import { Tx, TxRaw } from "./codec/cosmos/tx/v1beta1/tx"; import { BroadcastTxResponse, diff --git a/packages/stargate/src/testutils.spec.ts b/packages/stargate/src/testutils.spec.ts index 93328416..f98fc7fc 100644 --- a/packages/stargate/src/testutils.spec.ts +++ b/packages/stargate/src/testutils.spec.ts @@ -1,8 +1,9 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Bip39, EnglishMnemonic, Random, Secp256k1, Slip10, Slip10Curve } from "@cosmjs/crypto"; import { Bech32 } from "@cosmjs/encoding"; -import { AminoSignResponse, coins, Secp256k1HdWallet, StdSignDoc } from "@cosmjs/launchpad"; +import { AminoSignResponse, Secp256k1HdWallet, StdSignDoc } from "@cosmjs/launchpad"; import { + coins, DirectSecp256k1HdWallet, DirectSignResponse, makeAuthInfoBytes,