From 1ac7b7581c0876e626d83bf9de258b7601889d25 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Thu, 26 Nov 2020 15:24:22 +0000 Subject: [PATCH 01/15] scripts: Add CW1 contract to launchpad --- scripts/launchpad/contracts/checksums.sha256 | 1 + scripts/launchpad/contracts/cw1_subkeys.wasm | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 scripts/launchpad/contracts/cw1_subkeys.wasm diff --git a/scripts/launchpad/contracts/checksums.sha256 b/scripts/launchpad/contracts/checksums.sha256 index e9600aba45..b441fcd805 100644 --- a/scripts/launchpad/contracts/checksums.sha256 +++ b/scripts/launchpad/contracts/checksums.sha256 @@ -1,3 +1,4 @@ +c478a6d9d6303e67f28d7863ea6e07426e9f0082744557d503e723bc1c46ccf8 cw1_subkeys.wasm 1a4a376ef1099ad3edc33aa1d3105e4621bc49e44b1ac0a449d7b6912e40fb0a cw3_fixed_multisig.wasm ebc2b11e2afa50d5dcd4234840cd581e948a59d888bb8d651598bba3732cd8ee cw-nameservice.wasm d04368320ad55089384adb171aaea39e43d710d7608829adba0300ed30aa2988 cw_erc20.wasm diff --git a/scripts/launchpad/contracts/cw1_subkeys.wasm b/scripts/launchpad/contracts/cw1_subkeys.wasm new file mode 100644 index 0000000000..beeddda4c9 --- /dev/null +++ b/scripts/launchpad/contracts/cw1_subkeys.wasm @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c478a6d9d6303e67f28d7863ea6e07426e9f0082744557d503e723bc1c46ccf8 +size 267814 From 5a1d8a1df079469d73b15059200aa915ac63ea7f Mon Sep 17 00:00:00 2001 From: willclarktech Date: Thu, 26 Nov 2020 15:24:41 +0000 Subject: [PATCH 02/15] scripts: Deploy CW1 contract to launchpad --- scripts/launchpad/deploy_cw1.js | 58 +++++++++++++++++++++++++++++++++ scripts/launchpad/init.sh | 1 + 2 files changed, 59 insertions(+) create mode 100755 scripts/launchpad/deploy_cw1.js diff --git a/scripts/launchpad/deploy_cw1.js b/scripts/launchpad/deploy_cw1.js new file mode 100755 index 0000000000..58de9becfd --- /dev/null +++ b/scripts/launchpad/deploy_cw1.js @@ -0,0 +1,58 @@ +#!/usr/bin/env node + +/* eslint-disable @typescript-eslint/naming-convention */ +const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm"); +const { Secp256k1HdWallet } = require("@cosmjs/launchpad"); +const fs = require("fs"); + +const httpUrl = "http://localhost:1317"; +const alice = { + mnemonic: "enlist hip relief stomach skate base shallow young switch frequent cry park", + address0: "cosmos14qemq0vw6y3gc3u3e0aty2e764u4gs5le3hada", + // address1: "cosmos1hhg2rlu9jscacku2wwckws7932qqqu8x3gfgw0", + // address2: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5", + // address3: "cosmos17yg9mssjenmc3jkqth6ulcwj9cxujrxxzezwta", + // address4: "cosmos1f7j7ryulwjfe9ljplvhtcaxa6wqgula3etktce", +}; + +const codeMeta = { + source: "https://crates.io/api/v1/crates/cw1-subkeys/0.3.1/download", + builder: "cosmwasm/rust-optimizer:0.10.4", +}; + +async function main() { + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet); + + const wasm = fs.readFileSync(__dirname + "/contracts/cw1_subkeys.wasm"); + const uploadReceipt = await client.upload(wasm, codeMeta, "Upload CW1 subkeys contract"); + console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`); + + const initMsg = { + admins: [alice.address0], + mutable: false, + }; + const label = "Subkey test"; + const { contractAddress } = await client.instantiate(uploadReceipt.codeId, initMsg, label, { + memo: `Create a CW1 instance for ${alice.address0}`, + admin: alice.address0, + }); + await client.sendTokens(contractAddress, [ + { + amount: "1000", + denom: "ucosm", + }, + ]); + console.info(`Contract instantiated for ${alice.address0} subkey at ${contractAddress}`); +} + +main().then( + () => { + console.info("All done, let the coins flow."); + process.exit(0); + }, + (error) => { + console.error(error); + process.exit(1); + }, +); diff --git a/scripts/launchpad/init.sh b/scripts/launchpad/init.sh index 7c49cb3eb2..114762503d 100755 --- a/scripts/launchpad/init.sh +++ b/scripts/launchpad/init.sh @@ -27,4 +27,5 @@ SCRIPT_DIR="$(realpath "$(dirname "$0")")" "$SCRIPT_DIR/deploy_hackatom.js" "$SCRIPT_DIR/deploy_erc20.js" "$SCRIPT_DIR/deploy_cw3.js" +"$SCRIPT_DIR/deploy_cw1.js" # "$SCRIPT_DIR/deploy_nameservice.js" From c0c73ca507210c402789b64f792a059f3a2a5569 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Thu, 26 Nov 2020 15:27:21 +0000 Subject: [PATCH 03/15] cosmwasm: Add CW1 contract details to testutils --- packages/cosmwasm/src/testutils.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/cosmwasm/src/testutils.spec.ts b/packages/cosmwasm/src/testutils.spec.ts index f9f12ef6f4..5b2202b83b 100644 --- a/packages/cosmwasm/src/testutils.spec.ts +++ b/packages/cosmwasm/src/testutils.spec.ts @@ -103,6 +103,14 @@ export const deployedCw3 = { ], }; +/** Deployed as part of scripts/launchpad/init.sh */ +export const deployedCw1 = { + codeId: 4, + source: "https://crates.io/api/v1/crates/cw1-subkeys/0.3.1/download", + builder: "cosmwasm/rust-optimizer:0.10.4", + instances: ["cosmos1vs2vuks65rq7xj78mwtvn7vvnm2gn7ad5me0d2"], +}; + export const launchpad = { endpoint: "http://localhost:1317", chainId: "testing", @@ -141,6 +149,16 @@ export function pendingWithoutCw3(): void { } } +export function cw1Enabled(): boolean { + return !!process.env.CW1_ENABLED; +} + +export function pendingWithoutCw1(): void { + if (!cw1Enabled()) { + return pending("Set CW1_ENABLED to enable CW1-based tests"); + } +} + /** Returns first element. Throws if array has a different length than 1. */ export function fromOneElementArray(elements: ArrayLike): T { if (elements.length !== 1) throw new Error(`Expected exactly one element but got ${elements.length}`); From 81f25e9a15ecb3a2c669b20467ee820ff2a411ec Mon Sep 17 00:00:00 2001 From: willclarktech Date: Thu, 26 Nov 2020 15:56:36 +0000 Subject: [PATCH 04/15] cosmwasm: Add Cw1CosmWasmClient class --- packages/cosmwasm/src/cw1cosmwasmclient.ts | 52 +++++++++++++++++++ .../cosmwasm/types/cw1cosmwasmclient.d.ts | 25 +++++++++ 2 files changed, 77 insertions(+) create mode 100644 packages/cosmwasm/src/cw1cosmwasmclient.ts create mode 100644 packages/cosmwasm/types/cw1cosmwasmclient.d.ts diff --git a/packages/cosmwasm/src/cw1cosmwasmclient.ts b/packages/cosmwasm/src/cw1cosmwasmclient.ts new file mode 100644 index 0000000000..7d187133e6 --- /dev/null +++ b/packages/cosmwasm/src/cw1cosmwasmclient.ts @@ -0,0 +1,52 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { Account, BroadcastMode, Coin, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launchpad"; + +import { CosmosMsg } from "./cosmosmsg"; +import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; + +export interface CanSendResult { + readonly can_send: boolean; +} + +export class Cw1CosmWasmClient extends SigningCosmWasmClient { + public readonly cw1ContractAddress: string; + + public constructor( + apiUrl: string, + signerAddress: string, + signer: OfflineSigner, + cw1ContractAddress: string, + gasPrice?: GasPrice, + gasLimits?: Partial>, + broadcastMode?: BroadcastMode, + ) { + super(apiUrl, signerAddress, signer, gasPrice, gasLimits, broadcastMode); + this.cw1ContractAddress = cw1ContractAddress; + } + + public getAccount(address?: string): Promise { + return super.getAccount(address || this.cw1ContractAddress); + } + + public canSend(msg: CosmosMsg, address = this.senderAddress): Promise { + return this.queryContractSmart(this.cw1ContractAddress, { + can_send: { + sender: address, + msg: msg, + }, + }); + } + + public executeSubkey( + msgs: readonly CosmosMsg[], + memo = "", + transferAmount: readonly Coin[] = [], + ): Promise { + const handleMsg = { + execute: { + msgs: msgs, + }, + }; + return this.execute(this.cw1ContractAddress, handleMsg, memo, transferAmount); + } +} diff --git a/packages/cosmwasm/types/cw1cosmwasmclient.d.ts b/packages/cosmwasm/types/cw1cosmwasmclient.d.ts new file mode 100644 index 0000000000..e5f3660692 --- /dev/null +++ b/packages/cosmwasm/types/cw1cosmwasmclient.d.ts @@ -0,0 +1,25 @@ +import { Account, BroadcastMode, Coin, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launchpad"; +import { CosmosMsg } from "./cosmosmsg"; +import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; +export interface CanSendResult { + readonly can_send: boolean; +} +export declare class Cw1CosmWasmClient extends SigningCosmWasmClient { + readonly cw1ContractAddress: string; + constructor( + apiUrl: string, + signerAddress: string, + signer: OfflineSigner, + cw1ContractAddress: string, + gasPrice?: GasPrice, + gasLimits?: Partial>, + broadcastMode?: BroadcastMode, + ); + getAccount(address?: string): Promise; + canSend(msg: CosmosMsg, address?: string): Promise; + executeSubkey( + msgs: readonly CosmosMsg[], + memo?: string, + transferAmount?: readonly Coin[], + ): Promise; +} From a013829460e29faf023fc015d7f688ebaaa3d85d Mon Sep 17 00:00:00 2001 From: willclarktech Date: Thu, 26 Nov 2020 15:58:24 +0000 Subject: [PATCH 05/15] cosmwasm: Add tests for Cw1CosmWasmClient --- .../cosmwasm/src/cw1cosmwasmclient.spec.ts | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 packages/cosmwasm/src/cw1cosmwasmclient.spec.ts diff --git a/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts b/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts new file mode 100644 index 0000000000..c3824ffdaa --- /dev/null +++ b/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts @@ -0,0 +1,139 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { coins, Secp256k1HdWallet } from "@cosmjs/launchpad"; + +import { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; +import { + alice, + deployedCw1, + launchpad, + makeRandomAddress, + pendingWithoutCw1, + pendingWithoutLaunchpad, +} from "./testutils.spec"; + +describe("Cw1CosmWasmClient", () => { + const contractAddress = deployedCw1.instances[0]; + const defaultToAddress = makeRandomAddress(); + const defaultMsg = { + bank: { + send: { + from_address: contractAddress, + to_address: defaultToAddress, + amount: coins(1, "ucosm"), + }, + }, + }; + + describe("constructor", () => { + it("can be constructed", async () => { + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1CosmWasmClient(launchpad.endpoint, alice.address0, wallet, contractAddress); + expect(client).toBeTruthy(); + }); + }); + + describe("canSend", () => { + it("returns true if client signer can send", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1CosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.canSend(defaultMsg); + + expect(result).toEqual({ can_send: true }); + }); + + it("returns false if client signer cannot send", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1CosmWasmClient( + launchpad.endpoint, + alice.address1, + wallet, + deployedCw1.instances[0], + ); + const result = await client.canSend(defaultMsg); + + expect(result).toEqual({ can_send: false }); + }); + + it("returns true if supplied signer can send", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1CosmWasmClient( + launchpad.endpoint, + alice.address1, + wallet, + deployedCw1.instances[0], + ); + const result = await client.canSend(defaultMsg, alice.address0); + + expect(result).toEqual({ can_send: true }); + }); + + it("returns false if supplied signer cannot send", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1CosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.canSend(defaultMsg, alice.address1); + + expect(result).toEqual({ can_send: false }); + }); + }); + + describe("executeSubkey", () => { + it("works", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1CosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.executeSubkey([defaultMsg]); + + expect(result.transactionHash).toBeTruthy(); + }); + + it("works with a transfer", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1CosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const balanceBefore = parseInt((await client.getAccount())!.balance[0].amount, 10); + const memo = "This time with coins"; + const transferAmount = coins(1000, "ucosm"); + const result = await client.executeSubkey([defaultMsg], memo, transferAmount); + const balanceAfter = parseInt((await client.getAccount())!.balance[0].amount, 10); + + expect(result.transactionHash).toBeTruthy(); + expect(balanceAfter).toEqual(balanceBefore + 999); + }); + }); +}); From 43b8c154dbf545080db8545339f9f2d9737d323c Mon Sep 17 00:00:00 2001 From: willclarktech Date: Thu, 26 Nov 2020 17:28:58 +0000 Subject: [PATCH 06/15] cosmwasm: Update Cw1CosmWasmClient for review comments --- .../cosmwasm/src/cw1cosmwasmclient.spec.ts | 29 +++---------------- packages/cosmwasm/src/cw1cosmwasmclient.ts | 21 +++++--------- .../cosmwasm/types/cw1cosmwasmclient.d.ts | 13 ++------- 3 files changed, 14 insertions(+), 49 deletions(-) diff --git a/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts b/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts index c3824ffdaa..471b0e6427 100644 --- a/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts @@ -46,7 +46,7 @@ describe("Cw1CosmWasmClient", () => { ); const result = await client.canSend(defaultMsg); - expect(result).toEqual({ can_send: true }); + expect(result).toEqual(true); }); it("returns false if client signer cannot send", async () => { @@ -62,7 +62,7 @@ describe("Cw1CosmWasmClient", () => { ); const result = await client.canSend(defaultMsg); - expect(result).toEqual({ can_send: false }); + expect(result).toEqual(false); }); it("returns true if supplied signer can send", async () => { @@ -78,7 +78,7 @@ describe("Cw1CosmWasmClient", () => { ); const result = await client.canSend(defaultMsg, alice.address0); - expect(result).toEqual({ can_send: true }); + expect(result).toEqual(true); }); it("returns false if supplied signer cannot send", async () => { @@ -94,7 +94,7 @@ describe("Cw1CosmWasmClient", () => { ); const result = await client.canSend(defaultMsg, alice.address1); - expect(result).toEqual({ can_send: false }); + expect(result).toEqual(false); }); }); @@ -114,26 +114,5 @@ describe("Cw1CosmWasmClient", () => { expect(result.transactionHash).toBeTruthy(); }); - - it("works with a transfer", async () => { - pendingWithoutLaunchpad(); - pendingWithoutCw1(); - - const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); - const client = new Cw1CosmWasmClient( - launchpad.endpoint, - alice.address0, - wallet, - deployedCw1.instances[0], - ); - const balanceBefore = parseInt((await client.getAccount())!.balance[0].amount, 10); - const memo = "This time with coins"; - const transferAmount = coins(1000, "ucosm"); - const result = await client.executeSubkey([defaultMsg], memo, transferAmount); - const balanceAfter = parseInt((await client.getAccount())!.balance[0].amount, 10); - - expect(result.transactionHash).toBeTruthy(); - expect(balanceAfter).toEqual(balanceBefore + 999); - }); }); }); diff --git a/packages/cosmwasm/src/cw1cosmwasmclient.ts b/packages/cosmwasm/src/cw1cosmwasmclient.ts index 7d187133e6..a9c2bf5457 100644 --- a/packages/cosmwasm/src/cw1cosmwasmclient.ts +++ b/packages/cosmwasm/src/cw1cosmwasmclient.ts @@ -1,13 +1,9 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { Account, BroadcastMode, Coin, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launchpad"; +import { Account, BroadcastMode, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launchpad"; import { CosmosMsg } from "./cosmosmsg"; import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; -export interface CanSendResult { - readonly can_send: boolean; -} - export class Cw1CosmWasmClient extends SigningCosmWasmClient { public readonly cw1ContractAddress: string; @@ -24,29 +20,26 @@ export class Cw1CosmWasmClient extends SigningCosmWasmClient { this.cw1ContractAddress = cw1ContractAddress; } - public getAccount(address?: string): Promise { + public async getAccount(address?: string): Promise { return super.getAccount(address || this.cw1ContractAddress); } - public canSend(msg: CosmosMsg, address = this.senderAddress): Promise { - return this.queryContractSmart(this.cw1ContractAddress, { + public async canSend(msg: CosmosMsg, address = this.senderAddress): Promise { + const result = await this.queryContractSmart(this.cw1ContractAddress, { can_send: { sender: address, msg: msg, }, }); + return result.can_send; } - public executeSubkey( - msgs: readonly CosmosMsg[], - memo = "", - transferAmount: readonly Coin[] = [], - ): Promise { + public async executeSubkey(msgs: readonly CosmosMsg[], memo = ""): Promise { const handleMsg = { execute: { msgs: msgs, }, }; - return this.execute(this.cw1ContractAddress, handleMsg, memo, transferAmount); + return this.execute(this.cw1ContractAddress, handleMsg, memo); } } diff --git a/packages/cosmwasm/types/cw1cosmwasmclient.d.ts b/packages/cosmwasm/types/cw1cosmwasmclient.d.ts index e5f3660692..e75e20cc1e 100644 --- a/packages/cosmwasm/types/cw1cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cw1cosmwasmclient.d.ts @@ -1,9 +1,6 @@ -import { Account, BroadcastMode, Coin, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launchpad"; +import { Account, BroadcastMode, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launchpad"; import { CosmosMsg } from "./cosmosmsg"; import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; -export interface CanSendResult { - readonly can_send: boolean; -} export declare class Cw1CosmWasmClient extends SigningCosmWasmClient { readonly cw1ContractAddress: string; constructor( @@ -16,10 +13,6 @@ export declare class Cw1CosmWasmClient extends SigningCosmWasmClient { broadcastMode?: BroadcastMode, ); getAccount(address?: string): Promise; - canSend(msg: CosmosMsg, address?: string): Promise; - executeSubkey( - msgs: readonly CosmosMsg[], - memo?: string, - transferAmount?: readonly Coin[], - ): Promise; + canSend(msg: CosmosMsg, address?: string): Promise; + executeSubkey(msgs: readonly CosmosMsg[], memo?: string): Promise; } From aff7fc509523d9e2fb98788ba243fa63c72120b3 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Thu, 26 Nov 2020 17:35:23 +0000 Subject: [PATCH 07/15] cosmwasm: Export Cw1CosmWasmClient --- packages/cosmwasm/src/index.ts | 1 + packages/cosmwasm/types/index.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/cosmwasm/src/index.ts b/packages/cosmwasm/src/index.ts index b90bf610e8..88cc32d091 100644 --- a/packages/cosmwasm/src/index.ts +++ b/packages/cosmwasm/src/index.ts @@ -17,6 +17,7 @@ export { SearchTxQuery, SearchTxFilter, } from "./cosmwasmclient"; +export { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; export { Cw3CosmWasmClient, Expiration, diff --git a/packages/cosmwasm/types/index.d.ts b/packages/cosmwasm/types/index.d.ts index b90bf610e8..88cc32d091 100644 --- a/packages/cosmwasm/types/index.d.ts +++ b/packages/cosmwasm/types/index.d.ts @@ -17,6 +17,7 @@ export { SearchTxQuery, SearchTxFilter, } from "./cosmwasmclient"; +export { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; export { Cw3CosmWasmClient, Expiration, From fbc5e0660ab03ae0cd34bfb25e208466c0c430ec Mon Sep 17 00:00:00 2001 From: willclarktech Date: Thu, 26 Nov 2020 17:41:09 +0000 Subject: [PATCH 08/15] cosmwasm: Rename Cw1CosmWasmClient.executeSubkey -> executeCw1 --- packages/cosmwasm/src/cw1cosmwasmclient.spec.ts | 4 ++-- packages/cosmwasm/src/cw1cosmwasmclient.ts | 2 +- packages/cosmwasm/types/cw1cosmwasmclient.d.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts b/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts index 471b0e6427..a8997fd15b 100644 --- a/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cw1cosmwasmclient.spec.ts @@ -98,7 +98,7 @@ describe("Cw1CosmWasmClient", () => { }); }); - describe("executeSubkey", () => { + describe("executeCw1", () => { it("works", async () => { pendingWithoutLaunchpad(); pendingWithoutCw1(); @@ -110,7 +110,7 @@ describe("Cw1CosmWasmClient", () => { wallet, deployedCw1.instances[0], ); - const result = await client.executeSubkey([defaultMsg]); + const result = await client.executeCw1([defaultMsg]); expect(result.transactionHash).toBeTruthy(); }); diff --git a/packages/cosmwasm/src/cw1cosmwasmclient.ts b/packages/cosmwasm/src/cw1cosmwasmclient.ts index a9c2bf5457..4905fd92c2 100644 --- a/packages/cosmwasm/src/cw1cosmwasmclient.ts +++ b/packages/cosmwasm/src/cw1cosmwasmclient.ts @@ -34,7 +34,7 @@ export class Cw1CosmWasmClient extends SigningCosmWasmClient { return result.can_send; } - public async executeSubkey(msgs: readonly CosmosMsg[], memo = ""): Promise { + public async executeCw1(msgs: readonly CosmosMsg[], memo = ""): Promise { const handleMsg = { execute: { msgs: msgs, diff --git a/packages/cosmwasm/types/cw1cosmwasmclient.d.ts b/packages/cosmwasm/types/cw1cosmwasmclient.d.ts index e75e20cc1e..b3da0c3237 100644 --- a/packages/cosmwasm/types/cw1cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cw1cosmwasmclient.d.ts @@ -14,5 +14,5 @@ export declare class Cw1CosmWasmClient extends SigningCosmWasmClient { ); getAccount(address?: string): Promise; canSend(msg: CosmosMsg, address?: string): Promise; - executeSubkey(msgs: readonly CosmosMsg[], memo?: string): Promise; + executeCw1(msgs: readonly CosmosMsg[], memo?: string): Promise; } From c274594935f893ae47b8e048bfaf3af483ccdad9 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Fri, 27 Nov 2020 12:18:19 +0000 Subject: [PATCH 09/15] scripts: Make launchpad CW1 contract mutable --- scripts/launchpad/deploy_cw1.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/launchpad/deploy_cw1.js b/scripts/launchpad/deploy_cw1.js index 58de9becfd..5a29333d4b 100755 --- a/scripts/launchpad/deploy_cw1.js +++ b/scripts/launchpad/deploy_cw1.js @@ -30,7 +30,7 @@ async function main() { const initMsg = { admins: [alice.address0], - mutable: false, + mutable: true, }; const label = "Subkey test"; const { contractAddress } = await client.instantiate(uploadReceipt.codeId, initMsg, label, { From c9d5cc50d4e4761c57b8dbf2018c37e9943ed09d Mon Sep 17 00:00:00 2001 From: willclarktech Date: Fri, 27 Nov 2020 12:19:10 +0000 Subject: [PATCH 10/15] cosmwasm: Move Expiration type into types file --- packages/cosmwasm/src/cw3cosmwasmclient.ts | 9 +-------- packages/cosmwasm/src/index.ts | 2 +- packages/cosmwasm/src/types.ts | 12 ++++++++++++ packages/cosmwasm/types/cw3cosmwasmclient.d.ts | 8 +------- packages/cosmwasm/types/index.d.ts | 2 +- packages/cosmwasm/types/types.d.ts | 10 ++++++++++ 6 files changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/cosmwasm/src/cw3cosmwasmclient.ts b/packages/cosmwasm/src/cw3cosmwasmclient.ts index 933c95743e..15f7494131 100644 --- a/packages/cosmwasm/src/cw3cosmwasmclient.ts +++ b/packages/cosmwasm/src/cw3cosmwasmclient.ts @@ -4,14 +4,7 @@ import { BroadcastMode, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launc import { CosmosMsg } from "./cosmosmsg"; import { Account } from "./cosmwasmclient"; import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; - -export type Expiration = - | { - readonly at_height: number; - } - | { - readonly at_time: number; - }; +import { Expiration } from "./types"; export enum Vote { Yes = "yes", diff --git a/packages/cosmwasm/src/index.ts b/packages/cosmwasm/src/index.ts index 88cc32d091..a9151c7dff 100644 --- a/packages/cosmwasm/src/index.ts +++ b/packages/cosmwasm/src/index.ts @@ -20,7 +20,6 @@ export { export { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; export { Cw3CosmWasmClient, - Expiration, ProposalResult, ProposalsResult, ThresholdResult, @@ -54,3 +53,4 @@ export { MsgUpdateAdmin, MsgStoreCode, } from "./msgs"; +export { Expiration } from "./types"; diff --git a/packages/cosmwasm/src/types.ts b/packages/cosmwasm/src/types.ts index bba7fc95f7..0d0ba0bad5 100644 --- a/packages/cosmwasm/src/types.ts +++ b/packages/cosmwasm/src/types.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { fromBase64, fromHex } from "@cosmjs/encoding"; export interface WasmData { @@ -25,3 +26,14 @@ export function parseWasmData({ key, val }: WasmData): Model { * This doen't privide any type safety over `any` but expresses intent in the code. */ export type JsonObject = any; + +export type Expiration = + | { + readonly at_height: number; + } + | { + readonly at_time: number; + } + | { + readonly never: Record; + }; diff --git a/packages/cosmwasm/types/cw3cosmwasmclient.d.ts b/packages/cosmwasm/types/cw3cosmwasmclient.d.ts index 80ff14fca2..fdc964781b 100644 --- a/packages/cosmwasm/types/cw3cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cw3cosmwasmclient.d.ts @@ -2,13 +2,7 @@ import { BroadcastMode, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launc import { CosmosMsg } from "./cosmosmsg"; import { Account } from "./cosmwasmclient"; import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; -export declare type Expiration = - | { - readonly at_height: number; - } - | { - readonly at_time: number; - }; +import { Expiration } from "./types"; export declare enum Vote { Yes = "yes", No = "no", diff --git a/packages/cosmwasm/types/index.d.ts b/packages/cosmwasm/types/index.d.ts index 88cc32d091..a9151c7dff 100644 --- a/packages/cosmwasm/types/index.d.ts +++ b/packages/cosmwasm/types/index.d.ts @@ -20,7 +20,6 @@ export { export { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; export { Cw3CosmWasmClient, - Expiration, ProposalResult, ProposalsResult, ThresholdResult, @@ -54,3 +53,4 @@ export { MsgUpdateAdmin, MsgStoreCode, } from "./msgs"; +export { Expiration } from "./types"; diff --git a/packages/cosmwasm/types/types.d.ts b/packages/cosmwasm/types/types.d.ts index 45d870c725..33928389f4 100644 --- a/packages/cosmwasm/types/types.d.ts +++ b/packages/cosmwasm/types/types.d.ts @@ -12,3 +12,13 @@ export declare function parseWasmData({ key, val }: WasmData): Model; * This doen't privide any type safety over `any` but expresses intent in the code. */ export declare type JsonObject = any; +export declare type Expiration = + | { + readonly at_height: number; + } + | { + readonly at_time: number; + } + | { + readonly never: Record; + }; From 60c812c7600626ad4a31b36f4294be7470b45927 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Fri, 27 Nov 2020 12:24:32 +0000 Subject: [PATCH 11/15] cosmwasm: Add Cw1SubkeyCosmWasmClient class --- .../cosmwasm/src/cw1subkeycosmwasmclient.ts | 123 ++++++++++++++++++ .../types/cw1subkeycosmwasmclient.d.ts | 38 ++++++ 2 files changed, 161 insertions(+) create mode 100644 packages/cosmwasm/src/cw1subkeycosmwasmclient.ts create mode 100644 packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts diff --git a/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts b/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts new file mode 100644 index 0000000000..a9a4529700 --- /dev/null +++ b/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts @@ -0,0 +1,123 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { Coin } from "@cosmjs/launchpad"; + +import { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; +import { ExecuteResult } from "./signingcosmwasmclient"; +import { Expiration } from "./types"; + +export interface AllowanceResult { + readonly balance: readonly Coin[]; + readonly expires: Expiration; +} + +export interface Cw1SubkeyPermissions { + readonly delegate: boolean; + readonly redelegate: boolean; + readonly undelegate: boolean; + readonly withdraw: boolean; +} + +export class Cw1SubkeyCosmWasmClient extends Cw1CosmWasmClient { + private async setAdmins(admins: readonly string[], memo = ""): Promise { + const handleMsg = { + update_admins: { + admins: admins, + }, + }; + return this.execute(this.cw1ContractAddress, handleMsg, memo); + } + + public async getAdmins(): Promise { + const { admins } = await this.queryContractSmart(this.cw1ContractAddress, { admin_list: {} }); + return admins; + } + + public async isAdmin(address = this.senderAddress): Promise { + const admins = await this.getAdmins(); + return admins.includes(address); + } + + public async getAllAllowances(): Promise { + const { allowances } = await this.queryContractSmart(this.cw1ContractAddress, { + all_allowances: {}, + }); + return allowances; + } + + public async getAllowance(address = this.senderAddress): Promise { + return this.queryContractSmart(this.cw1ContractAddress, { + allowance: { spender: address }, + }); + } + + public async getAllPermissions(): Promise { + const { permissions } = await this.queryContractSmart(this.cw1ContractAddress, { + all_permissions: {}, + }); + return permissions; + } + + public async getPermissions(address = this.senderAddress): Promise { + return this.queryContractSmart(this.cw1ContractAddress, { + permissions: { spender: address }, + }); + } + + public async addAdmin(address: string, memo = ""): Promise { + const admins = await this.getAdmins(); + const newAdmins = admins.includes(address) ? admins : [...admins, address]; + return this.setAdmins(newAdmins, memo); + } + + public async removeAdmin(address: string, memo = ""): Promise { + const admins = await this.getAdmins(); + const newAdmins = admins.filter((admin) => admin !== address); + return this.setAdmins(newAdmins, memo); + } + + public async increaseAllowance( + address: string, + amount: Coin, + expires?: Expiration, + memo = "", + ): Promise { + const handleMsg = { + increase_allowance: { + spender: address, + amount: amount, + expires: expires, + }, + }; + return this.execute(this.cw1ContractAddress, handleMsg, memo); + } + + public async decreaseAllowance( + address: string, + amount: Coin, + expires?: Expiration, + memo = "", + ): Promise { + const handleMsg = { + decrease_allowance: { + spender: address, + amount: amount, + expires: expires, + }, + }; + return this.execute(this.cw1ContractAddress, handleMsg, memo); + } + + public async setPermissions( + address: string, + permissions: Cw1SubkeyPermissions, + memo = "", + ): Promise { + const handleMsg = { + set_permissions: { + spender: address, + permissions: permissions, + }, + }; + return this.execute(this.cw1ContractAddress, handleMsg, memo); + } +} diff --git a/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts b/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts new file mode 100644 index 0000000000..0e7c1ef584 --- /dev/null +++ b/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts @@ -0,0 +1,38 @@ +import { Coin } from "@cosmjs/launchpad"; +import { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; +import { ExecuteResult } from "./signingcosmwasmclient"; +import { Expiration } from "./types"; +export interface AllowanceResult { + readonly balance: readonly Coin[]; + readonly expires: Expiration; +} +export interface Cw1SubkeyPermissions { + readonly delegate: boolean; + readonly redelegate: boolean; + readonly undelegate: boolean; + readonly withdraw: boolean; +} +export declare class Cw1SubkeyCosmWasmClient extends Cw1CosmWasmClient { + private setAdmins; + getAdmins(): Promise; + isAdmin(address?: string): Promise; + getAllAllowances(): Promise; + getAllowance(address?: string): Promise; + getAllPermissions(): Promise; + getPermissions(address?: string): Promise; + addAdmin(address: string, memo?: string): Promise; + removeAdmin(address: string, memo?: string): Promise; + increaseAllowance( + address: string, + amount: Coin, + expires?: Expiration, + memo?: string, + ): Promise; + decreaseAllowance( + address: string, + amount: Coin, + expires?: Expiration, + memo?: string, + ): Promise; + setPermissions(address: string, permissions: Cw1SubkeyPermissions, memo?: string): Promise; +} From d8ed695ae569576d1f10835319d37a3af4f57700 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Fri, 27 Nov 2020 12:24:46 +0000 Subject: [PATCH 12/15] cosmwasm: Add Cw1SubkeyCosmWasmClient tests --- .../src/cw1subkeycosmwasmclient.spec.ts | 322 ++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 packages/cosmwasm/src/cw1subkeycosmwasmclient.spec.ts diff --git a/packages/cosmwasm/src/cw1subkeycosmwasmclient.spec.ts b/packages/cosmwasm/src/cw1subkeycosmwasmclient.spec.ts new file mode 100644 index 0000000000..1761edf685 --- /dev/null +++ b/packages/cosmwasm/src/cw1subkeycosmwasmclient.spec.ts @@ -0,0 +1,322 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { coin, coins, Secp256k1HdWallet } from "@cosmjs/launchpad"; + +import { Cw1SubkeyCosmWasmClient } from "./cw1subkeycosmwasmclient"; +import { + alice, + deployedCw1, + launchpad, + makeRandomAddress, + pendingWithoutCw1, + pendingWithoutLaunchpad, +} from "./testutils.spec"; + +describe("Cw1SubkeyCosmWasmClient", () => { + describe("getAdmins", () => { + it("works", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.getAdmins(); + + expect(result).toEqual([alice.address0]); + }); + }); + + describe("isAdmin", () => { + it("returns true if client signer is admin", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.isAdmin(); + + expect(result).toEqual(true); + }); + + it("returns false if client signer is not admin", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address1, + wallet, + deployedCw1.instances[0], + ); + const result = await client.isAdmin(); + + expect(result).toEqual(false); + }); + + it("returns true if supplied signer is admin", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address1, + wallet, + deployedCw1.instances[0], + ); + const result = await client.isAdmin(alice.address0); + + expect(result).toEqual(true); + }); + + it("returns false if supplied signer is not admin", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.isAdmin(alice.address1); + + expect(result).toEqual(false); + }); + }); + + describe("getAllAllowances", () => { + it("works", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.getAllAllowances(); + expect(result).toBeTruthy(); + }); + }); + + describe("getAllowance", () => { + it("works for client signer", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.getAllowance(); + + expect(result).toEqual({ balance: [], expires: { never: {} } }); + }); + }); + + describe("getAllPermissions", () => { + it("works", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.getAllPermissions(); + expect(result).toBeTruthy(); + }); + }); + + describe("getPermissions", () => { + it("works for client signer", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.getPermissions(); + + expect(result).toEqual({ + delegate: false, + redelegate: false, + undelegate: false, + withdraw: false, + }); + }); + + it("works for supplied signer", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const result = await client.getPermissions(alice.address1); + + expect(result).toEqual({ + delegate: false, + redelegate: false, + undelegate: false, + withdraw: false, + }); + }); + }); + + describe("addAdmin and removeAdmin", () => { + it("works", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const newAdmin = makeRandomAddress(); + expect(await client.isAdmin(newAdmin)).toBeFalse(); + + const addResult = await client.addAdmin(newAdmin); + expect(addResult.transactionHash).toBeTruthy(); + expect(await client.isAdmin(newAdmin)).toBeTrue(); + + const removeResult = await client.removeAdmin(newAdmin); + expect(removeResult.transactionHash).toBeTruthy(); + expect(await client.isAdmin(newAdmin)).toBeFalse(); + }); + }); + + describe("increaseAllowance and decreaseAllowance", () => { + it("works", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const spender = makeRandomAddress(); + expect(await client.getAllowance(spender)).toEqual({ balance: [], expires: { never: {} } }); + + const increaseAmount = coin(100, "ucosm"); + const increaseResult = await client.increaseAllowance(spender, increaseAmount); + expect(increaseResult.transactionHash).toBeTruthy(); + expect(await client.getAllowance(spender)).toEqual({ + balance: [increaseAmount], + expires: { never: {} }, + }); + + const decreaseAmount = coin(20, "ucosm"); + const decreaseResult = await client.decreaseAllowance(spender, decreaseAmount); + expect(decreaseResult.transactionHash).toBeTruthy(); + expect(await client.getAllowance(spender)).toEqual({ + balance: coins(80, "ucosm"), + expires: { never: {} }, + }); + }); + + it("works with expiration", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const spender = makeRandomAddress(); + expect(await client.getAllowance(spender)).toEqual({ balance: [], expires: { never: {} } }); + + const increaseAmount = coin(100, "ucosm"); + const increaseExpiration = { at_height: 88888888888 }; + const increaseResult = await client.increaseAllowance(spender, increaseAmount, increaseExpiration); + expect(increaseResult.transactionHash).toBeTruthy(); + expect(await client.getAllowance(spender)).toEqual({ + balance: [increaseAmount], + expires: increaseExpiration, + }); + + const decreaseAmount = coin(20, "ucosm"); + const decreaseExpiration = { at_height: 99999999999 }; + const decreaseResult = await client.decreaseAllowance(spender, decreaseAmount, decreaseExpiration); + expect(decreaseResult.transactionHash).toBeTruthy(); + expect(await client.getAllowance(spender)).toEqual({ + balance: coins(80, "ucosm"), + expires: decreaseExpiration, + }); + }); + }); + + describe("setPermissions", () => { + it("works", async () => { + pendingWithoutLaunchpad(); + pendingWithoutCw1(); + + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic); + const client = new Cw1SubkeyCosmWasmClient( + launchpad.endpoint, + alice.address0, + wallet, + deployedCw1.instances[0], + ); + const spender = makeRandomAddress(); + const defaultPermissions = { + delegate: false, + redelegate: false, + undelegate: false, + withdraw: false, + }; + expect(await client.getPermissions(spender)).toEqual(defaultPermissions); + + const newPermissions = { + delegate: true, + redelegate: true, + undelegate: true, + withdraw: false, + }; + const setPermissionsResult = await client.setPermissions(spender, newPermissions); + expect(setPermissionsResult.transactionHash).toBeTruthy(); + expect(await client.getPermissions(spender)).toEqual(newPermissions); + + const resetPermissionsResult = await client.setPermissions(spender, defaultPermissions); + expect(resetPermissionsResult.transactionHash).toBeTruthy(); + expect(await client.getPermissions(spender)).toEqual(defaultPermissions); + }); + }); +}); From 8e1cb34312cde768dc59dcab407b507b7eaa8270 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 30 Nov 2020 14:47:36 +0100 Subject: [PATCH 13/15] Move Expiration to cw0 module and add link to source --- packages/cosmwasm/src/cw1subkeycosmwasmclient.ts | 2 +- packages/cosmwasm/src/cw3cosmwasmclient.ts | 2 +- packages/cosmwasm/src/index.ts | 3 ++- packages/cosmwasm/src/interfaces/cw0.ts | 15 +++++++++++++++ packages/cosmwasm/src/interfaces/index.ts | 1 + packages/cosmwasm/src/types.ts | 12 ------------ .../cosmwasm/types/cw1subkeycosmwasmclient.d.ts | 2 +- packages/cosmwasm/types/cw3cosmwasmclient.d.ts | 2 +- packages/cosmwasm/types/index.d.ts | 2 +- packages/cosmwasm/types/interfaces/cw0.d.ts | 13 +++++++++++++ packages/cosmwasm/types/interfaces/index.d.ts | 1 + packages/cosmwasm/types/types.d.ts | 10 ---------- 12 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 packages/cosmwasm/src/interfaces/cw0.ts create mode 100644 packages/cosmwasm/src/interfaces/index.ts create mode 100644 packages/cosmwasm/types/interfaces/cw0.d.ts create mode 100644 packages/cosmwasm/types/interfaces/index.d.ts diff --git a/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts b/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts index a9a4529700..8f55228b3c 100644 --- a/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts +++ b/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts @@ -2,8 +2,8 @@ import { Coin } from "@cosmjs/launchpad"; import { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; +import { Expiration } from "./interfaces"; import { ExecuteResult } from "./signingcosmwasmclient"; -import { Expiration } from "./types"; export interface AllowanceResult { readonly balance: readonly Coin[]; diff --git a/packages/cosmwasm/src/cw3cosmwasmclient.ts b/packages/cosmwasm/src/cw3cosmwasmclient.ts index 15f7494131..6603bc7c1d 100644 --- a/packages/cosmwasm/src/cw3cosmwasmclient.ts +++ b/packages/cosmwasm/src/cw3cosmwasmclient.ts @@ -3,8 +3,8 @@ import { BroadcastMode, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launc import { CosmosMsg } from "./cosmosmsg"; import { Account } from "./cosmwasmclient"; +import { Expiration } from "./interfaces"; import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; -import { Expiration } from "./types"; export enum Vote { Yes = "yes", diff --git a/packages/cosmwasm/src/index.ts b/packages/cosmwasm/src/index.ts index a9151c7dff..692d9bbce7 100644 --- a/packages/cosmwasm/src/index.ts +++ b/packages/cosmwasm/src/index.ts @@ -1,4 +1,6 @@ +export { Expiration } from "./interfaces"; export { setupWasmExtension, WasmExtension } from "./lcdapi/wasm"; + export { BankMsg, CosmosMsg, CustomMsg, StakingMsg, WasmMsg } from "./cosmosmsg"; export { Account, @@ -53,4 +55,3 @@ export { MsgUpdateAdmin, MsgStoreCode, } from "./msgs"; -export { Expiration } from "./types"; diff --git a/packages/cosmwasm/src/interfaces/cw0.ts b/packages/cosmwasm/src/interfaces/cw0.ts new file mode 100644 index 0000000000..17d098dd29 --- /dev/null +++ b/packages/cosmwasm/src/interfaces/cw0.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/naming-convention */ + +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/packages/cw0/src/expiration.rs#L14 + */ +export type Expiration = + | { + readonly at_height: number; + } + | { + readonly at_time: number; + } + | { + readonly never: Record; + }; diff --git a/packages/cosmwasm/src/interfaces/index.ts b/packages/cosmwasm/src/interfaces/index.ts new file mode 100644 index 0000000000..b91ff756b6 --- /dev/null +++ b/packages/cosmwasm/src/interfaces/index.ts @@ -0,0 +1 @@ +export { Expiration } from "./cw0"; diff --git a/packages/cosmwasm/src/types.ts b/packages/cosmwasm/src/types.ts index 0d0ba0bad5..bba7fc95f7 100644 --- a/packages/cosmwasm/src/types.ts +++ b/packages/cosmwasm/src/types.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import { fromBase64, fromHex } from "@cosmjs/encoding"; export interface WasmData { @@ -26,14 +25,3 @@ export function parseWasmData({ key, val }: WasmData): Model { * This doen't privide any type safety over `any` but expresses intent in the code. */ export type JsonObject = any; - -export type Expiration = - | { - readonly at_height: number; - } - | { - readonly at_time: number; - } - | { - readonly never: Record; - }; diff --git a/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts b/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts index 0e7c1ef584..015829fd24 100644 --- a/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts @@ -1,7 +1,7 @@ import { Coin } from "@cosmjs/launchpad"; import { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; +import { Expiration } from "./interfaces"; import { ExecuteResult } from "./signingcosmwasmclient"; -import { Expiration } from "./types"; export interface AllowanceResult { readonly balance: readonly Coin[]; readonly expires: Expiration; diff --git a/packages/cosmwasm/types/cw3cosmwasmclient.d.ts b/packages/cosmwasm/types/cw3cosmwasmclient.d.ts index fdc964781b..21b63d460c 100644 --- a/packages/cosmwasm/types/cw3cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cw3cosmwasmclient.d.ts @@ -1,8 +1,8 @@ import { BroadcastMode, GasLimits, GasPrice, OfflineSigner } from "@cosmjs/launchpad"; import { CosmosMsg } from "./cosmosmsg"; import { Account } from "./cosmwasmclient"; +import { Expiration } from "./interfaces"; import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; -import { Expiration } from "./types"; export declare enum Vote { Yes = "yes", No = "no", diff --git a/packages/cosmwasm/types/index.d.ts b/packages/cosmwasm/types/index.d.ts index a9151c7dff..071e93518c 100644 --- a/packages/cosmwasm/types/index.d.ts +++ b/packages/cosmwasm/types/index.d.ts @@ -1,3 +1,4 @@ +export { Expiration } from "./interfaces"; export { setupWasmExtension, WasmExtension } from "./lcdapi/wasm"; export { BankMsg, CosmosMsg, CustomMsg, StakingMsg, WasmMsg } from "./cosmosmsg"; export { @@ -53,4 +54,3 @@ export { MsgUpdateAdmin, MsgStoreCode, } from "./msgs"; -export { Expiration } from "./types"; diff --git a/packages/cosmwasm/types/interfaces/cw0.d.ts b/packages/cosmwasm/types/interfaces/cw0.d.ts new file mode 100644 index 0000000000..1a9bae4326 --- /dev/null +++ b/packages/cosmwasm/types/interfaces/cw0.d.ts @@ -0,0 +1,13 @@ +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/packages/cw0/src/expiration.rs#L14 + */ +export declare type Expiration = + | { + readonly at_height: number; + } + | { + readonly at_time: number; + } + | { + readonly never: Record; + }; diff --git a/packages/cosmwasm/types/interfaces/index.d.ts b/packages/cosmwasm/types/interfaces/index.d.ts new file mode 100644 index 0000000000..b91ff756b6 --- /dev/null +++ b/packages/cosmwasm/types/interfaces/index.d.ts @@ -0,0 +1 @@ +export { Expiration } from "./cw0"; diff --git a/packages/cosmwasm/types/types.d.ts b/packages/cosmwasm/types/types.d.ts index 33928389f4..45d870c725 100644 --- a/packages/cosmwasm/types/types.d.ts +++ b/packages/cosmwasm/types/types.d.ts @@ -12,13 +12,3 @@ export declare function parseWasmData({ key, val }: WasmData): Model; * This doen't privide any type safety over `any` but expresses intent in the code. */ export declare type JsonObject = any; -export declare type Expiration = - | { - readonly at_height: number; - } - | { - readonly at_time: number; - } - | { - readonly never: Record; - }; From f4bd386081d3b358b341a8f3463c521c6aa4d391 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 30 Nov 2020 14:53:15 +0100 Subject: [PATCH 14/15] Add link to Vote source --- packages/cosmwasm/src/cw3cosmwasmclient.ts | 3 +++ packages/cosmwasm/types/cw3cosmwasmclient.d.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/cosmwasm/src/cw3cosmwasmclient.ts b/packages/cosmwasm/src/cw3cosmwasmclient.ts index 6603bc7c1d..ef8efb9d5f 100644 --- a/packages/cosmwasm/src/cw3cosmwasmclient.ts +++ b/packages/cosmwasm/src/cw3cosmwasmclient.ts @@ -6,6 +6,9 @@ import { Account } from "./cosmwasmclient"; import { Expiration } from "./interfaces"; import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/packages/cw3/src/msg.rs#L35 + */ export enum Vote { Yes = "yes", No = "no", diff --git a/packages/cosmwasm/types/cw3cosmwasmclient.d.ts b/packages/cosmwasm/types/cw3cosmwasmclient.d.ts index 21b63d460c..b0fe42201a 100644 --- a/packages/cosmwasm/types/cw3cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cw3cosmwasmclient.d.ts @@ -3,6 +3,9 @@ import { CosmosMsg } from "./cosmosmsg"; import { Account } from "./cosmwasmclient"; import { Expiration } from "./interfaces"; import { CosmWasmFeeTable, ExecuteResult, SigningCosmWasmClient } from "./signingcosmwasmclient"; +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/packages/cw3/src/msg.rs#L35 + */ export declare enum Vote { Yes = "yes", No = "no", From 8cbad1a46fb3677cae34c70d5b9337c22ab5502d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 30 Nov 2020 15:10:00 +0100 Subject: [PATCH 15/15] Improve type safety for Multisig contract responses. --- .../src/cw1subkeycosmwasmclient.spec.ts | 3 +- .../cosmwasm/src/cw1subkeycosmwasmclient.ts | 45 +++++++++++++++---- .../types/cw1subkeycosmwasmclient.d.ts | 22 +++++++-- 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/packages/cosmwasm/src/cw1subkeycosmwasmclient.spec.ts b/packages/cosmwasm/src/cw1subkeycosmwasmclient.spec.ts index 1761edf685..2054fbf65d 100644 --- a/packages/cosmwasm/src/cw1subkeycosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cw1subkeycosmwasmclient.spec.ts @@ -144,7 +144,8 @@ describe("Cw1SubkeyCosmWasmClient", () => { deployedCw1.instances[0], ); const result = await client.getAllPermissions(); - expect(result).toBeTruthy(); + expect(result.length).toEqual(1); + // TODO: test content of permissions }); }); diff --git a/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts b/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts index 8f55228b3c..87aa92dea9 100644 --- a/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts +++ b/packages/cosmwasm/src/cw1subkeycosmwasmclient.ts @@ -5,11 +5,24 @@ import { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; import { Expiration } from "./interfaces"; import { ExecuteResult } from "./signingcosmwasmclient"; -export interface AllowanceResult { +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/contracts/cw1-subkeys/src/msg.rs#L88 + */ +export interface Cw1SubkeyAllowanceInfo { readonly balance: readonly Coin[]; readonly expires: Expiration; } +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/contracts/cw1-subkeys/src/msg.rs#L83 + */ +interface AllAllowancesResponse { + readonly allowances: readonly Cw1SubkeyAllowanceInfo[]; +} + +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/contracts/cw1-subkeys/src/state.rs#L15 + */ export interface Cw1SubkeyPermissions { readonly delegate: boolean; readonly redelegate: boolean; @@ -17,6 +30,22 @@ export interface Cw1SubkeyPermissions { readonly withdraw: boolean; } +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/contracts/cw1-subkeys/src/msg.rs#L95 + */ +export interface Cw1SubkeyPermissionsInfo { + /** Spender address */ + readonly spender: string; + readonly permissions: readonly Cw1SubkeyPermissions[]; +} + +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/contracts/cw1-subkeys/src/msg.rs#L101 + */ +interface AllPermissionsResponse { + readonly permissions: readonly Cw1SubkeyPermissionsInfo[]; +} + export class Cw1SubkeyCosmWasmClient extends Cw1CosmWasmClient { private async setAdmins(admins: readonly string[], memo = ""): Promise { const handleMsg = { @@ -37,24 +66,24 @@ export class Cw1SubkeyCosmWasmClient extends Cw1CosmWasmClient { return admins.includes(address); } - public async getAllAllowances(): Promise { - const { allowances } = await this.queryContractSmart(this.cw1ContractAddress, { + public async getAllAllowances(): Promise { + const response: AllAllowancesResponse = await this.queryContractSmart(this.cw1ContractAddress, { all_allowances: {}, }); - return allowances; + return response.allowances; } - public async getAllowance(address = this.senderAddress): Promise { + public async getAllowance(address = this.senderAddress): Promise { return this.queryContractSmart(this.cw1ContractAddress, { allowance: { spender: address }, }); } - public async getAllPermissions(): Promise { - const { permissions } = await this.queryContractSmart(this.cw1ContractAddress, { + public async getAllPermissions(): Promise { + const response: AllPermissionsResponse = await this.queryContractSmart(this.cw1ContractAddress, { all_permissions: {}, }); - return permissions; + return response.permissions; } public async getPermissions(address = this.senderAddress): Promise { diff --git a/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts b/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts index 015829fd24..da665871f0 100644 --- a/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cw1subkeycosmwasmclient.d.ts @@ -2,23 +2,37 @@ import { Coin } from "@cosmjs/launchpad"; import { Cw1CosmWasmClient } from "./cw1cosmwasmclient"; import { Expiration } from "./interfaces"; import { ExecuteResult } from "./signingcosmwasmclient"; -export interface AllowanceResult { +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/contracts/cw1-subkeys/src/msg.rs#L88 + */ +export interface Cw1SubkeyAllowanceInfo { readonly balance: readonly Coin[]; readonly expires: Expiration; } +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/contracts/cw1-subkeys/src/state.rs#L15 + */ export interface Cw1SubkeyPermissions { readonly delegate: boolean; readonly redelegate: boolean; readonly undelegate: boolean; readonly withdraw: boolean; } +/** + * @see https://github.com/CosmWasm/cosmwasm-plus/blob/v0.3.2/contracts/cw1-subkeys/src/msg.rs#L95 + */ +export interface Cw1SubkeyPermissionsInfo { + /** Spender address */ + readonly spender: string; + readonly permissions: readonly Cw1SubkeyPermissions[]; +} export declare class Cw1SubkeyCosmWasmClient extends Cw1CosmWasmClient { private setAdmins; getAdmins(): Promise; isAdmin(address?: string): Promise; - getAllAllowances(): Promise; - getAllowance(address?: string): Promise; - getAllPermissions(): Promise; + getAllAllowances(): Promise; + getAllowance(address?: string): Promise; + getAllPermissions(): Promise; getPermissions(address?: string): Promise; addAdmin(address: string, memo?: string): Promise; removeAdmin(address: string, memo?: string): Promise;