diff --git a/.changeset/blue-pillows-think.md b/.changeset/blue-pillows-think.md new file mode 100644 index 00000000..a845151c --- /dev/null +++ b/.changeset/blue-pillows-think.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/long-hats-jam.md b/.changeset/long-hats-jam.md new file mode 100644 index 00000000..6a0f6aac --- /dev/null +++ b/.changeset/long-hats-jam.md @@ -0,0 +1,5 @@ +--- +'@confio/relayer': patch +--- + +Added the handling of begin and end block events diff --git a/CHANGELOG.md b/CHANGELOG.md index 85e77e3c..74563e8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.1.6 + +### Patch Changes + +- a4c9a31: Update relayer demo to use oysternet and nyancat +- 2a955ea: Add home option to ics20 command +- 9e4a5c3: Update deps, faster polling for tests + ## 0.1.5 ### Patch Changes diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index c864d103..1d6bbf22 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -4,20 +4,24 @@ See [docs for starter pack](https://www.npmjs.com/package/typescript-starter). ### Set up local chains -To start two local blockchains, so we can test, run the following commands in two different consoles. +To start two local blockchains, so we can test, run the following commands in three different consoles. +These are long-living commands and will keep running indefinitely ```sh # in one console ./scripts/simapp/start.sh # in another console ./scripts/wasmd/start.sh +# in yet another console +./scripts/gaia/start.sh ``` -When you are done, you can run the following in any console: +When you are done running tests, you can run the following in any console to shut down the chains: ```sh ./scripts/simapp/stop.sh ./scripts/wasmd/stop.sh +./scripts/gaia/stop.sh ``` ### Setup binaries diff --git a/README.md b/README.md index 7c2fd1bc..f35a1fc1 100644 --- a/README.md +++ b/README.md @@ -61,31 +61,41 @@ Reads the configuration and starts relaying packets. 1. Init the configuration ```sh - ibc-setup init --src relayer_test_1 --dest relayer_test_2 + ibc-setup init --src oysternet --dest nyancat ``` - creates relayer's home directory at `~/.ibc-setup` - creates `app.yaml` inside relayer's home with `src`, `dest` and newly generated `mnemonic` - pulls default `registry.yaml` to relayer's home - - funds addresses on both sides so relayer can pay the fee while relaying packets + - funds addresses on `oysternet` so relayer can pay the fee while relaying packets - > **NOTE:** Test blockchains `relayer_test_1` and `relayer_test_2` are running in the public. You do not need to start any blockchain locally to complete the quick start guide. + > **NOTE:** Both testnets are running in the public. You do not need to start any blockchain locally to complete the quick start guide. > **NOTE:** Run `ibc-setup balances` to see the amount of tokens on each address. -2. Create `ics20` channel +2. Get testnet tokens for `nyancat` + + - Find your relayer address on nyancat via: `ibc-setup keys list | grep nyancat` + - Join IRISnet discord with [this invite link](https://discord.gg/X6dZZxs3#nyncat-faucet) + - Go to the `nyancat-faucet` channel + - Request tokens at this address in the above channel: `$faucet iaa1fxmqew9dgg44jdf3l34zwa8rx7tcf42wz8ehjk` + - Check you have tokens on oysternet and nyancat via `ibc-setup balances` + + [Original Instructions from IRISnet](https://github.com/irisnet/testnets/tree/master/nyancat#faucet) + +3. Create `ics20` channel ```sh - ibc-setup ics20 + ibc-setup ics20 -v ``` - creates a new connection on source and desination chains - saves connection ids to `app.yaml` file - creates a new channel -3. Start the relayer in the verbose mode and 10s frequency polling +4. Start the relayer in the verbose mode and 10s frequency polling ```sh - ibc-relayer start -v --poll 10 + ibc-relayer start -v --poll 15 ``` ### Send tokens between chains @@ -102,29 +112,44 @@ Reads the configuration and starts relaying packets. make install ``` -2. Create a new account and fund it +2. Make sure `iris` binary is installed on your system + + - you must be running Linux or OSX on amd64 + - [install Go 1.15+](https://golang.org/doc/install) and ensure that `$PATH` includes Go binaries (you may need to restart your terminal session) + - clone and install `iris`: + ```sh + git clone https://github.com/irisnet/irishub + cd irishub + git checkout v1.1.1 + make install + ``` + +3. Create a new account and fund it ```sh wasmd keys add sender - JSON=$(jq -n --arg addr $(wasmd keys show -a sender) '{"denom":"umuon","address":$addr}') - curl -X POST --header "Content-Type: application/json" --data "$JSON" http://49.12.73.189:8001/credit + JSON=$(jq -n --arg addr $(wasmd keys show -a sender) '{"denom":"usponge","address":$addr}') + curl -X POST --header "Content-Type: application/json" --data "$JSON" https://faucet.oysternet.cosmwasm.com/credit ``` -3. Create another account to send tokens to +4. Create a valid IRISnet address to send tokens to + ```sh - wasmd keys add receiver + iris keys add receiver ``` -4. Send tokens + + [Get testnet tokens](https://github.com/irisnet/testnets/tree/master/nyancat#faucet) if you want to send tokens to `oysternet`. + +5. Send tokens ```sh - wasmd tx ibc-transfer transfer transfer $(wasmd keys show -a receiver) 200umuon --from $(wasmd keys show -a sender) --node http://168.119.254.205:26657 --chain-id network-1 --fees 2000umuon + wasmd tx ibc-transfer transfer transfer $(iris keys show -a receiver) 200usponge --from $(wasmd keys show -a sender) --node http://rpc.oysternet.cosmwasm.com:80 --chain-id oysternet-1 --fees 2000usponge --packet-timeout-height 0-0 ``` - replace `` with the channel id obtained while configuring the relayer (2nd point) - if you cleared out the terminal, query the channel ```sh - # replace `connection-id` with value of `srcConnection` property from `~/.ibc-setup/app.yaml` file - ibc-setup channels --chain relayer_test_1 --connection + ibc-setup channels --chain oysternet ``` -5. Observe the relayer output +6. Observe the relayer output ## Configuration overview @@ -200,9 +225,9 @@ The `GET /metrics` endpoint will be exposed by default on port `8080`, which you ## Chain Requirements -The blockchain must be based on Cosmos SDK `v0.41.1+`. In particular it must have -[PR 8458](https://github.com/cosmos/cosmos-sdk/pull/8458) merged (if you are using a fork) -in order for the relayer to work properly. `ibc-setup` should work on `v0.40.0+` +The blockchain must be based on Cosmos SDK `v0.42.4+`. In particular it must have +[PR 8458](https://github.com/cosmos/cosmos-sdk/pull/8458) and [PR 9081](https://github.com/cosmos/cosmos-sdk/pull/9081) +merged (if you are using a fork) in order for the relayer to work properly. `ibc-setup` should work on `v0.40.0+` The chain must have a large value for `staking.params.historical_entries` (often set in genesis). The default is "10000" and this should work with "1000", but no relayer will work if it is set to 0. diff --git a/demo/registry.yaml b/demo/registry.yaml index f57648c5..b01cd6e3 100644 --- a/demo/registry.yaml +++ b/demo/registry.yaml @@ -1,24 +1,24 @@ version: 1 chains: - musselnet: - chain_id: musselnet-4 + oysternet: + chain_id: oysternet-1 # Bech32 prefix for addresses prefix: wasm # This determines the gas payments we make (and defines the fee token) - gas_price: 0.025umayo + gas_price: 0.025usponge # The path we use to derive the private key from the mnemonic # Note: The hd paths shown have no meaningful relationship to the existing chains. # It is recommended practice to use a different hd_path than those commonly used for user accounts. hd_path: m/44'/1234'/0'/1' # If you include an optional faucet, it will load the relayer with tokens in `ibc-setup init` - faucet: https://faucet.musselnet.cosmwasm.com + faucet: https://faucet.oysternet.cosmwasm.com # You can optionally define a default ics20_port that will be used instead of transfer if no flags set ics20_port: 'transfer' # You can include multiple RPC endpoints and it will rotate through them if # one is down (TODO) rpc: - - https://rpc.musselnet.cosmwasm.com + - http://rpc.oysternet.cosmwasm.com:80 local_wasm: chain_id: testing prefix: wasm @@ -34,24 +34,6 @@ chains: ics20_port: 'custom' rpc: - http://localhost:26658 - relayer_test_1: - chain_id: network-1 - prefix: wasm - gas_price: 0.01umuon - hd_path: m/44'/1234'/0'/4' - ics20_port: 'transfer' - faucet: http://49.12.73.189:8001 - rpc: - - http://168.119.254.205:26657 - relayer_test_2: - chain_id: network-2 - prefix: wasm - gas_price: 0.01umuon - hd_path: m/44'/1234'/0'/5' - ics20_port: 'transfer' - faucet: http://49.12.73.189:8002 - rpc: - - http://188.34.162.78:26657 cosmoshub-4: chain_id: cosmoshub-4 prefix: cosmos @@ -63,3 +45,12 @@ chains: ics20_port: 'transfer' rpc: - https://rpc.cosmos.network:443 + nyancat: + # This is a long-living testnet by IrisNet + chain_id: nyancat-8 + prefix: iaa + gas_price: 0.2unyan + hd_path: m/44'/118'/0'/0/0 + ics20_port: 'transfer' + rpc: + - http://34.80.202.172:26657/ diff --git a/package.json b/package.json index 854f3024..28452607 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@confio/relayer", - "version": "0.1.5", + "version": "0.1.6", "description": "IBC Relayer in TypeScript", "repository": "https://github.com/confio/ts-relayer", "license": "MIT", @@ -8,13 +8,13 @@ "scripts": { "build": "tsc -p tsconfig.json", "fix": "run-s fix:*", - "fix:prettier": "prettier \"**/*.{ts,md}\" --write", + "fix:prettier": "prettier \"**/*.{ts,md}\" \"scripts/wasmd/*.js\" --write", "fix:lint": "eslint src --ext .ts --fix", "test": "run-s build test:*", "test:lint": "eslint src --ext .ts", - "test:prettier": "prettier \"**/*.{ts,md}\" --list-different", + "test:prettier": "prettier \"**/*.{ts,md}\" \"scripts/wasmd/*.js\" --list-different", "test:unit": "nyc --silent ava --serial", - "focused-test": "yarn test:unit ./src/lib/link.spec.ts", + "focused-test": "yarn test:unit ./src/lib/cosmwasm.spec.ts", "watch:build": "tsc -p tsconfig.json -w", "watch:test": "nyc --silent ava --watch --serial", "cov": "run-s build test:unit cov:html cov:lcov && open-cli coverage/index.html", @@ -28,15 +28,16 @@ "node": ">=12" }, "dependencies": { - "@cosmjs/crypto": "0.25.3", - "@cosmjs/encoding": "0.25.3", - "@cosmjs/faucet-client": "0.25.3", - "@cosmjs/math": "0.25.3", - "@cosmjs/proto-signing": "0.25.3", - "@cosmjs/stargate": "0.25.3", - "@cosmjs/stream": "0.25.3", - "@cosmjs/tendermint-rpc": "0.25.3", - "@cosmjs/utils": "0.25.3", + "@cosmjs/cosmwasm-stargate": "0.25.5", + "@cosmjs/crypto": "0.25.5", + "@cosmjs/encoding": "0.25.5", + "@cosmjs/faucet-client": "0.25.5", + "@cosmjs/math": "0.25.5", + "@cosmjs/proto-signing": "0.25.5", + "@cosmjs/stargate": "0.25.5", + "@cosmjs/stream": "0.25.5", + "@cosmjs/tendermint-rpc": "0.25.5", + "@cosmjs/utils": "0.25.5", "ajv": "7.1.1", "axios": "0.21.1", "commander": "7.1.0", diff --git a/scripts/simapp/env b/scripts/simapp/env index 497dd5a7..0812657c 100644 --- a/scripts/simapp/env +++ b/scripts/simapp/env @@ -1,4 +1,5 @@ # Choose from https://hub.docker.com/r/interchainio/simapp/tags REPOSITORY="interchainio/simapp" -VERSION="v0.41.3" +# We pin this to the oldest version we want to maintain compatibility with +VERSION="v0.42.4" CONTAINER_NAME="simapp" diff --git a/scripts/wasmd/deploy_contracts.js b/scripts/wasmd/deploy_contracts.js new file mode 100755 index 00000000..73e1d9f6 --- /dev/null +++ b/scripts/wasmd/deploy_contracts.js @@ -0,0 +1,99 @@ +#!/usr/bin/env node +/*jshint esversion: 8 */ + +/* eslint-disable @typescript-eslint/naming-convention */ +const axios = require('axios'); +const { SigningCosmWasmClient } = require('@cosmjs/cosmwasm-stargate'); +const { GasPrice } = require('@cosmjs/stargate'); +const { DirectSecp256k1HdWallet } = require('@cosmjs/proto-signing'); +const { + QueryPacketReceiptRequest, +} = require('@cosmjs/stargate/build/codec/ibc/core/channel/v1/query'); + +// from src/lib/testutils.ts:wasmd +const config = { + endpoint: 'http://localhost:26659', + bech32prefix: 'wasm', + feeDenom: 'ucosm', + gasPrice: GasPrice.fromString('0.025ucosm'), + mnemonic: + 'enlist hip relief stomach skate base shallow young switch frequent cry park', +}; + +const contracts = [ + { + name: 'cw20-base', + wasmUrl: + 'https://github.com/CosmWasm/cosmwasm-plus/releases/download/v0.6.1/cw20_base.wasm', + codeMeta: { + source: + 'https://github.com/CosmWasm/cosmwasm-plus/tree/v0.6.0/contracts/cw20-base', + builder: 'cosmwasm/workspace-optimizer:0.11.0', + }, + }, + { + name: 'cw20-ics20', + wasmUrl: + 'https://github.com/CosmWasm/cosmwasm-plus/releases/download/v0.6.1/cw20_ics20.wasm', + codeMeta: { + source: + 'https://github.com/CosmWasm/cosmwasm-plus/tree/v0.6.0/contracts/cw20-ics20', + builder: 'cosmwasm/workspace-optimizer:0.11.0', + }, + }, +]; + +async function downloadWasm(url) { + const r = await axios.get(url, { responseType: 'arraybuffer' }); + if (r.status !== 200) { + throw new Error(`Download error: ${r.status}`); + } + return r.data; +} + +async function main() { + // use the faucet account to upload (it has fee tokens) + const wallet = await DirectSecp256k1HdWallet.fromMnemonic(config.mnemonic, { + prefix: config.bech32prefix, + }); + const { address } = (await wallet.getAccounts())[0]; + const options = { + prefix: config.bech32prefix, + gasPrice: config.gasPrice, + gasLimits: { + upload: 1750000, + }, + }; + const client = await SigningCosmWasmClient.connectWithSigner( + config.endpoint, + wallet, + options + ); + + const uploaded = []; + for (const contract of contracts) { + console.info(`Downloading ${contract.name} at ${contract.wasmUrl}...`); + const wasm = await downloadWasm(contract.wasmUrl); + const receipt = await client.upload( + address, + wasm, + contract.codeMeta, + `Upload ${contract.name}` + ); + console.debug(`Upload succeeded. Receipt: ${JSON.stringify(receipt)}`); + uploaded.push({ codeId: receipt.codeId, name: contract.name }); + } + + uploaded.forEach((x) => console.log(x)); +} + +main().then( + () => { + console.info('All done, let the coins flow.'); + process.exit(0); + }, + (error) => { + console.error(error); + process.exit(1); + } +); diff --git a/scripts/wasmd/deploy_cw1.js b/scripts/wasmd/deploy_cw1.js deleted file mode 100755 index e32b0351..00000000 --- a/scripts/wasmd/deploy_cw1.js +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env node - -/* eslint-disable @typescript-eslint/naming-convention */ -const { DirectSecp256k1HdWallet } = require("@cosmjs/proto-signing"); -const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm-stargate"); -const fs = require("fs"); - -const endpoint = "http://localhost:26659"; -const alice = { - mnemonic: "enlist hip relief stomach skate base shallow young switch frequent cry park", - address0: "wasm14qemq0vw6y3gc3u3e0aty2e764u4gs5lndxgyk", - // address1: "wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y", - // address2: "wasm1xv9tklw7d82sezh9haa573wufgy59vmwnxhnsl", - // address3: "wasm17yg9mssjenmc3jkqth6ulcwj9cxujrxxg9nmzk", - // address4: "wasm1f7j7ryulwjfe9ljplvhtcaxa6wqgula3nh873j", -}; - -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 DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, "wasm"); - const client = await SigningCosmWasmClient.connectWithSigner(endpoint, wallet); - - const wasm = fs.readFileSync(__dirname + "/contracts/cw1_subkeys.wasm"); - const uploadReceipt = await client.upload(alice.address0, wasm, codeMeta, "Upload CW1 subkeys contract"); - console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`); - - const initMsg = { - admins: [alice.address0], - mutable: true, - }; - const label = "Subkey test"; - const { contractAddress } = await client.instantiate(alice.address0, uploadReceipt.codeId, initMsg, label, { - memo: `Create a CW1 instance for ${alice.address0}`, - admin: alice.address0, - }); - await client.sendTokens(alice.address0, 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/wasmd/deploy_cw3.js b/scripts/wasmd/deploy_cw3.js deleted file mode 100755 index 74369db1..00000000 --- a/scripts/wasmd/deploy_cw3.js +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env node - -/* eslint-disable @typescript-eslint/naming-convention */ -const { DirectSecp256k1HdWallet } = require("@cosmjs/proto-signing"); -const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm-stargate"); -const fs = require("fs"); - -const endpoint = "http://localhost:26659"; -const alice = { - mnemonic: "enlist hip relief stomach skate base shallow young switch frequent cry park", - address0: "wasm14qemq0vw6y3gc3u3e0aty2e764u4gs5lndxgyk", - address1: "wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y", - address2: "wasm1xv9tklw7d82sezh9haa573wufgy59vmwnxhnsl", - address3: "wasm17yg9mssjenmc3jkqth6ulcwj9cxujrxxg9nmzk", - address4: "wasm1f7j7ryulwjfe9ljplvhtcaxa6wqgula3nh873j", -}; - -const codeMeta = { - source: "https://crates.io/api/v1/crates/cw3-fixed-multisig/0.3.1/download", - builder: "cosmwasm/rust-optimizer:0.10.4", -}; - -const initData = [ - { - admin: alice.address0, - label: "Multisig (1/3)", - initMsg: { - voters: [ - { addr: alice.address0, weight: 1 }, - { addr: alice.address1, weight: 1 }, - { addr: alice.address2, weight: 1 }, - ], - required_weight: 1, - max_voting_period: { height: 12345 }, - }, - }, - { - admin: alice.address0, - label: "Multisig (2/3)", - initMsg: { - voters: [ - { addr: alice.address0, weight: 1 }, - { addr: alice.address1, weight: 1 }, - { addr: alice.address2, weight: 1 }, - ], - required_weight: 2, - max_voting_period: { height: 12345 }, - }, - }, - { - admin: alice.address0, - label: "Multisig (uneven weights)", - initMsg: { - voters: [ - { addr: alice.address0, weight: 1 }, - { addr: alice.address1, weight: 2 }, - { addr: alice.address2, weight: 3 }, - ], - required_weight: 3, - max_voting_period: { height: 12345 }, - }, - }, -]; - -async function main() { - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, "wasm"); - const client = await SigningCosmWasmClient.connectWithSigner(endpoint, wallet); - - const wasm = fs.readFileSync(__dirname + "/contracts/cw3_fixed_multisig.wasm"); - const uploadReceipt = await client.upload( - alice.address0, - wasm, - codeMeta, - "Upload CW3 fixed multisig contract", - ); - console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`); - - for (const { admin, initMsg, label } of initData) { - const { contractAddress } = await client.instantiate( - alice.address0, - uploadReceipt.codeId, - initMsg, - label, - { - memo: `Create a CW3 instance for ${initMsg.symbol}`, - admin: admin, - }, - ); - await client.sendTokens(alice.address0, contractAddress, [ - { - amount: "1000", - denom: "ucosm", - }, - ]); - console.info(`Contract instantiated for ${label} 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/wasmd/deploy_erc20.js b/scripts/wasmd/deploy_erc20.js deleted file mode 100755 index 52059e39..00000000 --- a/scripts/wasmd/deploy_erc20.js +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env node - -/* eslint-disable @typescript-eslint/naming-convention */ -const { DirectSecp256k1HdWallet } = require("@cosmjs/proto-signing"); -const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm-stargate"); -const fs = require("fs"); - -const endpoint = "http://localhost:26659"; -const alice = { - mnemonic: "enlist hip relief stomach skate base shallow young switch frequent cry park", - address0: "wasm14qemq0vw6y3gc3u3e0aty2e764u4gs5lndxgyk", - address1: "wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y", - address2: "wasm1xv9tklw7d82sezh9haa573wufgy59vmwnxhnsl", - address3: "wasm17yg9mssjenmc3jkqth6ulcwj9cxujrxxg9nmzk", - address4: "wasm1f7j7ryulwjfe9ljplvhtcaxa6wqgula3nh873j", -}; -const unused = { - address: "wasm1cjsxept9rkggzxztslae9ndgpdyt240842kpxh", -}; -const guest = { - address: "wasm17d0jcz59jf68g52vq38tuuncmwwjk42us8fnse", -}; - -const codeMeta = { - source: "https://crates.io/api/v1/crates/cw-erc20/0.7.0/download", - builder: "cosmwasm/rust-optimizer:0.10.4", -}; - -const initDataHash = { - admin: undefined, - initMsg: { - decimals: 5, - name: "Hash token", - symbol: "HASH", - initial_balances: [ - { - address: alice.address0, - amount: "11", - }, - { - address: alice.address1, - amount: "11", - }, - { - address: alice.address2, - amount: "11", - }, - { - address: alice.address3, - amount: "11", - }, - { - address: alice.address4, - amount: "11", - }, - { - address: unused.address, - amount: "12812345", - }, - { - address: guest.address, - amount: "22004000000", - }, - ], - }, -}; -const initDataIsa = { - admin: undefined, - initMsg: { - decimals: 0, - name: "Isa Token", - symbol: "ISA", - initial_balances: [ - { - address: alice.address0, - amount: "999999999", - }, - { - address: alice.address1, - amount: "999999999", - }, - { - address: alice.address2, - amount: "999999999", - }, - { - address: alice.address3, - amount: "999999999", - }, - { - address: alice.address4, - amount: "999999999", - }, - { - address: unused.address, - amount: "42", - }, - ], - }, -}; -const initDataJade = { - admin: alice.address1, - initMsg: { - decimals: 18, - name: "Jade Token", - symbol: "JADE", - initial_balances: [ - { - address: alice.address0, - amount: "189189189000000000000000000", // 189189189 JADE - }, - { - address: alice.address1, - amount: "189189189000000000000000000", // 189189189 JADE - }, - { - address: alice.address2, - amount: "189189189000000000000000000", // 189189189 JADE - }, - { - address: alice.address3, - amount: "189189189000000000000000000", // 189189189 JADE - }, - { - address: alice.address4, - amount: "189189189000000000000000000", // 189189189 JADE - }, - { - address: guest.address, - amount: "189500000000000000000", // 189.5 JADE - }, - ], - }, -}; - -async function main() { - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, "wasm"); - const client = await SigningCosmWasmClient.connectWithSigner(endpoint, wallet); - - const wasm = fs.readFileSync(__dirname + "/contracts/cw_erc20.wasm"); - const uploadReceipt = await client.upload(alice.address0, wasm, codeMeta, "Upload ERC20 contract"); - console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`); - - for (const { initMsg, admin } of [initDataHash, initDataIsa, initDataJade]) { - const { contractAddress } = await client.instantiate( - alice.address0, - uploadReceipt.codeId, - initMsg, - initMsg.symbol, - { - memo: `Create an ERC20 instance for ${initMsg.symbol}`, - admin: admin, - }, - ); - console.info(`Contract instantiated for ${initMsg.symbol} 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/wasmd/deploy_hackatom.js b/scripts/wasmd/deploy_hackatom.js deleted file mode 100755 index 8197d501..00000000 --- a/scripts/wasmd/deploy_hackatom.js +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env node - -/* eslint-disable @typescript-eslint/naming-convention */ -const { DirectSecp256k1HdWallet } = require("@cosmjs/proto-signing"); -const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm-stargate"); -const fs = require("fs"); - -const endpoint = "http://localhost:26659"; -const alice = { - mnemonic: "enlist hip relief stomach skate base shallow young switch frequent cry park", - address0: "wasm14qemq0vw6y3gc3u3e0aty2e764u4gs5lndxgyk", - address1: "wasm1hhg2rlu9jscacku2wwckws7932qqqu8xm5ca8y", - address2: "wasm1xv9tklw7d82sezh9haa573wufgy59vmwnxhnsl", - address3: "wasm17yg9mssjenmc3jkqth6ulcwj9cxujrxxg9nmzk", - address4: "wasm1f7j7ryulwjfe9ljplvhtcaxa6wqgula3nh873j", -}; - -const codeMeta = { - source: "https://crates.io/api/v1/crates/hackatom/not-yet-released/download", - builder: "cosmwasm/rust-optimizer:0.9.1", -}; - -const inits = [ - { - label: "From deploy_hackatom.js (0)", - msg: { - beneficiary: alice.address0, - verifier: alice.address0, - }, - admin: undefined, - }, - { - label: "From deploy_hackatom.js (1)", - msg: { - beneficiary: alice.address1, - verifier: alice.address1, - }, - admin: undefined, - }, - { - label: "From deploy_hackatom.js (2)", - msg: { - beneficiary: alice.address2, - verifier: alice.address2, - }, - admin: alice.address1, - }, -]; - -async function main() { - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, undefined, "wasm"); - const client = await SigningCosmWasmClient.connectWithSigner(endpoint, wallet); - - const wasm = fs.readFileSync(__dirname + "/contracts/hackatom.wasm"); - const uploadReceipt = await client.upload(alice.address0, wasm, codeMeta, "Upload hackatom contract"); - console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`); - - for (const { label, msg, admin } of inits) { - const { contractAddress } = await client.instantiate(alice.address0, uploadReceipt.codeId, msg, label, { - memo: `Create a hackatom instance in deploy_hackatom.js`, - admin: admin, - }); - console.info(`Contract instantiated 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/wasmd/env b/scripts/wasmd/env index 8144880a..abe6885e 100644 --- a/scripts/wasmd/env +++ b/scripts/wasmd/env @@ -1,5 +1,5 @@ # Choose from https://hub.docker.com/r/cosmwasm/wasmd/tags REPOSITORY="cosmwasm/wasmd" -VERSION="v0.16.0" - +# This should be pinned to the newest version we support +VERSION="v0.17.0" CONTAINER_NAME="wasmd" diff --git a/scripts/wasmd/generate_addresses.js b/scripts/wasmd/generate_addresses.js index 3ed78145..4a100bc2 100755 --- a/scripts/wasmd/generate_addresses.js +++ b/scripts/wasmd/generate_addresses.js @@ -1,34 +1,43 @@ #!/usr/bin/env node /* eslint-disable @typescript-eslint/naming-convention */ -const { encodeSecp256k1Pubkey, makeCosmoshubPath, Secp256k1HdWallet } = require("@cosmjs/launchpad"); +const { + encodeSecp256k1Pubkey, + makeCosmoshubPath, + Secp256k1HdWallet, +} = require('@cosmjs/launchpad'); -const prefix = "wasm"; +const prefix = 'wasm'; const accountsToCreate = [ { mnemonic: - "economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone", + 'economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone', accountNumbers: [0, 1, 2, 3, 4], }, { - mnemonic: "enlist hip relief stomach skate base shallow young switch frequent cry park", + mnemonic: + 'enlist hip relief stomach skate base shallow young switch frequent cry park', accountNumbers: [0, 1, 2, 3, 4], }, { - mnemonic: "remain fragile remove stamp quiz bus country dress critic mammal office need", + mnemonic: + 'remain fragile remove stamp quiz bus country dress critic mammal office need', accountNumbers: [0, 1, 2, 3, 4], }, { - mnemonic: "oyster design unusual machine spread century engine gravity focus cave carry slot", + mnemonic: + 'oyster design unusual machine spread century engine gravity focus cave carry slot', accountNumbers: [0], }, { - mnemonic: "degree tackle suggest window test behind mesh extra cover prepare oak script", + mnemonic: + 'degree tackle suggest window test behind mesh extra cover prepare oak script', accountNumbers: [0], }, { - mnemonic: "example indicate trick cereal hub fix civil host kiss version bird dash", + mnemonic: + 'example indicate trick cereal hub fix civil host kiss version bird dash', accountNumbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }, ]; @@ -37,19 +46,23 @@ async function main() { for (const { mnemonic, accountNumbers } of accountsToCreate) { const wallets = await Promise.all( accountNumbers.map((accountNumber) => - Secp256k1HdWallet.fromMnemonic(mnemonic, makeCosmoshubPath(accountNumber), prefix), - ), - ); - const accounts = (await Promise.all(wallets.map((wallet) => wallet.getAccounts()))).map( - (accountsForWallet) => accountsForWallet[0], + Secp256k1HdWallet.fromMnemonic( + mnemonic, + makeCosmoshubPath(accountNumber), + prefix + ) + ) ); + const accounts = ( + await Promise.all(wallets.map((wallet) => wallet.getAccounts())) + ).map((accountsForWallet) => accountsForWallet[0]); - console.info("=".repeat(process.stdout.columns)); - console.info("mnemonic:", mnemonic); + console.info('='.repeat(process.stdout.columns)); + console.info('mnemonic:', mnemonic); for (const { address, pubkey } of accounts) { - console.info("-".repeat(process.stdout.columns)); - console.info("pubkey:", encodeSecp256k1Pubkey(pubkey).value); - console.info("address:", address); + console.info('-'.repeat(process.stdout.columns)); + console.info('pubkey:', encodeSecp256k1Pubkey(pubkey).value); + console.info('address:', address); } } } @@ -59,5 +72,5 @@ main().then( (error) => { console.error(error); process.exit(1); - }, + } ); diff --git a/scripts/wasmd/init.sh b/scripts/wasmd/init.sh index 5ac8723f..45a5fcae 100755 --- a/scripts/wasmd/init.sh +++ b/scripts/wasmd/init.sh @@ -12,21 +12,4 @@ echo "Okay, thank you for your patience." SCRIPT_DIR="$(realpath "$(dirname "$0")")" -# -# Cosmos SDK init -# -"$SCRIPT_DIR/send_first.js" - -# -# CosmWasm init -# -( - echo "Ensuring contracts' checksums are correct ..." - cd "$SCRIPT_DIR/contracts" - sha256sum --check checksums.sha256 -) -"$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" +"$SCRIPT_DIR/deploy_contracts.js" diff --git a/scripts/wasmd/send_first.js b/scripts/wasmd/send_first.js deleted file mode 100755 index fa20673b..00000000 --- a/scripts/wasmd/send_first.js +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env node - -/* eslint-disable @typescript-eslint/naming-convention */ -const { Random } = require("@cosmjs/crypto"); -const { Bech32 } = require("@cosmjs/encoding"); -const { coins } = require("@cosmjs/launchpad"); -const { DirectSecp256k1HdWallet } = require("@cosmjs/proto-signing"); -const { assertIsBroadcastTxSuccess, SigningStargateClient } = require("@cosmjs/stargate"); - -const rpcUrl = "http://localhost:26659"; -const prefix = "wasm"; -const faucet = { - mnemonic: - "economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone", - address0: "wasm1pkptre7fdkl6gfrzlesjjvhxhlc3r4gm32kke3", -}; - -async function main() { - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic, undefined, prefix); - const client = await SigningStargateClient.connectWithSigner(rpcUrl, wallet); - const recipient = Bech32.encode(prefix, Random.getBytes(20)); - const amount = coins(226644, "ucosm"); - const memo = "Ensure chain has my pubkey"; - const sendResult = await client.sendTokens(faucet.address0, recipient, amount, memo); - assertIsBroadcastTxSuccess(sendResult); -} - -main().then( - () => process.exit(0), - (error) => { - console.error(error); - process.exit(1); - }, -); diff --git a/src/binary/ibc-setup/commands/balances.ts b/src/binary/ibc-setup/commands/balances.ts index ae72134c..f812929b 100644 --- a/src/binary/ibc-setup/commands/balances.ts +++ b/src/binary/ibc-setup/commands/balances.ts @@ -1,11 +1,8 @@ import path from 'path'; -import { stringToPath } from '@cosmjs/crypto'; -import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing'; import { GasPrice } from '@cosmjs/stargate'; import { Coin } from '../../../codec/cosmos/base/v1beta1/coin'; -import { IbcClient } from '../../../lib/ibcclient'; import { registryFile } from '../../constants'; import { Logger } from '../../create-logger'; import { borderlessTable } from '../../utils/borderless-table'; @@ -14,6 +11,7 @@ import { loadAndValidateRegistry } from '../../utils/load-and-validate-registry' import { resolveHomeOption } from '../../utils/options/shared/resolve-home-option'; import { resolveKeyFileOption } from '../../utils/options/shared/resolve-key-file-option'; import { resolveMnemonicOption } from '../../utils/options/shared/resolve-mnemonic-option'; +import { signingClient } from '../../utils/signing-client'; import { Flags, getAddresses, Options } from './keys-list'; @@ -45,32 +43,13 @@ export async function run(options: Options, logger: Logger) { const balances = ( await Promise.allSettled( addresses.map(async ([chain, data, address]) => { - const hdPathsToSpread = data.hd_path - ? { hdPaths: [stringToPath(data.hd_path)] } - : {}; - const signer = await DirectSecp256k1HdWallet.fromMnemonic( + const client = await signingClient( + data, options.mnemonic, - { - prefix: data.prefix, - ...hdPathsToSpread, - } + logger.child({ label: chain }) ); - const gasPrice = GasPrice.fromString(data.gas_price); - - const client = await IbcClient.connectWithSigner( - data.rpc[0], // rpc[0] is guaranteed to be defined by registry validator - signer, - address, - { - prefix: data.prefix, - gasPrice, - logger, - } - ); - const coin = await client.query.bank.balance(address, gasPrice.denom); - return [chain, coin]; }) ) diff --git a/src/binary/ibc-setup/index.ts b/src/binary/ibc-setup/index.ts index e8fb84e1..ffc0d260 100644 --- a/src/binary/ibc-setup/index.ts +++ b/src/binary/ibc-setup/index.ts @@ -58,6 +58,7 @@ const ics20Command = program .description( 'Create new unordered channel (ics20-1) for given chains, ports, and connections' ) + .addOption(homeOption) .addOption(srcTrust) .addOption(destTrust) .addOption(mnemonicOption) diff --git a/src/binary/utils/signing-client.ts b/src/binary/utils/signing-client.ts index 71d5756c..d1a533f7 100644 --- a/src/binary/utils/signing-client.ts +++ b/src/binary/utils/signing-client.ts @@ -19,10 +19,19 @@ export async function signingClient( ...hdPathsToSpread, }); const { address } = (await signer.getAccounts())[0]; + // This is test timing to let us handle 250ms blocks without huge delays + const extras = + process.env.NODE_ENV == 'test' + ? { + broadcastPollIntervalMs: 300, + broadcastTimeoutMs: 2000, + } + : {}; const options: IbcClientOptions = { prefix: chain.prefix, gasPrice: GasPrice.fromString(chain.gas_price), logger, + ...extras, }; const client = await IbcClient.connectWithSigner( chain.rpc[0], diff --git a/src/lib/cosmwasm.spec.ts b/src/lib/cosmwasm.spec.ts new file mode 100644 index 00000000..2b6e455d --- /dev/null +++ b/src/lib/cosmwasm.spec.ts @@ -0,0 +1,303 @@ +import { fromUtf8, toBase64, toUtf8 } from '@cosmjs/encoding'; +import { assert } from '@cosmjs/utils'; +import test from 'ava'; +import axios from 'axios'; + +import { Link } from './link'; +import { CosmWasmSigner, ics20, setup, setupWasmClient } from './testutils'; + +const codeIds: Record = { + cw20: 0, + ics20: 0, +}; + +interface WasmData { + wasmUrl: string; + codeMeta: { + source: string; + builder: string; + }; +} + +const contracts: Record = { + cw20: { + wasmUrl: + 'https://github.com/CosmWasm/cosmwasm-plus/releases/download/v0.6.1/cw20_base.wasm', + codeMeta: { + source: + 'https://github.com/CosmWasm/cosmwasm-plus/tree/v0.6.0/contracts/cw20-base', + builder: 'cosmwasm/workspace-optimizer:0.11.0', + }, + }, + ics20: { + wasmUrl: + 'https://github.com/CosmWasm/cosmwasm-plus/releases/download/v0.6.1/cw20_ics20.wasm', + codeMeta: { + source: + 'https://github.com/CosmWasm/cosmwasm-plus/tree/v0.6.0/contracts/cw20-ics20', + builder: 'cosmwasm/workspace-optimizer:0.11.0', + }, + }, +}; + +async function downloadWasm(url: string) { + const r = await axios.get(url, { responseType: 'arraybuffer' }); + if (r.status !== 200) { + throw new Error(`Download error: ${r.status}`); + } + return r.data; +} + +test.before(async (t) => { + const cosmwasm = await setupWasmClient(); + + for (const name in contracts) { + const contract = contracts[name]; + console.info(`Downloading ${name} at ${contract.wasmUrl}...`); + const wasm = await downloadWasm(contract.wasmUrl); + const receipt = await cosmwasm.sign.upload( + cosmwasm.senderAddress, + wasm, + contract.codeMeta, + `Upload ${name}` + ); + console.debug(`Upload ${name} with CodeID: ${receipt.codeId}`); + codeIds[name] = receipt.codeId; + } + + t.pass(); +}); + +// creates it with 6 decimal places +// provides 123456.789 tokens to the creator +function cw20init(owner: string, symbol: string): Record { + return { + decimals: 6, + name: symbol, + symbol, + initial_balances: [ + { + address: owner, + amount: '123456789000', + }, + ], + }; +} + +async function balance( + cosmwasm: CosmWasmSigner, + cw20Addr: string, + senderAddress?: string +): Promise { + const query = { + balance: { + address: senderAddress || cosmwasm.senderAddress, + }, + }; + const res = await cosmwasm.sign.queryContractSmart(cw20Addr, query); + // print this + return res.balance; +} + +test.serial('successfully instantiate contracts', async (t) => { + const cosmwasm = await setupWasmClient(); + + // instantiate cw20 + const initMsg = cw20init(cosmwasm.senderAddress, 'DEMO'); + const { contractAddress: cw20Addr } = await cosmwasm.sign.instantiate( + cosmwasm.senderAddress, + codeIds.cw20, + initMsg, + 'DEMO' + ); + t.truthy(cw20Addr); + + const bal = await balance(cosmwasm, cw20Addr); + t.is('123456789000', bal); + + // instantiate ics20 + const ics20Msg = { + default_timeout: 3600, + }; + const { contractAddress: ics20Addr } = await cosmwasm.sign.instantiate( + cosmwasm.senderAddress, + codeIds.ics20, + ics20Msg, + 'ICS' + ); + t.truthy(ics20Addr); +}); + +test.serial('set up channel with ics20 contract', async (t) => { + const cosmwasm = await setupWasmClient(); + + // instantiate ics20 + const ics20Msg = { + default_timeout: 3600, + }; + const { contractAddress: ics20Addr } = await cosmwasm.sign.instantiate( + cosmwasm.senderAddress, + codeIds.ics20, + ics20Msg, + 'ICS' + ); + t.truthy(ics20Addr); + + // FIXME: query this when https://github.com/cosmos/cosmjs/issues/836 is resolved + const wasmPort = `wasm.${ics20Addr}`; + + const [src, dest] = await setup(); + const link = await Link.createWithNewConnections(src, dest); + await link.createChannel( + 'A', + ics20.srcPortId, + wasmPort, + ics20.ordering, + ics20.version + ); +}); + +test.serial('send packets with ics20 contract', async (t) => { + const cosmwasm = await setupWasmClient(); + + // instantiate cw20 + const initMsg = cw20init(cosmwasm.senderAddress, 'CASH'); + const { contractAddress: cw20Addr } = await cosmwasm.sign.instantiate( + cosmwasm.senderAddress, + codeIds.cw20, + initMsg, + 'CASH' + ); + t.truthy(cw20Addr); + let bal = await balance(cosmwasm, cw20Addr); + t.is('123456789000', bal); + + // instantiate ics20 + const ics20Msg = { + default_timeout: 3600, + }; + const { contractAddress: ics20Addr } = await cosmwasm.sign.instantiate( + cosmwasm.senderAddress, + codeIds.ics20, + ics20Msg, + 'ICSX' + ); + t.truthy(ics20Addr); + // FIXME: query this when https://github.com/cosmos/cosmjs/issues/836 is resolved + const wasmPort = `wasm.${ics20Addr}`; + + const [src, dest] = await setup(); + const link = await Link.createWithNewConnections(src, dest); + const channels = await link.createChannel( + 'A', + ics20.srcPortId, + wasmPort, + ics20.ordering, + ics20.version + ); + + // send a packet from the ics20 contract.... (on dest chain) + const receiveMsg = { + channel: channels.dest.channelId, + remote_address: src.senderAddress, + }; + const encoded = toBase64(toUtf8(JSON.stringify(receiveMsg))); + const sendMsg = { + send: { + contract: ics20Addr, + amount: '456789000', // leaving 123000.0000000 tokens + msg: encoded, + }, + }; + await cosmwasm.sign.execute( + cosmwasm.senderAddress, + cw20Addr, + sendMsg, + 'Send CW20 tokens via ICS20' + ); + + // let's see if the balance went down + bal = await balance(cosmwasm, cw20Addr); + t.is('123000000000', bal); + + // check source balance + const preBalance = await src.sign.getAllBalances(src.senderAddress); + t.is(1, preBalance.length); + t.is('umuon', preBalance[0].denom); + + // ensure the packet moves + const packets = await link.getPendingPackets('B'); + t.is(packets.length, 1); + const preAcks = await link.getPendingAcks('A'); + t.is(preAcks.length, 0); + // move it and the ack + const txAcks = await link.relayPackets('B', packets); + t.is(txAcks.length, 1); + const parsedAck1 = JSON.parse(fromUtf8(txAcks[0].acknowledgement)); + t.truthy(parsedAck1.result); + t.falsy(parsedAck1.error); + + // need to wait briefly for it to be indexed + await src.waitOneBlock(); + // and send the acks over + await link.relayAcks('A', txAcks); + + // check source balances increased + const relayedBalance = await src.sign.getAllBalances(src.senderAddress); + t.is(2, relayedBalance.length); + const ibcCoin = relayedBalance.find((d) => d.denom !== 'umuon'); + assert(ibcCoin); + t.is('456789000', ibcCoin.amount); + console.log(ibcCoin); + + // send this token back over the channel + const timeoutHeight = await dest.timeoutHeight(500); + await src.transferTokens( + channels.src.portId, + channels.src.channelId, + ibcCoin, + dest.senderAddress, + timeoutHeight + ); + await src.waitOneBlock(); + + // ensure the packet moves + const packets2 = await link.getPendingPackets('A'); + t.is(packets2.length, 1); + // move it and the ack + const txAcks2 = await link.relayPackets('A', packets2); + t.is(txAcks2.length, 1); + // need to wait briefly for it to be indexed + await dest.waitOneBlock(); + // and send the acks over + await link.relayAcks('B', txAcks2); + + // balance updated on recipient + const gotBal = await balance(cosmwasm, cw20Addr, dest.senderAddress); + t.is(gotBal, '456789000'); + + // send native token over channel (from dest -> cosmwasm chain) + const timeoutHeight2 = await dest.timeoutHeight(500); + const nativeCoin = { + denom: 'umuon', + amount: '111111', + }; + await src.transferTokens( + channels.src.portId, + channels.src.channelId, + nativeCoin, + dest.senderAddress, + timeoutHeight2 + ); + await src.waitOneBlock(); + + // relay this packet + const packets3 = await link.getPendingPackets('A'); + t.is(packets3.length, 1); + const txAcks3 = await link.relayPackets('A', packets3); + t.is(txAcks3.length, 1); + // and expect error on ack + const parsedAck = JSON.parse(fromUtf8(txAcks3[0].acknowledgement)); + t.truthy(parsedAck.error); + t.falsy(parsedAck.result); +}); diff --git a/src/lib/endpoint.ts b/src/lib/endpoint.ts index 2d70b3ca..b85579eb 100644 --- a/src/lib/endpoint.ts +++ b/src/lib/endpoint.ts @@ -5,7 +5,12 @@ import { CommitResponse } from '@cosmjs/tendermint-rpc'; import { Packet } from '../codec/ibc/core/channel/v1/channel'; import { IbcClient } from './ibcclient'; -import { Ack, parseAcksFromLogs, parsePacketsFromLogs } from './utils'; +import { + Ack, + parseAcksFromLogs, + parsePacketsFromBlockResult, + parsePacketsFromLogs, +} from './utils'; export interface PacketWithMetadata { packet: Packet; @@ -54,8 +59,35 @@ export class Endpoint { return this.client.getCommit(); } - // returns all packets (auto-paginates, so be careful about not setting a minHeight) - public async querySentPackets({ + private async getPacketsFromBlockEvents({ + minHeight, + maxHeight, + }: QueryOpts = {}): Promise { + let query = `send_packet.packet_connection='${this.connectionID}'`; + if (minHeight) { + query = `${query} AND block.height>=${minHeight}`; + } + if (maxHeight) { + query = `${query} AND block.height<=${maxHeight}`; + } + + const search = await this.client.tm.blockSearchAll({ query }); + const resultsNested = await Promise.all( + search.blocks.map(async ({ block }) => { + const height = block.header.height; + const result = await this.client.tm.blockResults(height); + return parsePacketsFromBlockResult(result).map((packet) => ({ + packet, + height, + sender: '', + })); + }) + ); + + return ([] as PacketWithMetadata[]).concat(...resultsNested); + } + + private async getPacketsFromTxs({ minHeight, maxHeight, }: QueryOpts = {}): Promise { @@ -92,6 +124,21 @@ export class Endpoint { return ([] as PacketWithMetadata[]).concat(...resultsNested); } + // returns all packets (auto-paginates, so be careful about not setting a minHeight) + public async querySentPackets({ + minHeight, + maxHeight, + }: QueryOpts = {}): Promise { + const txsPackets = await this.getPacketsFromTxs({ minHeight, maxHeight }); + const eventsPackets = await this.getPacketsFromBlockEvents({ + minHeight, + maxHeight, + }); + return ([] as PacketWithMetadata[]) + .concat(...txsPackets) + .concat(...eventsPackets); + } + // returns all acks (auto-paginates, so be careful about not setting a minHeight) public async queryWrittenAcks({ minHeight, diff --git a/src/lib/testutils.ts b/src/lib/testutils.ts index 9d3505ba..2dac98c3 100644 --- a/src/lib/testutils.ts +++ b/src/lib/testutils.ts @@ -1,4 +1,8 @@ // This file outputs some basic test functionality, and includes tests that they work +import { + SigningCosmWasmClient, + SigningCosmWasmClientOptions, +} from '@cosmjs/cosmwasm-stargate'; import { Bip39, Random } from '@cosmjs/crypto'; import { Bech32 } from '@cosmjs/encoding'; import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing'; @@ -188,6 +192,34 @@ export async function signingClient( return client; } +export async function signingCosmWasmClient( + opts: SigningOpts, + mnemonic: string +): Promise { + const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { + prefix: opts.prefix, + }); + const { address: senderAddress } = (await wallet.getAccounts())[0]; + + const options: SigningCosmWasmClientOptions = { + prefix: opts.prefix, + gasPrice: GasPrice.fromString(opts.minFee), + // This is just for tests - don't add this in production code + broadcastPollIntervalMs: 300, + broadcastTimeoutMs: 2000, + gasLimits: { + upload: 1750000, + }, + }; + const sign = await SigningCosmWasmClient.connectWithSigner( + opts.tendermintUrlHttp, + wallet, + options + ); + + return { sign, senderAddress }; +} + // This is simapp -> wasm export async function setup(logger?: Logger): Promise { // create apps and fund an account @@ -209,6 +241,20 @@ export async function setupGaiaWasm(logger?: Logger): Promise { return [src, dest]; } +export interface CosmWasmSigner { + readonly sign: SigningCosmWasmClient; + readonly senderAddress: string; +} + +// This creates a client for the CosmWasm chain, that can interact with contracts +export async function setupWasmClient(): Promise { + // create apps and fund an account + const mnemonic = generateMnemonic(); + const cosmwasm = await signingCosmWasmClient(wasmd, mnemonic); + await fundAccount(wasmd, cosmwasm.senderAddress, '4000000'); + return cosmwasm; +} + export async function fundAccount( opts: FundingOpts, rcpt: string, @@ -218,6 +264,9 @@ export async function fundAccount( const feeTokens = { amount, denom: GasPrice.fromString(opts.minFee).denom, + gasLimits: { + upload: 1750000, + }, }; await client.sendTokens(rcpt, [feeTokens]); } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 100caecf..79584c79 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,8 @@ import { fromUtf8, toHex, toUtf8 } from '@cosmjs/encoding'; import { BroadcastTxFailure, Coin, logs, StdFee } from '@cosmjs/stargate'; +const { parseEvent } = logs; import { + BlockResultsResponse, ReadonlyDateWithNanoseconds, Header as RpcHeader, ValidatorPubkey as RpcPubKey, @@ -193,6 +195,28 @@ interface ParsedEvent { readonly attributes: readonly ParsedAttribute[]; } +function decodeBase64(value: Uint8Array): string { + return Buffer.from(value).toString('binary'); +} + +export function parsePacketsFromBlockResult( + result: BlockResultsResponse +): Packet[] { + const allEvents: ParsedEvent[] = result.beginBlockEvents + .concat(...result.endBlockEvents) + .filter(({ type }) => type === 'send_packet') + .map(({ type, attributes }) => ({ + type, + attributes: attributes.map(({ key, value }) => ({ + key: decodeBase64(key), + value: decodeBase64(value), + })), + })); + + const flatEvents = ([] as ParsedEvent[]).concat(allEvents.map(parseEvent)); + return flatEvents.map(parsePacket); +} + export function parsePacketsFromLogs(logs: readonly logs.Log[]): Packet[] { // grab all send_packet events from the logs const allEvents: ParsedEvent[][] = logs.map((log) => diff --git a/yarn.lock b/yarn.lock index 6313e9fe..58447b50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -397,24 +397,54 @@ ripemd160 "^2.0.2" sha.js "^2.4.11" -"@cosmjs/amino@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.25.3.tgz#fcb2d28657ef4f787c2ac8b8b8c6b0f9563be55a" - integrity sha512-YKEcUwthGjNHUmmTu+aQcLJCeKysG5l4NBBkrf2w+g7e247DB6yZXcpRhZZZqBwj2Pd5P4KnoOaEkwPlvGEsXg== - dependencies: - "@cosmjs/crypto" "^0.25.3" - "@cosmjs/encoding" "^0.25.3" - "@cosmjs/math" "^0.25.3" - "@cosmjs/utils" "^0.25.3" - -"@cosmjs/crypto@0.25.3", "@cosmjs/crypto@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.25.3.tgz#fad5fc375c3a7c20a4847d859a987fd82b7e5b42" - integrity sha512-EKQjoeQDNcTOrbajRvs5hieUFIFZ8am+ojAd7qfdm9Lfxlvnq7pX2pMAMqn9Er8+4PaaLUI7d8xBTq3f1GSg8w== - dependencies: - "@cosmjs/encoding" "^0.25.3" - "@cosmjs/math" "^0.25.3" - "@cosmjs/utils" "^0.25.3" +"@cosmjs/amino@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.25.5.tgz#a22abac74057848834f1e3b0d2b90a0a328dd119" + integrity sha512-q9tU2b9hJ3S/KxYCLSyiZCfkaidbSPBr2sJ5HPLxz48y5O4k9sYM7bPa0zioeePaIBnby3AOgyjucVxlbzUlYg== + dependencies: + "@cosmjs/crypto" "^0.25.5" + "@cosmjs/encoding" "^0.25.5" + "@cosmjs/math" "^0.25.5" + "@cosmjs/utils" "^0.25.5" + +"@cosmjs/cosmwasm-launchpad@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/cosmwasm-launchpad/-/cosmwasm-launchpad-0.25.5.tgz#270dd139c7a52a1c51324e9c9e9aa730ddc0f1a4" + integrity sha512-9/Z8m9rfovtgchN1thcdVBEBXCrh5clVNgpVp1rpbkUSSGA7T5tCR8H1qX0+Yd3HLRh212O5EnCfzzXjNxR21A== + dependencies: + "@cosmjs/crypto" "^0.25.5" + "@cosmjs/encoding" "^0.25.5" + "@cosmjs/launchpad" "^0.25.5" + "@cosmjs/math" "^0.25.5" + "@cosmjs/utils" "^0.25.5" + pako "^2.0.2" + +"@cosmjs/cosmwasm-stargate@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/cosmwasm-stargate/-/cosmwasm-stargate-0.25.5.tgz#481ab455ed860c40490117f623dedc7a32ae2c55" + integrity sha512-lUwjXMnVO+8iZtqwKr4hEBgA9q03AqAhUBNNyyG8oeTpgASrQ1ZlLjjmBWgvEpw4JokRduDf7il+NNENjaeAhQ== + dependencies: + "@cosmjs/amino" "^0.25.5" + "@cosmjs/cosmwasm-launchpad" "^0.25.5" + "@cosmjs/crypto" "^0.25.5" + "@cosmjs/encoding" "^0.25.5" + "@cosmjs/math" "^0.25.5" + "@cosmjs/proto-signing" "^0.25.5" + "@cosmjs/stargate" "^0.25.5" + "@cosmjs/tendermint-rpc" "^0.25.5" + "@cosmjs/utils" "^0.25.5" + long "^4.0.0" + pako "^2.0.2" + protobufjs "~6.10.2" + +"@cosmjs/crypto@0.25.5", "@cosmjs/crypto@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.25.5.tgz#4b8e4a8693f9f597b4de92df1b1057f61ec7a735" + integrity sha512-i0Nfbk4JXAwyKNGPFu0o1xV6IJUbYmmveySytbU/yweybcZppxoczjSQ1sGrUaLVLvgfcpfwZte3jKqDR67+dg== + dependencies: + "@cosmjs/encoding" "^0.25.5" + "@cosmjs/math" "^0.25.5" + "@cosmjs/utils" "^0.25.5" bip39 "^3.0.2" bn.js "^4.11.8" elliptic "^6.5.3" @@ -423,98 +453,111 @@ ripemd160 "^2.0.2" sha.js "^2.4.11" -"@cosmjs/encoding@0.25.3", "@cosmjs/encoding@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.25.3.tgz#5ebcc0f736a96d5cca847233616efafea3603b7f" - integrity sha512-UoBtXE0NLQ2c7x2TCGxqFJ5UzEKL3bgP7mRjswXdpFDut0RaH3+M+nOGxjWavA80kcThGMVlb6mFOJfM1aRacw== +"@cosmjs/encoding@0.25.5", "@cosmjs/encoding@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.25.5.tgz#edc90084b112b57ca73ec82c1682ac9c4da3bb3a" + integrity sha512-QT7MaPBiMeCaMJ6VZZKeOqDQlAxMEzTFjBFhbkdyx5DVRc4dPOVO4HbTggmIN5/eizIv4/CNJSVTR//tO53J0A== dependencies: base64-js "^1.3.0" bech32 "^1.1.4" readonly-date "^1.0.0" -"@cosmjs/faucet-client@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/faucet-client/-/faucet-client-0.25.3.tgz#dba9ade3544483397097d01507421bc997fb032e" - integrity sha512-G4AbABlf0Ah0ElN5VS37nl1gDk/Q6MJRTxPXv37NX4p96o+dUm+0vvfZH0735kUTo7FtPECoBIhnSEPapPeLew== +"@cosmjs/faucet-client@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/faucet-client/-/faucet-client-0.25.5.tgz#253ef75356320048968e8a816eb5e8fad63ec8cc" + integrity sha512-bslIU0Oa9WUPexgwxkQlq2opNAVyBFFMUjlkduricNc9C6/UOR/l6mjB0mt+VU6JTcc9wkxf6wt3wD2dwZQklw== dependencies: axios "^0.21.1" -"@cosmjs/json-rpc@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.25.3.tgz#a37fa2afb6a1db65eb791e7b5fb4403cc891af7f" - integrity sha512-UEn/EDYvpCKMFUpatr7vcfEZij1Kd5m/YxAge9vFKRQ5G4Lk/glsWROQ7e8wAbD/95Zrd6PVJ5Mz3AVOPib3AA== +"@cosmjs/json-rpc@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.25.5.tgz#34c7489fc6ae32a4acaabf658e057a55c431a193" + integrity sha512-WFDallAolxBqB8V/mYxU0riF/OBoc6Fc2DDsZhds5xOZxeN9sTX0qWhu1UiFyURw4Z9D+SjB9QngqSDBTMTdjw== dependencies: - "@cosmjs/stream" "^0.25.3" + "@cosmjs/stream" "^0.25.5" xstream "^11.14.0" -"@cosmjs/math@0.25.3", "@cosmjs/math@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.25.3.tgz#b2cdd39747d5721359eb2275381600b0db5ccfb3" - integrity sha512-5zIyVpDbB7KexF4lFOJ6PrJepscJoTkLEDq7UB6HgmiWdLC73NkHltHsBXSOwmZ7xOPI3EJZfZPAU8j2zPOeIQ== +"@cosmjs/launchpad@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/launchpad/-/launchpad-0.25.5.tgz#bfd7b5ce89145f26c35ebbce98e3f6ec0e06c520" + integrity sha512-7zsFWQHLAiOUBXV/QWvffI/5Ssma67lEO3V01DZ8CtfnWiO4bCSbnU2sslSAH11RkrPdNfRBM7WOBczMMigIzw== + dependencies: + "@cosmjs/amino" "^0.25.5" + "@cosmjs/crypto" "^0.25.5" + "@cosmjs/encoding" "^0.25.5" + "@cosmjs/math" "^0.25.5" + "@cosmjs/utils" "^0.25.5" + axios "^0.21.1" + fast-deep-equal "^3.1.3" + +"@cosmjs/math@0.25.5", "@cosmjs/math@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.25.5.tgz#0f586610353c95b2055d1b2556a232622cd2d453" + integrity sha512-LWovT1uTHlr+VEce27/14Wrgc4INJYOYk1+ncyvbZertixNFH6OMnc9Xkk0DIV4RYmW+/fvB9kCXVnNtQGSuHg== dependencies: bn.js "^4.11.8" -"@cosmjs/proto-signing@0.25.3", "@cosmjs/proto-signing@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.25.3.tgz#75109b9069a43bb59066a16be25cccb59a5ae01b" - integrity sha512-ysS0S4LI6rUOY4nABIk5GZ6ev7o9AQl+VUaj3vYTs/cdscOKWmDvgP7go8d82Q2QpZtMNlJAGoiC4gh04DSrgA== +"@cosmjs/proto-signing@0.25.5", "@cosmjs/proto-signing@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.25.5.tgz#93122ed1d57518a1c520917afde62e5aa9754033" + integrity sha512-YWVp+dGHt7v6ZKjOs8CI9xkpOV49eweHbYMv/vCVYF4cEh0kWwy2WzbWIkUH9zwwUqCxigVOTBYUCfbsjEbfug== dependencies: - "@cosmjs/amino" "^0.25.3" + "@cosmjs/amino" "^0.25.5" long "^4.0.0" protobufjs "~6.10.2" -"@cosmjs/socket@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.25.3.tgz#9dd00aadea8fa088afaf194096adaf338a4f57ee" - integrity sha512-CXW53AmaSftoHM3CIln8eCwTS2iaAsHDcr99bV8M00WnLDEvF0niwl9ETBEzi8l6HC1e/8ZcP54ygggznDiKTA== +"@cosmjs/socket@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/socket/-/socket-0.25.5.tgz#2b565afd310cdfaf3b106b901302ae93d849ec54" + integrity sha512-wcJVbD4xlF4+5hMum4tOmAy5ppX+E9qrB9Pvt3T7BK+6T5uobxiBQCLEiDwHP3n42RBj+xQWJrScPf5IEWmZKg== dependencies: - "@cosmjs/stream" "^0.25.3" + "@cosmjs/stream" "^0.25.5" isomorphic-ws "^4.0.1" - ws "^6.2.0" + ws "^7" xstream "^11.14.0" -"@cosmjs/stargate@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.25.3.tgz#9a35abc0e6332d926aa209e3f7ca88ae6be144f8" - integrity sha512-zVcigAMbDLlRlUXZ8W9igQQEdmiTir+ZSLtw+F0//DanVNGdlIWn5tHTcGrqncNfl22xttEAcdarWSEcIBc5xg== +"@cosmjs/stargate@0.25.5", "@cosmjs/stargate@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/stargate/-/stargate-0.25.5.tgz#29d0ce8d50f5c3030bd8c127bee355460a88c609" + integrity sha512-cOZ+fOC16YT7W2vjBnnk9oJfuIlB02KdK6dn6aigMd4mx+7DS2jvNslpQrfKmtrwB9AVsgc6tklBkYwG5qAuKQ== dependencies: "@confio/ics23" "^0.6.3" - "@cosmjs/amino" "^0.25.3" - "@cosmjs/encoding" "^0.25.3" - "@cosmjs/math" "^0.25.3" - "@cosmjs/proto-signing" "^0.25.3" - "@cosmjs/stream" "^0.25.3" - "@cosmjs/tendermint-rpc" "^0.25.3" - "@cosmjs/utils" "^0.25.3" + "@cosmjs/amino" "^0.25.5" + "@cosmjs/encoding" "^0.25.5" + "@cosmjs/math" "^0.25.5" + "@cosmjs/proto-signing" "^0.25.5" + "@cosmjs/stream" "^0.25.5" + "@cosmjs/tendermint-rpc" "^0.25.5" + "@cosmjs/utils" "^0.25.5" long "^4.0.0" protobufjs "~6.10.2" -"@cosmjs/stream@0.25.3", "@cosmjs/stream@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.25.3.tgz#061bbda1ec50277d2ce8f78dd6017f6654a1e3c0" - integrity sha512-aGGoe2X5TWIiM8B9YqlhryQwnJz/noRthHdX/k/Wi6nnwvclf8zJS9GAApa2zFVo3A1JcpV0WwCICKzrr4chCg== +"@cosmjs/stream@0.25.5", "@cosmjs/stream@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/stream/-/stream-0.25.5.tgz#5c538fc11e9d3d3ef16849164730dafee180e22e" + integrity sha512-a+0sDNKZTxw9p4j5tl7SI0siMTii7AQot1+5vkH5BkQoAv3C3D8jagPouUz3RUFuh13qftPxPLiHzDFLNSjTnQ== dependencies: xstream "^11.14.0" -"@cosmjs/tendermint-rpc@0.25.3", "@cosmjs/tendermint-rpc@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.25.3.tgz#44b4540edb11ebfb56e14c37c0c3e9d38c712d06" - integrity sha512-bu2gz0YlhIfj1MFraShgRpARJmR3eh9KwdVEwhdzR7Re3E5U+nClgDWH3fxzsoFw9oXmcT/wLGMF1gKxk8PNRw== - dependencies: - "@cosmjs/crypto" "^0.25.3" - "@cosmjs/encoding" "^0.25.3" - "@cosmjs/json-rpc" "^0.25.3" - "@cosmjs/math" "^0.25.3" - "@cosmjs/socket" "^0.25.3" - "@cosmjs/stream" "^0.25.3" +"@cosmjs/tendermint-rpc@0.25.5", "@cosmjs/tendermint-rpc@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.25.5.tgz#5467391f430f648d46d9f39aa4324515affb8578" + integrity sha512-WlUCFVdhbwA3IDA1C858S8WOtLseZLXKTdj5fz1sTKSBmtrig4l1ZMKHHlZRprvmjSpkpbjgSQU+RjjvBd75BA== + dependencies: + "@cosmjs/crypto" "^0.25.5" + "@cosmjs/encoding" "^0.25.5" + "@cosmjs/json-rpc" "^0.25.5" + "@cosmjs/math" "^0.25.5" + "@cosmjs/socket" "^0.25.5" + "@cosmjs/stream" "^0.25.5" axios "^0.21.1" readonly-date "^1.0.0" xstream "^11.14.0" -"@cosmjs/utils@0.25.3", "@cosmjs/utils@^0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.25.3.tgz#e74a85d0be74ef6a9cb3efa45fd219e459cdaa03" - integrity sha512-ASarTIAnrFbvpAUs7QA0gBzYqQy2VFPWOeoM1+9rW9LHY9wbLXySme7v2G/p2oeyP6OZo2gRuHjXyBf6deClsQ== +"@cosmjs/utils@0.25.5", "@cosmjs/utils@^0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.25.5.tgz#6dc9d8de81acb9d49b6d1420f61ea2390f5d5f07" + integrity sha512-U4YdgJadFgXWblthgyXqP28Yw5rsw2IX/cOES0pa6fiB81hoYl2LXqXiuKp2yVPoAgk8JpkFh3i5KchcD9muJg== "@dabh/diagnostics@^2.0.2": version "2.0.2" @@ -1091,11 +1134,6 @@ astral-regex@^2.0.0: resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - async@^3.1.0: version "3.2.0" resolved "https://registry.npmjs.org/async/-/async-3.2.0.tgz" @@ -2191,9 +2229,9 @@ external-editor@^3.1.0: iconv-lite "^0.4.24" tmp "^0.0.33" -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.2.0: @@ -3829,6 +3867,11 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" +pako@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.3.tgz#cdf475e31b678565251406de9e759196a0ea7a43" + integrity sha512-WjR1hOeg+kki3ZIOjaf4b5WVcay1jaliKSYiEaB1XzwhMQZJxRdQRv0V31EKBYlxb4T7SK3hjfc/jxyU64BoSw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" @@ -5185,12 +5228,10 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@^6.2.0: - version "6.2.1" - resolved "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== - dependencies: - async-limiter "~1.0.0" +ws@^7: + version "7.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691" + integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw== xdg-basedir@^4.0.0: version "4.0.0"