diff --git a/CHANGELOG.md b/CHANGELOG.md index 45c8face..264926d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,10 @@ and this project adheres to - @cosmjs/stargateAdd `StargateClient.broadcastTxSync` and `SigningStargateClient.signAndBroadcastSync` to allow broadcasting without waiting for block inclusion. ([#1396]) +- @cosmjs/cosmwasm-stargate: Add Amino JSON support for + `MsgStoreCode.instantiate_permission`. ([#334]) +[#334]: https://github.com/cosmos/cosmjs/issues/334 [#1266]: https://github.com/cosmos/cosmjs/issues/1266 [#1305]: https://github.com/cosmos/cosmjs/issues/1305 [#1396]: https://github.com/cosmos/cosmjs/pull/1396 diff --git a/packages/cosmwasm-stargate/src/modules/wasm/aminomessages.spec.ts b/packages/cosmwasm-stargate/src/modules/wasm/aminomessages.spec.ts index ea6c9b3a..ffa7bbbd 100644 --- a/packages/cosmwasm-stargate/src/modules/wasm/aminomessages.spec.ts +++ b/packages/cosmwasm-stargate/src/modules/wasm/aminomessages.spec.ts @@ -10,6 +10,7 @@ import { MsgStoreCode, MsgUpdateAdmin, } from "cosmjs-types/cosmwasm/wasm/v1/tx"; +import { AccessType } from "cosmjs-types/cosmwasm/wasm/v1/types"; import Long from "long"; import { @@ -40,6 +41,36 @@ describe("AminoTypes", () => { value: { sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", wasm_byte_code: "WUVMTE9XIFNVQk1BUklORQ==", + instantiate_permission: undefined, + }, + }; + expect(aminoMsg).toEqual(expected); + }); + + it("works for MsgStoreCode with access type", () => { + const msg: MsgStoreCode = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + wasmByteCode: fromBase64("WUVMTE9XIFNVQk1BUklORQ=="), + instantiatePermission: { + permission: AccessType.ACCESS_TYPE_ONLY_ADDRESS, + address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + addresses: [], + }, + }; + const aminoMsg = new AminoTypes(createWasmAminoConverters()).toAmino({ + typeUrl: "/cosmwasm.wasm.v1.MsgStoreCode", + value: msg, + }); + const expected: AminoMsgStoreCode = { + type: "wasm/MsgStoreCode", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + wasm_byte_code: "WUVMTE9XIFNVQk1BUklORQ==", + instantiate_permission: { + permission: "OnlyAddress", + address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + addresses: undefined, + }, }, }; expect(aminoMsg).toEqual(expected); @@ -277,6 +308,35 @@ describe("AminoTypes", () => { }); }); + it("works for MsgStoreCode with access type", () => { + const aminoMsg: AminoMsgStoreCode = { + type: "wasm/MsgStoreCode", + value: { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + wasm_byte_code: "WUVMTE9XIFNVQk1BUklORQ==", + instantiate_permission: { + permission: "OnlyAddress", + address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + addresses: undefined, + }, + }, + }; + const msg = new AminoTypes(createWasmAminoConverters()).fromAmino(aminoMsg); + const expectedValue: MsgStoreCode = { + sender: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + wasmByteCode: fromBase64("WUVMTE9XIFNVQk1BUklORQ=="), + instantiatePermission: { + permission: AccessType.ACCESS_TYPE_ONLY_ADDRESS, + address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", + addresses: [], + }, + }; + expect(msg).toEqual({ + typeUrl: "/cosmwasm.wasm.v1.MsgStoreCode", + value: expectedValue, + }); + }); + it("works for MsgInstantiateContract", () => { // With admin { diff --git a/packages/cosmwasm-stargate/src/modules/wasm/aminomessages.ts b/packages/cosmwasm-stargate/src/modules/wasm/aminomessages.ts index 315ff6c4..0bb180b6 100644 --- a/packages/cosmwasm-stargate/src/modules/wasm/aminomessages.ts +++ b/packages/cosmwasm-stargate/src/modules/wasm/aminomessages.ts @@ -10,13 +10,60 @@ import { MsgStoreCode, MsgUpdateAdmin, } from "cosmjs-types/cosmwasm/wasm/v1/tx"; +import { AccessType } from "cosmjs-types/cosmwasm/wasm/v1/types"; import Long from "long"; -// TODO: implement +export function accessTypeFromString(str: string): AccessType { + switch (str) { + case "Unspecified": + return AccessType.ACCESS_TYPE_UNSPECIFIED; + case "Nobody": + return AccessType.ACCESS_TYPE_NOBODY; + case "OnlyAddress": + return AccessType.ACCESS_TYPE_ONLY_ADDRESS; + case "Everybody": + return AccessType.ACCESS_TYPE_EVERYBODY; + case "AnyOfAddresses": + return AccessType.ACCESS_TYPE_ANY_OF_ADDRESSES; + default: + return AccessType.UNRECOGNIZED; + } +} + +export function accessTypeToString(object: any): string { + switch (object) { + case AccessType.ACCESS_TYPE_UNSPECIFIED: + return "Unspecified"; + case AccessType.ACCESS_TYPE_NOBODY: + return "Nobody"; + case AccessType.ACCESS_TYPE_ONLY_ADDRESS: + return "OnlyAddress"; + case AccessType.ACCESS_TYPE_EVERYBODY: + return "Everybody"; + case AccessType.ACCESS_TYPE_ANY_OF_ADDRESSES: + return "AnyOfAddresses"; + case AccessType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + /** * @see https://github.com/CosmWasm/wasmd/blob/v0.18.0-rc1/proto/cosmwasm/wasm/v1/types.proto#L36-L41 */ -type AccessConfig = never; +export interface AccessConfig { + /** + * Permission should be one kind of string 'Nobody', 'OnlyAddress', 'Everybody', 'AnyOfAddresses', 'Unspecified' + * @see https://github.com/CosmWasm/wasmd/blob/v0.31.0/x/wasm/types/params.go#L54 + */ + readonly permission: string; + /** + * Address + * Deprecated: replaced by addresses + */ + readonly address?: string; + readonly addresses?: string[]; +} /** * The Amino JSON representation of [MsgStoreCode]. @@ -155,14 +202,36 @@ export function createWasmAminoConverters(): AminoConverters { return { "/cosmwasm.wasm.v1.MsgStoreCode": { aminoType: "wasm/MsgStoreCode", - toAmino: ({ sender, wasmByteCode }: MsgStoreCode): AminoMsgStoreCode["value"] => ({ + toAmino: ({ + sender, + wasmByteCode, + instantiatePermission, + }: MsgStoreCode): AminoMsgStoreCode["value"] => ({ sender: sender, wasm_byte_code: toBase64(wasmByteCode), + instantiate_permission: instantiatePermission + ? { + permission: accessTypeToString(instantiatePermission.permission), + address: instantiatePermission.address || undefined, + addresses: + instantiatePermission.addresses.length !== 0 ? instantiatePermission.addresses : undefined, + } + : undefined, }), - fromAmino: ({ sender, wasm_byte_code }: AminoMsgStoreCode["value"]): MsgStoreCode => ({ + fromAmino: ({ + sender, + wasm_byte_code, + instantiate_permission, + }: AminoMsgStoreCode["value"]): MsgStoreCode => ({ sender: sender, wasmByteCode: fromBase64(wasm_byte_code), - instantiatePermission: undefined, + instantiatePermission: instantiate_permission + ? { + permission: accessTypeFromString(instantiate_permission.permission), + address: instantiate_permission.address ?? "", + addresses: instantiate_permission.addresses ?? [], + } + : undefined, }), }, "/cosmwasm.wasm.v1.MsgInstantiateContract": { diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts index 8695248c..55ab0bb8 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts @@ -20,6 +20,7 @@ import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin"; import { MsgDelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx"; import { AuthInfo, TxBody, TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; import { MsgExecuteContract, MsgStoreCode } from "cosmjs-types/cosmwasm/wasm/v1/tx"; +import { AccessConfig, AccessType } from "cosmjs-types/cosmwasm/wasm/v1/types"; import Long from "long"; import pako from "pako"; import protobuf from "protobufjs/minimal"; @@ -125,6 +126,31 @@ describe("SigningCosmWasmClient", () => { expect(codeId).toBeGreaterThanOrEqual(1); client.disconnect(); }); + + it("works with legacy Amino signer access type", async () => { + pendingWithoutWasmd(); + 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); + const wasm = getHackatom().data; + const accessConfig: AccessConfig = { + permission: AccessType.ACCESS_TYPE_EVERYBODY, + address: "", + addresses: [], + }; + const { codeId, checksum, originalSize, compressedSize } = await client.upload( + alice.address0, + wasm, + defaultUploadFee, + "test memo", + accessConfig, + ); + expect(checksum).toEqual(toHex(sha256(wasm))); + expect(originalSize).toEqual(wasm.length); + expect(compressedSize).toBeLessThan(wasm.length * 0.5); + expect(codeId).toBeGreaterThanOrEqual(1); + client.disconnect(); + }); }); describe("instantiate", () => { diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts index e7b219d4..e21b66ed 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts @@ -51,6 +51,7 @@ import { MsgStoreCode, MsgUpdateAdmin, } from "cosmjs-types/cosmwasm/wasm/v1/tx"; +import { AccessConfig } from "cosmjs-types/cosmwasm/wasm/v1/types"; import Long from "long"; import pako from "pako"; @@ -284,6 +285,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { wasmCode: Uint8Array, fee: StdFee | "auto" | number, memo = "", + instantiatePermission?: AccessConfig, ): Promise { const compressed = pako.gzip(wasmCode, { level: 9 }); const storeCodeMsg: MsgStoreCodeEncodeObject = { @@ -291,6 +293,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { value: MsgStoreCode.fromPartial({ sender: senderAddress, wasmByteCode: compressed, + instantiatePermission, }), };