Remove laucnhpad

Applies diff from https://github.com/cosmos/cosmjs/pull/1015
This commit is contained in:
Simon Warta 2022-02-08 13:56:18 +01:00
parent 1cfda9dccf
commit 9970f77f18
105 changed files with 110 additions and 9216 deletions

View File

@ -87,10 +87,6 @@ jobs:
wget -O "$HOME/git-lfs.deb" https://packagecloud.io/github/git-lfs/packages/ubuntu/focal/git-lfs_2.12.1_amd64.deb/download.deb wget -O "$HOME/git-lfs.deb" https://packagecloud.io/github/git-lfs/packages/ubuntu/focal/git-lfs_2.12.1_amd64.deb/download.deb
sudo dpkg -i "$HOME/git-lfs.deb" sudo dpkg -i "$HOME/git-lfs.deb"
- checkout - checkout
- run: # start early for less wait time below
name: Start launchpad
command: ./scripts/launchpad/start.sh
background: true
- run: # start early for less wait time below - run: # start early for less wait time below
name: Start wasmd name: Start wasmd
command: ./scripts/wasmd/start.sh command: ./scripts/wasmd/start.sh
@ -142,9 +138,6 @@ jobs:
- run: - run:
name: Install Dependencies name: Install Dependencies
command: yarn install --immutable --immutable-cache --check-cache command: yarn install --immutable --immutable-cache --check-cache
- run:
name: Initialize launchpad (deploy contracts and friends)
command: ./scripts/launchpad/init.sh
- run: - run:
name: Initialize wasmd (deploy contracts and friends) name: Initialize wasmd (deploy contracts and friends)
command: ./scripts/wasmd/init.sh command: ./scripts/wasmd/init.sh
@ -154,7 +147,6 @@ jobs:
- run: - run:
name: Run tests name: Run tests
environment: environment:
LAUNCHPAD_ENABLED: 1
TENDERMINT_ENABLED: 1 TENDERMINT_ENABLED: 1
SOCKETSERVER_ENABLED: 1 SOCKETSERVER_ENABLED: 1
SKIP_BUILD: 1 SKIP_BUILD: 1
@ -173,14 +165,14 @@ jobs:
name: Run CLI examples name: Run CLI examples
working_directory: packages/cli working_directory: packages/cli
environment: environment:
TENDERMINT_ENABLED: 1
SOCKETSERVER_ENABLED: 1
SKIP_BUILD: 1 SKIP_BUILD: 1
WASMD_ENABLED: 1
command: | command: |
yarn node ./bin/cosmwasm-cli --init examples/cosmwasm.ts --code "process.exit(0)" [ "<< parameters.simapp >>" = "simapp42" ] && export SIMAPP42_ENABLED=1 SLOW_SIMAPP42_ENABLED=1
yarn node ./bin/cosmwasm-cli --init examples/delegate.ts --code "process.exit(0)" [ "<< parameters.simapp >>" = "simapp44" ] && export SIMAPP44_ENABLED=1 SLOW_SIMAPP44_ENABLED=1
yarn node ./bin/cosmwasm-cli --init examples/faucet_addresses.ts --code "process.exit(0)" ./run_examples.sh
yarn node ./bin/cosmwasm-cli --init examples/generate_address.ts --code "process.exit(0)"
yarn node ./bin/cosmwasm-cli --init examples/local_faucet.ts --code "process.exit(0)"
yarn node ./bin/cosmwasm-cli --init examples/mask.ts --code "process.exit(0)"
- run: - run:
name: Stop chains name: Stop chains
command: | command: |
@ -189,7 +181,6 @@ jobs:
./scripts/<< parameters.simapp >>/stop.sh ./scripts/<< parameters.simapp >>/stop.sh
./scripts/<< parameters.simapp >>/slow_stop.sh ./scripts/<< parameters.simapp >>/slow_stop.sh
./scripts/wasmd/stop.sh ./scripts/wasmd/stop.sh
./scripts/launchpad/stop.sh
test-node: test-node:
parameters: parameters:
node-version: node-version:
@ -211,10 +202,6 @@ jobs:
wget -O "$HOME/git-lfs.deb" https://packagecloud.io/github/git-lfs/packages/ubuntu/focal/git-lfs_2.12.1_amd64.deb/download.deb wget -O "$HOME/git-lfs.deb" https://packagecloud.io/github/git-lfs/packages/ubuntu/focal/git-lfs_2.12.1_amd64.deb/download.deb
sudo dpkg -i "$HOME/git-lfs.deb" sudo dpkg -i "$HOME/git-lfs.deb"
- checkout - checkout
- run: # start early for less wait time below
name: Start launchpad
command: ./scripts/launchpad/start.sh
background: true
- run: # start early for less wait time below - run: # start early for less wait time below
name: Start wasmd name: Start wasmd
command: ./scripts/wasmd/start.sh command: ./scripts/wasmd/start.sh
@ -271,9 +258,6 @@ jobs:
- run: - run:
name: Install Dependencies name: Install Dependencies
command: yarn install --immutable --immutable-cache --check-cache command: yarn install --immutable --immutable-cache --check-cache
- run:
name: Initialize launchpad (deploy contracts and friends)
command: ./scripts/launchpad/init.sh
- run: - run:
name: Initialize wasmd (deploy contracts and friends) name: Initialize wasmd (deploy contracts and friends)
command: ./scripts/wasmd/init.sh command: ./scripts/wasmd/init.sh
@ -282,7 +266,6 @@ jobs:
command: ./scripts/socketserver/start.sh command: ./scripts/socketserver/start.sh
- run: - run:
environment: environment:
LAUNCHPAD_ENABLED: 1
SIMAPP42_ENABLED: 1 SIMAPP42_ENABLED: 1
SLOW_SIMAPP42_ENABLED: 1 SLOW_SIMAPP42_ENABLED: 1
TENDERMINT_ENABLED: 1 TENDERMINT_ENABLED: 1
@ -301,7 +284,6 @@ jobs:
name: Run CLI examples name: Run CLI examples
working_directory: packages/cli working_directory: packages/cli
environment: environment:
LAUNCHPAD_ENABLED: 1
SIMAPP42_ENABLED: 1 SIMAPP42_ENABLED: 1
SLOW_SIMAPP42_ENABLED: 1 SLOW_SIMAPP42_ENABLED: 1
TENDERMINT_ENABLED: 1 TENDERMINT_ENABLED: 1
@ -317,7 +299,6 @@ jobs:
./scripts/simapp42/stop.sh ./scripts/simapp42/stop.sh
./scripts/simapp42/slow_stop.sh ./scripts/simapp42/slow_stop.sh
./scripts/wasmd/stop.sh ./scripts/wasmd/stop.sh
./scripts/launchpad/stop.sh
test-chrome: test-chrome:
machine: machine:
# We can't use a containerized environment since it requires remote docker to start custom containers. # We can't use a containerized environment since it requires remote docker to start custom containers.
@ -337,10 +318,6 @@ jobs:
wget -O "$HOME/git-lfs.deb" https://packagecloud.io/github/git-lfs/packages/ubuntu/focal/git-lfs_2.12.1_amd64.deb/download.deb wget -O "$HOME/git-lfs.deb" https://packagecloud.io/github/git-lfs/packages/ubuntu/focal/git-lfs_2.12.1_amd64.deb/download.deb
sudo dpkg -i "$HOME/git-lfs.deb" sudo dpkg -i "$HOME/git-lfs.deb"
- checkout - checkout
- run: # start early for less wait time below
name: Start launchpad
command: ./scripts/launchpad/start.sh
background: true
- run: # start early for less wait time below - run: # start early for less wait time below
name: Start wasmd name: Start wasmd
command: ./scripts/wasmd/start.sh command: ./scripts/wasmd/start.sh
@ -392,9 +369,6 @@ jobs:
- run: - run:
name: Install Dependencies name: Install Dependencies
command: yarn install --immutable --immutable-cache --check-cache command: yarn install --immutable --immutable-cache --check-cache
- run:
name: Initialize launchpad (deploy contracts and friends)
command: ./scripts/launchpad/init.sh
- run: - run:
name: Initialize wasmd (deploy contracts and friends) name: Initialize wasmd (deploy contracts and friends)
command: ./scripts/wasmd/init.sh command: ./scripts/wasmd/init.sh
@ -403,7 +377,6 @@ jobs:
command: ./scripts/socketserver/start.sh command: ./scripts/socketserver/start.sh
- run: - run:
environment: environment:
LAUNCHPAD_ENABLED: 1
SIMAPP42_ENABLED: 1 SIMAPP42_ENABLED: 1
SLOW_SIMAPP42_ENABLED: 1 SLOW_SIMAPP42_ENABLED: 1
TENDERMINT_ENABLED: 1 TENDERMINT_ENABLED: 1
@ -419,7 +392,6 @@ jobs:
./scripts/simapp42/stop.sh ./scripts/simapp42/stop.sh
./scripts/simapp42/slow_stop.sh ./scripts/simapp42/slow_stop.sh
./scripts/wasmd/stop.sh ./scripts/wasmd/stop.sh
./scripts/launchpad/stop.sh
coverage: coverage:
machine: machine:
# We can't use a containerized environment since it requires remote docker to start custom containers. # We can't use a containerized environment since it requires remote docker to start custom containers.
@ -438,10 +410,6 @@ jobs:
wget -O "$HOME/git-lfs.deb" https://packagecloud.io/github/git-lfs/packages/ubuntu/focal/git-lfs_2.12.1_amd64.deb/download.deb wget -O "$HOME/git-lfs.deb" https://packagecloud.io/github/git-lfs/packages/ubuntu/focal/git-lfs_2.12.1_amd64.deb/download.deb
sudo dpkg -i "$HOME/git-lfs.deb" sudo dpkg -i "$HOME/git-lfs.deb"
- checkout - checkout
- run: # start early for less wait time below
name: Start launchpad
command: ./scripts/launchpad/start.sh
background: true
- run: # start early for less wait time below - run: # start early for less wait time below
name: Start wasmd name: Start wasmd
command: ./scripts/wasmd/start.sh command: ./scripts/wasmd/start.sh
@ -493,9 +461,6 @@ jobs:
- run: - run:
name: Install Dependencies name: Install Dependencies
command: yarn install --immutable --immutable-cache --check-cache command: yarn install --immutable --immutable-cache --check-cache
- run:
name: Initialize launchpad (deploy contracts and friends)
command: ./scripts/launchpad/init.sh
- run: - run:
name: Initialize wasmd (deploy contracts and friends) name: Initialize wasmd (deploy contracts and friends)
command: ./scripts/wasmd/init.sh command: ./scripts/wasmd/init.sh
@ -504,7 +469,6 @@ jobs:
command: ./scripts/socketserver/start.sh command: ./scripts/socketserver/start.sh
- run: - run:
environment: environment:
LAUNCHPAD_ENABLED: 1
SIMAPP42_ENABLED: 1 SIMAPP42_ENABLED: 1
SLOW_SIMAPP42_ENABLED: 1 SLOW_SIMAPP42_ENABLED: 1
TENDERMINT_ENABLED: 1 TENDERMINT_ENABLED: 1
@ -523,7 +487,6 @@ jobs:
./scripts/simapp42/stop.sh ./scripts/simapp42/stop.sh
./scripts/simapp42/slow_stop.sh ./scripts/simapp42/slow_stop.sh
./scripts/wasmd/stop.sh ./scripts/wasmd/stop.sh
./scripts/launchpad/stop.sh
docs-build: docs-build:
docker: docker:
- image: circleci/node:16.13-bullseye - image: circleci/node:16.13-bullseye

211
.pnp.cjs generated
View File

@ -54,10 +54,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"name": "@cosmjs/json-rpc", "name": "@cosmjs/json-rpc",
"reference": "workspace:packages/json-rpc" "reference": "workspace:packages/json-rpc"
}, },
{
"name": "@cosmjs/launchpad",
"reference": "workspace:packages/launchpad"
},
{ {
"name": "@cosmjs/ledger-amino", "name": "@cosmjs/ledger-amino",
"reference": "workspace:packages/ledger-amino" "reference": "workspace:packages/ledger-amino"
@ -102,7 +98,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@cosmjs/faucet", ["workspace:packages/faucet"]], ["@cosmjs/faucet", ["workspace:packages/faucet"]],
["@cosmjs/faucet-client", ["workspace:packages/faucet-client"]], ["@cosmjs/faucet-client", ["workspace:packages/faucet-client"]],
["@cosmjs/json-rpc", ["workspace:packages/json-rpc"]], ["@cosmjs/json-rpc", ["workspace:packages/json-rpc"]],
["@cosmjs/launchpad", ["workspace:packages/launchpad"]],
["@cosmjs/ledger-amino", ["workspace:packages/ledger-amino"]], ["@cosmjs/ledger-amino", ["workspace:packages/ledger-amino"]],
["@cosmjs/math", ["workspace:packages/math"]], ["@cosmjs/math", ["workspace:packages/math"]],
["@cosmjs/proto-signing", ["workspace:packages/proto-signing"]], ["@cosmjs/proto-signing", ["workspace:packages/proto-signing"]],
@ -254,10 +249,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"@cosmjs/json-rpc", "@cosmjs/json-rpc",
"workspace:packages/json-rpc" "workspace:packages/json-rpc"
], ],
[
"@cosmjs/launchpad",
"workspace:packages/launchpad"
],
[ [
"@cosmjs/ledger-amino", "@cosmjs/ledger-amino",
"workspace:packages/ledger-amino" "workspace:packages/ledger-amino"
@ -2856,7 +2847,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@cosmjs/cosmwasm-stargate", "workspace:packages/cosmwasm-stargate"], ["@cosmjs/cosmwasm-stargate", "workspace:packages/cosmwasm-stargate"],
["@cosmjs/crypto", "workspace:packages/crypto"], ["@cosmjs/crypto", "workspace:packages/crypto"],
["@cosmjs/encoding", "workspace:packages/encoding"], ["@cosmjs/encoding", "workspace:packages/encoding"],
["@cosmjs/launchpad", "workspace:packages/launchpad"],
["@cosmjs/proto-signing", "workspace:packages/proto-signing"], ["@cosmjs/proto-signing", "workspace:packages/proto-signing"],
["@cosmjs/stargate", "workspace:packages/stargate"], ["@cosmjs/stargate", "workspace:packages/stargate"],
["eslint", "npm:7.26.0"], ["eslint", "npm:7.26.0"],
@ -3256,7 +3246,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@cosmjs/crypto", "workspace:packages/crypto"], ["@cosmjs/crypto", "workspace:packages/crypto"],
["@cosmjs/encoding", "workspace:packages/encoding"], ["@cosmjs/encoding", "workspace:packages/encoding"],
["@cosmjs/faucet-client", "workspace:packages/faucet-client"], ["@cosmjs/faucet-client", "workspace:packages/faucet-client"],
["@cosmjs/launchpad", "workspace:packages/launchpad"],
["@cosmjs/math", "workspace:packages/math"], ["@cosmjs/math", "workspace:packages/math"],
["@cosmjs/proto-signing", "workspace:packages/proto-signing"], ["@cosmjs/proto-signing", "workspace:packages/proto-signing"],
["@cosmjs/stargate", "workspace:packages/stargate"], ["@cosmjs/stargate", "workspace:packages/stargate"],
@ -3465,7 +3454,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@cosmjs/faucet", "workspace:packages/faucet"], ["@cosmjs/faucet", "workspace:packages/faucet"],
["@cosmjs/crypto", "workspace:packages/crypto"], ["@cosmjs/crypto", "workspace:packages/crypto"],
["@cosmjs/encoding", "workspace:packages/encoding"], ["@cosmjs/encoding", "workspace:packages/encoding"],
["@cosmjs/launchpad", "workspace:packages/launchpad"],
["@cosmjs/math", "workspace:packages/math"], ["@cosmjs/math", "workspace:packages/math"],
["@cosmjs/proto-signing", "workspace:packages/proto-signing"], ["@cosmjs/proto-signing", "workspace:packages/proto-signing"],
["@cosmjs/stargate", "workspace:packages/stargate"], ["@cosmjs/stargate", "workspace:packages/stargate"],
@ -3588,58 +3576,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "SOFT", "linkType": "SOFT",
}] }]
]], ]],
["@cosmjs/launchpad", [
["workspace:packages/launchpad", {
"packageLocation": "./packages/launchpad/",
"packageDependencies": [
["@cosmjs/launchpad", "workspace:packages/launchpad"],
["@cosmjs/amino", "workspace:packages/amino"],
["@cosmjs/crypto", "workspace:packages/crypto"],
["@cosmjs/encoding", "workspace:packages/encoding"],
["@cosmjs/math", "workspace:packages/math"],
["@cosmjs/utils", "workspace:packages/utils"],
["@istanbuljs/nyc-config-typescript", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:1.0.1"],
["@types/eslint-plugin-prettier", "npm:3.1.0"],
["@types/jasmine", "npm:3.8.1"],
["@types/karma-firefox-launcher", "npm:2.1.0"],
["@types/karma-jasmine", "npm:4.0.0"],
["@types/karma-jasmine-html-reporter", "npm:1.5.1"],
["@types/node", "npm:15.3.1"],
["@typescript-eslint/eslint-plugin", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:4.28.4"],
["@typescript-eslint/parser", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:4.28.4"],
["axios", "npm:0.21.4"],
["eslint", "npm:7.26.0"],
["eslint-config-prettier", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:8.3.0"],
["eslint-import-resolver-node", "npm:0.3.4"],
["eslint-plugin-import", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:2.23.2"],
["eslint-plugin-prettier", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:3.4.0"],
["eslint-plugin-simple-import-sort", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:7.0.0"],
["esm", "npm:3.2.25"],
["fast-deep-equal", "npm:3.1.3"],
["glob", "npm:7.1.7"],
["jasmine", "npm:3.8.0"],
["jasmine-core", "npm:3.8.0"],
["jasmine-spec-reporter", "npm:6.0.0"],
["karma", "npm:6.3.2"],
["karma-chrome-launcher", "npm:3.1.0"],
["karma-firefox-launcher", "npm:2.1.0"],
["karma-jasmine", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:4.0.1"],
["karma-jasmine-html-reporter", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:1.6.0"],
["nyc", "npm:15.1.0"],
["prettier", "npm:2.4.1"],
["readonly-date", "npm:1.0.0"],
["ses", "npm:0.11.1"],
["source-map-support", "npm:0.5.19"],
["stream-browserify", "npm:3.0.0"],
["ts-node", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:8.10.2"],
["typedoc", "virtual:4f1584ad4aba8733a24be7c8aebbffafef25607f2d00f4b314cf96717145c692763628a31c2b85d4686fbb091ff21ebffa3cc337399c042c19a32b9bdb786464#npm:0.22.11"],
["typescript", "patch:typescript@npm%3A4.4.4#~builtin<compat/typescript>::version=4.4.4&hash=ddd1e8"],
["webpack", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:5.37.1"],
["webpack-cli", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:4.7.0"]
],
"linkType": "SOFT",
}]
]],
["@cosmjs/ledger-amino", [ ["@cosmjs/ledger-amino", [
["workspace:packages/ledger-amino", { ["workspace:packages/ledger-amino", {
"packageLocation": "./packages/ledger-amino/", "packageLocation": "./packages/ledger-amino/",
@ -3648,7 +3584,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@cosmjs/amino", "workspace:packages/amino"], ["@cosmjs/amino", "workspace:packages/amino"],
["@cosmjs/crypto", "workspace:packages/crypto"], ["@cosmjs/crypto", "workspace:packages/crypto"],
["@cosmjs/encoding", "workspace:packages/encoding"], ["@cosmjs/encoding", "workspace:packages/encoding"],
["@cosmjs/launchpad", "workspace:packages/launchpad"],
["@cosmjs/math", "workspace:packages/math"], ["@cosmjs/math", "workspace:packages/math"],
["@cosmjs/stargate", "workspace:packages/stargate"], ["@cosmjs/stargate", "workspace:packages/stargate"],
["@cosmjs/utils", "workspace:packages/utils"], ["@cosmjs/utils", "workspace:packages/utils"],
@ -5434,23 +5369,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
], ],
"linkType": "HARD", "linkType": "HARD",
}], }],
["virtual:c40ffa9da9410f1eb88a4aced56c0b7274d5471c24490a910b8d88bd5338ba8e40ff4f18351b2936308c9747c07026dcf363a139309f893ddadcb3906c5c9a66#npm:1.0.3", {
"packageLocation": "./.yarn/__virtual__/@webpack-cli-configtest-virtual-22aaa3961d/0/cache/@webpack-cli-configtest-npm-1.0.3-b6e357f778-4efcca159e.zip/node_modules/@webpack-cli/configtest/",
"packageDependencies": [
["@webpack-cli/configtest", "virtual:c40ffa9da9410f1eb88a4aced56c0b7274d5471c24490a910b8d88bd5338ba8e40ff4f18351b2936308c9747c07026dcf363a139309f893ddadcb3906c5c9a66#npm:1.0.3"],
["@types/webpack", null],
["@types/webpack-cli", null],
["webpack", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:5.37.1"],
["webpack-cli", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:4.7.0"]
],
"packagePeers": [
"@types/webpack-cli",
"@types/webpack",
"webpack-cli",
"webpack"
],
"linkType": "HARD",
}],
["virtual:e545141db94d121e901ffbb09d6c54f53f485ecdf99f9847aaf0f186594082f8121d89d8183c7bf05b17673cbb64c358037fbd69d06a0ad71096f07b595a2385#npm:1.0.3", { ["virtual:e545141db94d121e901ffbb09d6c54f53f485ecdf99f9847aaf0f186594082f8121d89d8183c7bf05b17673cbb64c358037fbd69d06a0ad71096f07b595a2385#npm:1.0.3", {
"packageLocation": "./.yarn/__virtual__/@webpack-cli-configtest-virtual-b614d6a687/0/cache/@webpack-cli-configtest-npm-1.0.3-b6e357f778-4efcca159e.zip/node_modules/@webpack-cli/configtest/", "packageLocation": "./.yarn/__virtual__/@webpack-cli-configtest-virtual-b614d6a687/0/cache/@webpack-cli-configtest-npm-1.0.3-b6e357f778-4efcca159e.zip/node_modules/@webpack-cli/configtest/",
"packageDependencies": [ "packageDependencies": [
@ -5665,20 +5583,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
], ],
"linkType": "HARD", "linkType": "HARD",
}], }],
["virtual:c40ffa9da9410f1eb88a4aced56c0b7274d5471c24490a910b8d88bd5338ba8e40ff4f18351b2936308c9747c07026dcf363a139309f893ddadcb3906c5c9a66#npm:1.2.4", {
"packageLocation": "./.yarn/__virtual__/@webpack-cli-info-virtual-bc51bdae82/0/cache/@webpack-cli-info-npm-1.2.4-e4a2135f37-4e27ccd04c.zip/node_modules/@webpack-cli/info/",
"packageDependencies": [
["@webpack-cli/info", "virtual:c40ffa9da9410f1eb88a4aced56c0b7274d5471c24490a910b8d88bd5338ba8e40ff4f18351b2936308c9747c07026dcf363a139309f893ddadcb3906c5c9a66#npm:1.2.4"],
["@types/webpack-cli", null],
["envinfo", "npm:7.8.1"],
["webpack-cli", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:4.7.0"]
],
"packagePeers": [
"@types/webpack-cli",
"webpack-cli"
],
"linkType": "HARD",
}],
["virtual:e545141db94d121e901ffbb09d6c54f53f485ecdf99f9847aaf0f186594082f8121d89d8183c7bf05b17673cbb64c358037fbd69d06a0ad71096f07b595a2385#npm:1.2.4", { ["virtual:e545141db94d121e901ffbb09d6c54f53f485ecdf99f9847aaf0f186594082f8121d89d8183c7bf05b17673cbb64c358037fbd69d06a0ad71096f07b595a2385#npm:1.2.4", {
"packageLocation": "./.yarn/__virtual__/@webpack-cli-info-virtual-98a9b8985b/0/cache/@webpack-cli-info-npm-1.2.4-e4a2135f37-4e27ccd04c.zip/node_modules/@webpack-cli/info/", "packageLocation": "./.yarn/__virtual__/@webpack-cli-info-virtual-98a9b8985b/0/cache/@webpack-cli-info-npm-1.2.4-e4a2135f37-4e27ccd04c.zip/node_modules/@webpack-cli/info/",
"packageDependencies": [ "packageDependencies": [
@ -5917,23 +5821,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
], ],
"linkType": "HARD", "linkType": "HARD",
}], }],
["virtual:c40ffa9da9410f1eb88a4aced56c0b7274d5471c24490a910b8d88bd5338ba8e40ff4f18351b2936308c9747c07026dcf363a139309f893ddadcb3906c5c9a66#npm:1.4.0", {
"packageLocation": "./.yarn/__virtual__/@webpack-cli-serve-virtual-a77aea3c64/0/cache/@webpack-cli-serve-npm-1.4.0-1f566be693-0b063bed4c.zip/node_modules/@webpack-cli/serve/",
"packageDependencies": [
["@webpack-cli/serve", "virtual:c40ffa9da9410f1eb88a4aced56c0b7274d5471c24490a910b8d88bd5338ba8e40ff4f18351b2936308c9747c07026dcf363a139309f893ddadcb3906c5c9a66#npm:1.4.0"],
["@types/webpack-cli", null],
["@types/webpack-dev-server", null],
["webpack-cli", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:4.7.0"],
["webpack-dev-server", null]
],
"packagePeers": [
"@types/webpack-cli",
"@types/webpack-dev-server",
"webpack-cli",
"webpack-dev-server"
],
"linkType": "HARD",
}],
["virtual:e545141db94d121e901ffbb09d6c54f53f485ecdf99f9847aaf0f186594082f8121d89d8183c7bf05b17673cbb64c358037fbd69d06a0ad71096f07b595a2385#npm:1.4.0", { ["virtual:e545141db94d121e901ffbb09d6c54f53f485ecdf99f9847aaf0f186594082f8121d89d8183c7bf05b17673cbb64c358037fbd69d06a0ad71096f07b595a2385#npm:1.4.0", {
"packageLocation": "./.yarn/__virtual__/@webpack-cli-serve-virtual-222b31cce8/0/cache/@webpack-cli-serve-npm-1.4.0-1f566be693-0b063bed4c.zip/node_modules/@webpack-cli/serve/", "packageLocation": "./.yarn/__virtual__/@webpack-cli-serve-virtual-222b31cce8/0/cache/@webpack-cli-serve-npm-1.4.0-1f566be693-0b063bed4c.zip/node_modules/@webpack-cli/serve/",
"packageDependencies": [ "packageDependencies": [
@ -6999,7 +6886,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@cosmjs/cosmwasm-stargate", "workspace:packages/cosmwasm-stargate"], ["@cosmjs/cosmwasm-stargate", "workspace:packages/cosmwasm-stargate"],
["@cosmjs/crypto", "workspace:packages/crypto"], ["@cosmjs/crypto", "workspace:packages/crypto"],
["@cosmjs/encoding", "workspace:packages/encoding"], ["@cosmjs/encoding", "workspace:packages/encoding"],
["@cosmjs/launchpad", "workspace:packages/launchpad"],
["@cosmjs/proto-signing", "workspace:packages/proto-signing"], ["@cosmjs/proto-signing", "workspace:packages/proto-signing"],
["@cosmjs/stargate", "workspace:packages/stargate"], ["@cosmjs/stargate", "workspace:packages/stargate"],
["eslint", "npm:7.26.0"], ["eslint", "npm:7.26.0"],
@ -11913,25 +11799,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
], ],
"linkType": "HARD", "linkType": "HARD",
}], }],
["virtual:3fc9ad73a25400af56d4125aff61a9fcb0cd6e665fdaa8ac5c8928896a4cae3b678e19c1bb722ba1f7dd8d9840cb8e3e6257c52c511a8ce7cb8e8e6dd26f35f3#npm:5.1.2", {
"packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-52b28a15cd/0/cache/terser-webpack-plugin-npm-5.1.2-59f409825a-0eb0e81f52.zip/node_modules/terser-webpack-plugin/",
"packageDependencies": [
["terser-webpack-plugin", "virtual:3fc9ad73a25400af56d4125aff61a9fcb0cd6e665fdaa8ac5c8928896a4cae3b678e19c1bb722ba1f7dd8d9840cb8e3e6257c52c511a8ce7cb8e8e6dd26f35f3#npm:5.1.2"],
["@types/webpack", null],
["jest-worker", "npm:26.6.2"],
["p-limit", "npm:3.1.0"],
["schema-utils", "npm:3.0.0"],
["serialize-javascript", "npm:5.0.1"],
["source-map", "npm:0.6.1"],
["terser", "npm:5.7.0"],
["webpack", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:5.37.1"]
],
"packagePeers": [
"@types/webpack",
"webpack"
],
"linkType": "HARD",
}],
["virtual:5e82fc811d8fd02f05149cd66fb79c02f16f05bfaf043b01e108813bc4e39a600f5a9f93ce3a86bd2c21b92c8082c2e5b50384c76a2fc4e252d186976e3055e2#npm:5.1.2", { ["virtual:5e82fc811d8fd02f05149cd66fb79c02f16f05bfaf043b01e108813bc4e39a600f5a9f93ce3a86bd2c21b92c8082c2e5b50384c76a2fc4e252d186976e3055e2#npm:5.1.2", {
"packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-1be69a9933/0/cache/terser-webpack-plugin-npm-5.1.2-59f409825a-0eb0e81f52.zip/node_modules/terser-webpack-plugin/", "packageLocation": "./.yarn/__virtual__/terser-webpack-plugin-virtual-1be69a9933/0/cache/terser-webpack-plugin-npm-5.1.2-59f409825a-0eb0e81f52.zip/node_modules/terser-webpack-plugin/",
"packageDependencies": [ "packageDependencies": [
@ -13065,42 +12932,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"webpack-cli" "webpack-cli"
], ],
"linkType": "HARD", "linkType": "HARD",
}],
["virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:5.37.1", {
"packageLocation": "./.yarn/__virtual__/webpack-virtual-3fc9ad73a2/0/cache/webpack-npm-5.37.1-1e75a59f6f-572d3cd617.zip/node_modules/webpack/",
"packageDependencies": [
["webpack", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:5.37.1"],
["@types/eslint-scope", "npm:3.7.0"],
["@types/estree", "npm:0.0.47"],
["@types/webpack-cli", null],
["@webassemblyjs/ast", "npm:1.11.0"],
["@webassemblyjs/wasm-edit", "npm:1.11.0"],
["@webassemblyjs/wasm-parser", "npm:1.11.0"],
["acorn", "npm:8.2.4"],
["browserslist", "npm:4.16.6"],
["chrome-trace-event", "npm:1.0.3"],
["enhanced-resolve", "npm:5.8.2"],
["es-module-lexer", "npm:0.4.1"],
["eslint-scope", "npm:5.1.1"],
["events", "npm:3.3.0"],
["glob-to-regexp", "npm:0.4.1"],
["graceful-fs", "npm:4.2.6"],
["json-parse-better-errors", "npm:1.0.2"],
["loader-runner", "npm:4.2.0"],
["mime-types", "npm:2.1.30"],
["neo-async", "npm:2.6.2"],
["schema-utils", "npm:3.0.0"],
["tapable", "npm:2.2.0"],
["terser-webpack-plugin", "virtual:3fc9ad73a25400af56d4125aff61a9fcb0cd6e665fdaa8ac5c8928896a4cae3b678e19c1bb722ba1f7dd8d9840cb8e3e6257c52c511a8ce7cb8e8e6dd26f35f3#npm:5.1.2"],
["watchpack", "npm:2.2.0"],
["webpack-cli", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:4.7.0"],
["webpack-sources", "npm:2.2.0"]
],
"packagePeers": [
"@types/webpack-cli",
"webpack-cli"
],
"linkType": "HARD",
}] }]
]], ]],
["webpack-cli", [ ["webpack-cli", [
@ -13698,48 +13529,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"webpack" "webpack"
], ],
"linkType": "HARD", "linkType": "HARD",
}],
["virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:4.7.0", {
"packageLocation": "./.yarn/__virtual__/webpack-cli-virtual-c40ffa9da9/0/cache/webpack-cli-npm-4.7.0-cb3d7c34ff-cecfb321b9.zip/node_modules/webpack-cli/",
"packageDependencies": [
["webpack-cli", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:4.7.0"],
["@discoveryjs/json-ext", "npm:0.5.3"],
["@types/webpack", null],
["@types/webpack-bundle-analyzer", null],
["@types/webpack-cli__generators", null],
["@types/webpack-cli__migrate", null],
["@types/webpack-dev-server", null],
["@webpack-cli/configtest", "virtual:c40ffa9da9410f1eb88a4aced56c0b7274d5471c24490a910b8d88bd5338ba8e40ff4f18351b2936308c9747c07026dcf363a139309f893ddadcb3906c5c9a66#npm:1.0.3"],
["@webpack-cli/generators", null],
["@webpack-cli/info", "virtual:c40ffa9da9410f1eb88a4aced56c0b7274d5471c24490a910b8d88bd5338ba8e40ff4f18351b2936308c9747c07026dcf363a139309f893ddadcb3906c5c9a66#npm:1.2.4"],
["@webpack-cli/migrate", null],
["@webpack-cli/serve", "virtual:c40ffa9da9410f1eb88a4aced56c0b7274d5471c24490a910b8d88bd5338ba8e40ff4f18351b2936308c9747c07026dcf363a139309f893ddadcb3906c5c9a66#npm:1.4.0"],
["colorette", "npm:1.2.2"],
["commander", "npm:7.2.0"],
["execa", "npm:5.0.0"],
["fastest-levenshtein", "npm:1.0.12"],
["import-local", "npm:3.0.2"],
["interpret", "npm:2.2.0"],
["rechoir", "npm:0.7.0"],
["v8-compile-cache", "npm:2.3.0"],
["webpack", "virtual:db46c448d4270fdb3b876d89bfac4ef71013dbbf936984e3bdf75d2625d01d582344f08a024c01ef1b5ce6d880baba103eab42675713e0991f42e83024f39f25#npm:5.37.1"],
["webpack-bundle-analyzer", null],
["webpack-dev-server", null],
["webpack-merge", "npm:5.7.3"]
],
"packagePeers": [
"@types/webpack-bundle-analyzer",
"@types/webpack-cli__generators",
"@types/webpack-cli__migrate",
"@types/webpack-dev-server",
"@types/webpack",
"@webpack-cli/generators",
"@webpack-cli/migrate",
"webpack-bundle-analyzer",
"webpack-dev-server",
"webpack"
],
"linkType": "HARD",
}] }]
]], ]],
["webpack-merge", [ ["webpack-merge", [

View File

@ -13,6 +13,4 @@ plugins:
pnpMode: loose pnpMode: loose
preferInteractive: true
yarnPath: .yarn/releases/yarn-3.1.0.cjs yarnPath: .yarn/releases/yarn-3.1.0.cjs

View File

@ -54,7 +54,6 @@ Here are some of them to get an idea:
| Package | Description | Latest | | Package | Description | Latest |
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| [@cosmjs/stargate](packages/stargate) | A client library for the Cosmos SDK 0.40 (cosmoshub-4), 0.41 and 0.42 (Stargate) | [![npm version](https://img.shields.io/npm/v/@cosmjs/stargate.svg)](https://www.npmjs.com/package/@cosmjs/stargate) | | [@cosmjs/stargate](packages/stargate) | A client library for the Cosmos SDK 0.40 (cosmoshub-4), 0.41 and 0.42 (Stargate) | [![npm version](https://img.shields.io/npm/v/@cosmjs/stargate.svg)](https://www.npmjs.com/package/@cosmjs/stargate) |
| [@cosmjs/launchpad](packages/launchpad) | A client library for the Cosmos SDK 0.37 (cosmoshub-3), 0.38 and 0.39 (Launchpad) | [![npm version](https://img.shields.io/npm/v/@cosmjs/launchpad.svg)](https://www.npmjs.com/package/@cosmjs/launchpad) |
| [@cosmjs/faucet](packages/faucet) | A faucet application for node.js | [![npm version](https://img.shields.io/npm/v/@cosmjs/faucet.svg)](https://www.npmjs.com/package/@cosmjs/faucet) | | [@cosmjs/faucet](packages/faucet) | A faucet application for node.js | [![npm version](https://img.shields.io/npm/v/@cosmjs/faucet.svg)](https://www.npmjs.com/package/@cosmjs/faucet) |
| [@cosmjs/cosmwasm-stargate](packages/cosmwasm-stargate) | Client for Stargate chains with the CosmWasm module enabled | [![npm version](https://img.shields.io/npm/v/@cosmjs/cosmwasm-stargate.svg)](https://www.npmjs.com/package/@cosmjs/cosmwasm-stargate) | | [@cosmjs/cosmwasm-stargate](packages/cosmwasm-stargate) | Client for Stargate chains with the CosmWasm module enabled | [![npm version](https://img.shields.io/npm/v/@cosmjs/cosmwasm-stargate.svg)](https://www.npmjs.com/package/@cosmjs/cosmwasm-stargate) |
| [@cosmjs/crypto](packages/crypto) | Cryptography for blockchain projects, e.g. hashing (SHA-2, Keccak256, Ripemd160), signing (secp256k1, ed25519), HD key derivation (BIPO39, SLIP-0010), KDFs and symmetric encryption for key storage (PBKDF2, Argon2, XChaCha20Poly1305) | [![npm version](https://img.shields.io/npm/v/@cosmjs/crypto.svg)](https://www.npmjs.com/package/@cosmjs/crypto) | | [@cosmjs/crypto](packages/crypto) | Cryptography for blockchain projects, e.g. hashing (SHA-2, Keccak256, Ripemd160), signing (secp256k1, ed25519), HD key derivation (BIPO39, SLIP-0010), KDFs and symmetric encryption for key storage (PBKDF2, Argon2, XChaCha20Poly1305) | [![npm version](https://img.shields.io/npm/v/@cosmjs/crypto.svg)](https://www.npmjs.com/package/@cosmjs/crypto) |

View File

@ -44,7 +44,6 @@
"@cosmjs/cosmwasm-stargate": "workspace:packages/cosmwasm-stargate", "@cosmjs/cosmwasm-stargate": "workspace:packages/cosmwasm-stargate",
"@cosmjs/crypto": "workspace:packages/crypto", "@cosmjs/crypto": "workspace:packages/crypto",
"@cosmjs/encoding": "workspace:packages/encoding", "@cosmjs/encoding": "workspace:packages/encoding",
"@cosmjs/launchpad": "workspace:packages/launchpad",
"@cosmjs/proto-signing": "workspace:packages/proto-signing", "@cosmjs/proto-signing": "workspace:packages/proto-signing",
"@cosmjs/stargate": "workspace:packages/stargate", "@cosmjs/stargate": "workspace:packages/stargate",
"eslint": "^7.5", "eslint": "^7.5",

View File

@ -2,8 +2,7 @@
[![npm version](https://img.shields.io/npm/v/@cosmjs/amino.svg)](https://www.npmjs.com/package/@cosmjs/amino) [![npm version](https://img.shields.io/npm/v/@cosmjs/amino.svg)](https://www.npmjs.com/package/@cosmjs/amino)
Helpers for Amino based signing which are shared between @cosmjs/launchpad and Helpers for Amino for @cosmjs/stargate.
@cosmjs/stargate.
## License ## License

View File

@ -1,7 +1,7 @@
{ {
"name": "@cosmjs/amino", "name": "@cosmjs/amino",
"version": "0.27.1", "version": "0.27.1",
"description": "Helpers for Amino based signing which are shared between @cosmjs/launchpad and @cosmjs/stargate.", "description": "Helpers for Amino based signing.",
"contributors": [ "contributors": [
"Simon Warta <webmaster128@users.noreply.github.com>" "Simon Warta <webmaster128@users.noreply.github.com>"
], ],

View File

@ -45,37 +45,29 @@ $ cosmwasm-cli
```ts ```ts
// Get account information // Get account information
const { account_number, sequence } = (await client.authAccounts(faucetAddress)) const account = await client.getAccount(faucetAddress);
.result.value;
// Craft a send transaction // Craft a send transaction
const emptyAddress = Bech32.encode("cosmos", Random.getBytes(20)); const emptyAddress = Bech32.encode("cosmos", Random.getBytes(20));
const memo = "My first contract on chain"; const memo = "My very first tx!";
const sendTokensMsg: MsgSend = { const msgSend = {
type: "cosmos-sdk/MsgSend", fromAddress: faucetAddress,
value: { toAddress: emptyAddress,
from_address: faucetAddress, amount: coins(1234, "ucosm"),
to_address: emptyAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
}; };
const signDoc = makeSignDoc( const msgAny = {
[sendTokensMsg], typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: msgSend,
};
// Broadcast and sign the transaction
const broadcastResult = await client.signAndBroadcast(
faucetAddress,
[msgAny],
defaultFee, defaultFee,
defaultNetworkId,
memo, memo,
account_number,
sequence,
); );
const { signed, signature } = await wallet.signAmino(faucetAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const broadcastResult = await client.broadcastTx(signedTx);
``` ```
## Extended helpers ## Extended helpers

View File

@ -1,45 +1,48 @@
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { import {
coin, coin,
coins, MsgDelegateEncodeObject,
makeSignDoc, SigningStargateClient,
makeStdTx, calculateFee,
CosmosClient, assertIsDeliverTxSuccess,
MsgDelegate, GasPrice,
Secp256k1HdWallet, } from "@cosmjs/stargate";
} from "@cosmjs/launchpad";
const wallet = await Secp256k1HdWallet.fromMnemonic( // Wallet
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(
"enlist hip relief stomach skate base shallow young switch frequent cry park", "enlist hip relief stomach skate base shallow young switch frequent cry park",
); );
const [{ address: senderAddress }] = await wallet.getAccounts(); const [{ address: signerAddress }] = await wallet.getAccounts();
console.log("Signer address:", signerAddress);
const client = new CosmosClient("http://localhost:1317"); // Network config
const rpcEndpoint = "ws://localhost:26658";
const gasPrice = GasPrice.fromString("0.025ucosm");
const msg: MsgDelegate = { // Setup client
type: "cosmos-sdk/MsgDelegate", const client = await SigningStargateClient.connectWithSigner(rpcEndpoint, wallet);
// Send delegate transaction
const msg: MsgDelegateEncodeObject = {
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: { value: {
delegator_address: senderAddress, delegatorAddress: signerAddress,
// To get the proper validator address, start the demo chain (./scripts/launchpad/start.sh), then run: // To get the proper validator address, start the demo chain (./scripts/simapp42/start.sh), then run:
// curl http://localhost:1317/staking/validators | jq '.result[0].operator_address' // curl http://localhost:1318/staking/validators | jq '.result[0].operator_address'
validator_address: "cosmosvaloper1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r3arurr", validatorAddress: "cosmosvaloper1urk9gy7cfws0ak9x5nu7lx4un9n6gqkrp230jk",
amount: coin(300000, "ustake"), amount: coin(300000, "ustake"),
}, },
}; };
const fee = { const fee = calculateFee(180_000, gasPrice);
amount: coins(2000, "ucosm"),
gas: "180000", // 180k
};
const memo = "Use your power wisely"; const memo = "Use your power wisely";
const chainId = await client.getChainId(); const chainId = await client.getChainId();
console.log("Connected to chain:", chainId); console.log("Connected to chain:", chainId);
const { accountNumber, sequence } = await client.getSequence(senderAddress); const result = await client.signAndBroadcast(signerAddress, [msg], fee, memo);
console.log("Account/sequence:", accountNumber, sequence);
const signDoc = makeSignDoc([msg], fee, chainId, memo, accountNumber, sequence);
const { signed, signature } = await wallet.signAmino(senderAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const result = await client.broadcastTx(signedTx);
console.log("Broadcast result:", result); console.log("Broadcast result:", result);
assertIsDeliverTxSuccess(result);
console.log("Successfully broadcasted:", result);
client.disconnect();

View File

@ -1,7 +1,11 @@
import { LcdClient, Secp256k1HdWallet, StdFee } from "@cosmjs/launchpad"; import { StdFee, SigningStargateClient } from "@cosmjs/stargate";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
import { coins } from "@cosmjs/amino";
import { Bech32 } from "@cosmjs/encoding";
import { Random } from "@cosmjs/crypto";
const defaultHttpUrl = "http://localhost:1317"; const defaultHttpUrl = "http://localhost:26658";
const defaultNetworkId = "testing";
const defaultFee: StdFee = { const defaultFee: StdFee = {
amount: [ amount: [
{ {
@ -16,5 +20,5 @@ const faucetMnemonic =
"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";
const faucetAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6"; const faucetAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6";
const wallet = await Secp256k1HdWallet.fromMnemonic(faucetMnemonic); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucetMnemonic);
const client = new LcdClient(defaultHttpUrl); const client = await SigningStargateClient.connectWithSigner(defaultHttpUrl, wallet);

View File

@ -45,7 +45,6 @@
"@cosmjs/crypto": "workspace:packages/crypto", "@cosmjs/crypto": "workspace:packages/crypto",
"@cosmjs/encoding": "workspace:packages/encoding", "@cosmjs/encoding": "workspace:packages/encoding",
"@cosmjs/faucet-client": "workspace:packages/faucet-client", "@cosmjs/faucet-client": "workspace:packages/faucet-client",
"@cosmjs/launchpad": "workspace:packages/launchpad",
"@cosmjs/math": "workspace:packages/math", "@cosmjs/math": "workspace:packages/math",
"@cosmjs/proto-signing": "workspace:packages/proto-signing", "@cosmjs/proto-signing": "workspace:packages/proto-signing",
"@cosmjs/stargate": "workspace:packages/stargate", "@cosmjs/stargate": "workspace:packages/stargate",

View File

@ -5,7 +5,7 @@ command -v shellcheck >/dev/null && shellcheck "$0"
if [ -n "${WASMD_ENABLED:-}" ]; then if [ -n "${WASMD_ENABLED:-}" ]; then
yarn node ./bin/cosmwasm-cli --init examples/cosmwasm.ts --code "process.exit(0)" yarn node ./bin/cosmwasm-cli --init examples/cosmwasm.ts --code "process.exit(0)"
fi fi
if [ -n "${LAUNCHPAD_ENABLED:-}" ]; then if [ -n "${SIMAPP42_ENABLED:-}" ]; then
yarn node ./bin/cosmwasm-cli --init examples/delegate.ts --code "process.exit(0)" yarn node ./bin/cosmwasm-cli --init examples/delegate.ts --code "process.exit(0)"
fi fi
yarn node ./bin/cosmwasm-cli --init examples/faucet_addresses.ts --code "process.exit(0)" yarn node ./bin/cosmwasm-cli --init examples/faucet_addresses.ts --code "process.exit(0)"

View File

@ -8,7 +8,7 @@ First of all you will need an instance of wasmd running. From the root directory
of this repository: of this repository:
```sh ```sh
./scripts/launchpad/start.sh && ./scripts/launchpad/init.sh ./scripts/wasmd/start.sh && ./scripts/wasmd/init.sh
``` ```
You will also need a faucet. From the root directory of this repository: You will also need a faucet. From the root directory of this repository:

View File

@ -10,7 +10,7 @@ describe("FaucetClient", () => {
const faucetUrl = "http://localhost:8000"; const faucetUrl = "http://localhost:8000";
const primaryToken = "ucosm"; const primaryToken = "ucosm";
const secondaryToken = "ustake"; const secondaryToken = "ustake";
const defaultAddress = "cosmos14qemq0vw6y3gc3u3e0aty2e764u4gs5le3hada"; const defaultAddress = "wasm14qemq0vw6y3gc3u3e0aty2e764u4gs5lndxgyk";
it("can be constructed", () => { it("can be constructed", () => {
// http // http

View File

@ -41,7 +41,6 @@
"dependencies": { "dependencies": {
"@cosmjs/crypto": "workspace:packages/crypto", "@cosmjs/crypto": "workspace:packages/crypto",
"@cosmjs/encoding": "workspace:packages/encoding", "@cosmjs/encoding": "workspace:packages/encoding",
"@cosmjs/launchpad": "workspace:packages/launchpad",
"@cosmjs/math": "workspace:packages/math", "@cosmjs/math": "workspace:packages/math",
"@cosmjs/proto-signing": "workspace:packages/proto-signing", "@cosmjs/proto-signing": "workspace:packages/proto-signing",
"@cosmjs/stargate": "workspace:packages/stargate", "@cosmjs/stargate": "workspace:packages/stargate",

View File

@ -18,5 +18,5 @@ export async function generate(args: readonly string[]): Promise<void> {
console.info(`FAUCET_PATH_PATTERN="${constants.pathPattern}"`); console.info(`FAUCET_PATH_PATTERN="${constants.pathPattern}"`);
// Log the addresses // Log the addresses
await createWallets(mnemonic, pathBuilder, constants.addressPrefix, constants.concurrency, true, true); await createWallets(mnemonic, pathBuilder, constants.addressPrefix, constants.concurrency, true);
} }

View File

@ -1,4 +1,3 @@
import { CosmosClient } from "@cosmjs/launchpad";
import { StargateClient } from "@cosmjs/stargate"; import { StargateClient } from "@cosmjs/stargate";
import { Webserver } from "../api/webserver"; import { Webserver } from "../api/webserver";
@ -17,14 +16,8 @@ export async function start(args: readonly string[]): Promise<void> {
// Connection // Connection
const blockchainBaseUrl = args[0]; const blockchainBaseUrl = args[0];
console.info(`Connecting to blockchain ${blockchainBaseUrl} ...`); console.info(`Connecting to blockchain ${blockchainBaseUrl} ...`);
let chainId; const client = await StargateClient.connect(blockchainBaseUrl);
let stargate = true; const chainId = await client.getChainId();
try {
chainId = await (await StargateClient.connect(blockchainBaseUrl)).getChainId();
} catch (_error) {
chainId = await new CosmosClient(blockchainBaseUrl).getChainId();
stargate = false;
}
console.info(`Connected to network: ${chainId}`); console.info(`Connected to network: ${chainId}`);
// Faucet // Faucet
@ -38,7 +31,6 @@ export async function start(args: readonly string[]): Promise<void> {
constants.mnemonic, constants.mnemonic,
pathBuilder, pathBuilder,
constants.concurrency, constants.concurrency,
stargate,
logging, logging,
); );
const chainTokens = faucet.configuredTokens(); const chainTokens = faucet.configuredTokens();

View File

@ -1,4 +1,4 @@
import { CosmosFeeTable, GasLimits, GasPrice } from "@cosmjs/launchpad"; import { GasPrice } from "@cosmjs/stargate";
import { TokenConfiguration } from "./tokenmanager"; import { TokenConfiguration } from "./tokenmanager";
import { parseBankTokens } from "./tokens"; import { parseBankTokens } from "./tokens";
@ -6,9 +6,7 @@ import { parseBankTokens } from "./tokens";
export const binaryName = "cosmos-faucet"; export const binaryName = "cosmos-faucet";
export const memo: string | undefined = process.env.FAUCET_MEMO; export const memo: string | undefined = process.env.FAUCET_MEMO;
export const gasPrice = GasPrice.fromString(process.env.FAUCET_GAS_PRICE || "0.025ucosm"); export const gasPrice = GasPrice.fromString(process.env.FAUCET_GAS_PRICE || "0.025ucosm");
export const gasLimits: GasLimits<CosmosFeeTable> = { export const gasLimitSend = parseInt(process.env.FAUCET_GAS_LIMIT || "200000", 10);
send: parseInt(process.env.FAUCET_GAS_LIMIT || "80000", 10),
};
export const concurrency: number = Number.parseInt(process.env.FAUCET_CONCURRENCY || "", 10) || 5; export const concurrency: number = Number.parseInt(process.env.FAUCET_CONCURRENCY || "", 10) || 5;
export const port: number = Number.parseInt(process.env.FAUCET_PORT || "", 10) || 8000; export const port: number = Number.parseInt(process.env.FAUCET_PORT || "", 10) || 8000;
export const mnemonic: string | undefined = process.env.FAUCET_MNEMONIC; export const mnemonic: string | undefined = process.env.FAUCET_MNEMONIC;

View File

@ -1,4 +1,4 @@
import { Coin } from "@cosmjs/launchpad"; import { Coin } from "@cosmjs/stargate";
import { MinimalAccount, SendJob } from "./types"; import { MinimalAccount, SendJob } from "./types";

View File

@ -1,18 +1,11 @@
import { Random } from "@cosmjs/crypto"; import { Random } from "@cosmjs/crypto";
import { Bech32 } from "@cosmjs/encoding"; import { Bech32 } from "@cosmjs/encoding";
import { CosmosClient } from "@cosmjs/launchpad";
import { makeCosmoshubPath, StargateClient } from "@cosmjs/stargate"; import { makeCosmoshubPath, StargateClient } from "@cosmjs/stargate";
import { assert } from "@cosmjs/utils"; import { assert } from "@cosmjs/utils";
import { Faucet } from "./faucet"; import { Faucet } from "./faucet";
import { TokenConfiguration } from "./tokenmanager"; import { TokenConfiguration } from "./tokenmanager";
function pendingWithoutLaunchpad(): void {
if (!process.env.LAUNCHPAD_ENABLED) {
return pending("Set LAUNCHPAD_ENABLED to enable Launchpad node-based tests");
}
}
function pendingWithoutSimapp(): void { function pendingWithoutSimapp(): void {
if (!process.env.SIMAPP42_ENABLED && !process.env.SIMAPP44_ENABLED) { if (!process.env.SIMAPP42_ENABLED && !process.env.SIMAPP44_ENABLED) {
return pending("Set SIMAPP42_ENABLED or SIMAPP44_ENABLED to enabled Stargate node-based tests"); return pending("Set SIMAPP42_ENABLED or SIMAPP44_ENABLED to enabled Stargate node-based tests");
@ -34,237 +27,20 @@ const faucetMnemonic =
describe("Faucet", () => { describe("Faucet", () => {
const pathBuilder = makeCosmoshubPath; const pathBuilder = makeCosmoshubPath;
describe("launchpad", () => { const apiUrl = "localhost:26658";
const apiUrl = "http://localhost:1317"; const stargate = true;
const stargate = false; let originalEnvVariable: string | undefined;
describe("constructor", () => { beforeAll(() => {
it("can be constructed", async () => { originalEnvVariable = process.env.FAUCET_CREDIT_AMOUNT_USTAKE;
pendingWithoutLaunchpad(); process.env.FAUCET_CREDIT_AMOUNT_USTAKE = "100000";
const faucet = await Faucet.make( });
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
pathBuilder,
3,
stargate,
);
expect(faucet).toBeTruthy();
});
});
describe("availableTokens", () => { afterAll(() => {
it("is empty when no tokens are configured", async () => { process.env.FAUCET_CREDIT_AMOUNT_USTAKE = originalEnvVariable;
pendingWithoutLaunchpad();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
{ bankTokens: [] },
faucetMnemonic,
pathBuilder,
3,
stargate,
);
const tickers = await faucet.availableTokens();
expect(tickers).toEqual([]);
});
it("is not empty with default token config", async () => {
pendingWithoutLaunchpad();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
pathBuilder,
3,
stargate,
);
const tickers = await faucet.availableTokens();
expect(tickers).toEqual(["ucosm", "ustake"]);
});
});
describe("send", () => {
it("can send bank token", async () => {
pendingWithoutLaunchpad();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
pathBuilder,
3,
stargate,
);
const recipient = makeRandomAddress();
await faucet.send({
amount: {
amount: "23456",
denom: "ucosm",
},
sender: faucet.holderAddress,
recipient: recipient,
});
const readOnlyClient = new CosmosClient(apiUrl);
const account = await readOnlyClient.getAccount(recipient);
assert(account);
expect(account.balance).toEqual([
{
amount: "23456",
denom: "ucosm",
},
]);
});
});
describe("refill", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
pathBuilder,
3,
stargate,
);
await faucet.refill();
const readOnlyClient = new CosmosClient(apiUrl);
const distributorBalance = (await readOnlyClient.getAccount(faucet.distributorAddresses[0]))?.balance;
assert(distributorBalance);
expect(distributorBalance).toEqual([
jasmine.objectContaining({
denom: "ucosm",
}),
jasmine.objectContaining({
denom: "ustake",
}),
]);
expect(Number.parseInt(distributorBalance[0].amount, 10)).toBeGreaterThanOrEqual(80_000000);
expect(Number.parseInt(distributorBalance[1].amount, 10)).toBeGreaterThanOrEqual(80_000000);
});
});
describe("credit", () => {
it("works for fee token", async () => {
pendingWithoutLaunchpad();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
pathBuilder,
3,
stargate,
);
const recipient = makeRandomAddress();
await faucet.credit(recipient, "ucosm");
const readOnlyClient = new CosmosClient(apiUrl);
const account = await readOnlyClient.getAccount(recipient);
assert(account);
expect(account.balance).toEqual([
{
amount: "10000000",
denom: "ucosm",
},
]);
});
it("works for stake token", async () => {
pendingWithoutLaunchpad();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
pathBuilder,
3,
stargate,
);
const recipient = makeRandomAddress();
await faucet.credit(recipient, "ustake");
const readOnlyClient = new CosmosClient(apiUrl);
const account = await readOnlyClient.getAccount(recipient);
assert(account);
expect(account.balance).toEqual([
{
amount: "10000000",
denom: "ustake",
},
]);
});
});
describe("configuredTokens", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
pathBuilder,
3,
stargate,
);
const tickers = faucet.configuredTokens();
expect(tickers).toEqual(["ucosm", "ustake"]);
});
});
describe("loadAccounts", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const faucet = await Faucet.make(
apiUrl,
defaultAddressPrefix,
defaultTokenConfig,
faucetMnemonic,
pathBuilder,
1,
stargate,
);
const accounts = await faucet.loadAccounts();
const readOnlyClient = new CosmosClient(apiUrl);
const expectedHolderAccount = await readOnlyClient.getAccount(faucet.holderAddress);
const expectedDistributorAccount = await readOnlyClient.getAccount(faucet.distributorAddresses[0]);
assert(expectedHolderAccount);
assert(expectedDistributorAccount);
expect(accounts).toEqual([
jasmine.objectContaining({
address: expectedHolderAccount.address,
balance: expectedHolderAccount.balance,
}),
jasmine.objectContaining({
address: expectedDistributorAccount.address,
balance: expectedDistributorAccount.balance,
}),
]);
});
});
}); });
describe("stargate", () => { describe("stargate", () => {
const apiUrl = "localhost:26658";
const stargate = true;
let originalEnvVariable: string | undefined;
beforeAll(() => {
originalEnvVariable = process.env.FAUCET_CREDIT_AMOUNT_USTAKE;
process.env.FAUCET_CREDIT_AMOUNT_USTAKE = "100000";
});
afterAll(() => {
process.env.FAUCET_CREDIT_AMOUNT_USTAKE = originalEnvVariable;
});
describe("constructor", () => { describe("constructor", () => {
it("can be constructed", async () => { it("can be constructed", async () => {
pendingWithoutSimapp(); pendingWithoutSimapp();

View File

@ -1,8 +1,3 @@
import {
assertIsBroadcastTxSuccess as assertIsBroadcastTxSuccessLaunchpad,
CosmosClient,
SigningCosmosClient,
} from "@cosmjs/launchpad";
import { import {
assertIsDeliverTxSuccess as assertIsDeliverTxSuccessStargate, assertIsDeliverTxSuccess as assertIsDeliverTxSuccessStargate,
calculateFee, calculateFee,
@ -30,19 +25,11 @@ export class Faucet {
mnemonic: string, mnemonic: string,
pathBuilder: PathBuilder, pathBuilder: PathBuilder,
numberOfDistributors: number, numberOfDistributors: number,
stargate = true,
logging = false, logging = false,
): Promise<Faucet> { ): Promise<Faucet> {
const wallets = await createWallets( const wallets = await createWallets(mnemonic, pathBuilder, addressPrefix, numberOfDistributors, logging);
mnemonic,
pathBuilder,
addressPrefix,
numberOfDistributors,
stargate,
logging,
);
const clients = await createClients(apiUrl, wallets); const clients = await createClients(apiUrl, wallets);
const readonlyClient = stargate ? await StargateClient.connect(apiUrl) : new CosmosClient(apiUrl); const readonlyClient = await StargateClient.connect(apiUrl);
return new Faucet(addressPrefix, config, clients, readonlyClient, logging); return new Faucet(addressPrefix, config, clients, readonlyClient, logging);
} }
@ -52,16 +39,16 @@ export class Faucet {
private readonly tokenConfig: TokenConfiguration; private readonly tokenConfig: TokenConfiguration;
private readonly tokenManager: TokenManager; private readonly tokenManager: TokenManager;
private readonly readOnlyClient: CosmosClient | StargateClient; private readonly readOnlyClient: StargateClient;
private readonly clients: { [senderAddress: string]: SigningCosmosClient | SigningStargateClient }; private readonly clients: { [senderAddress: string]: SigningStargateClient };
private readonly logging: boolean; private readonly logging: boolean;
private creditCount = 0; private creditCount = 0;
private constructor( private constructor(
addressPrefix: string, addressPrefix: string,
config: TokenConfiguration, config: TokenConfiguration,
clients: ReadonlyArray<readonly [string, SigningCosmosClient | SigningStargateClient]>, clients: ReadonlyArray<readonly [string, SigningStargateClient]>,
readonlyClient: CosmosClient | StargateClient, readonlyClient: StargateClient,
logging = false, logging = false,
) { ) {
this.addressPrefix = addressPrefix; this.addressPrefix = addressPrefix;
@ -94,11 +81,7 @@ export class Faucet {
*/ */
public async send(job: SendJob): Promise<void> { public async send(job: SendJob): Promise<void> {
const client = this.clients[job.sender]; const client = this.clients[job.sender];
if (client instanceof SigningCosmosClient) { const fee = calculateFee(constants.gasLimitSend, constants.gasPrice);
const result = await client.sendTokens(job.recipient, [job.amount], constants.memo);
return assertIsBroadcastTxSuccessLaunchpad(result);
}
const fee = calculateFee(constants.gasLimits.send, constants.gasPrice);
const result = await client.sendTokens(job.sender, job.recipient, [job.amount], fee, constants.memo); const result = await client.sendTokens(job.sender, job.recipient, [job.amount], fee, constants.memo);
assertIsDeliverTxSuccessStargate(result); assertIsDeliverTxSuccessStargate(result);
} }
@ -122,11 +105,7 @@ export class Faucet {
} }
public async loadAccount(address: string): Promise<MinimalAccount> { public async loadAccount(address: string): Promise<MinimalAccount> {
const balance = const balance = await this.readOnlyClient.getAllBalances(address);
this.readOnlyClient instanceof CosmosClient
? (await this.readOnlyClient.getAccount(address))?.balance ?? []
: await this.readOnlyClient.getAllBalances(address);
return { return {
address: address, address: address,
balance: balance, balance: balance,

View File

@ -1,9 +1,7 @@
import { pathToString } from "@cosmjs/crypto"; import { pathToString } from "@cosmjs/crypto";
import { Secp256k1HdWallet, SigningCosmosClient } from "@cosmjs/launchpad"; import { DirectSecp256k1HdWallet, OfflineSigner } from "@cosmjs/proto-signing";
import { DirectSecp256k1HdWallet, isOfflineDirectSigner, OfflineSigner } from "@cosmjs/proto-signing";
import { SigningStargateClient } from "@cosmjs/stargate"; import { SigningStargateClient } from "@cosmjs/stargate";
import * as constants from "./constants";
import { PathBuilder } from "./pathbuilder"; import { PathBuilder } from "./pathbuilder";
export async function createWallets( export async function createWallets(
@ -11,17 +9,18 @@ export async function createWallets(
pathBuilder: PathBuilder, pathBuilder: PathBuilder,
addressPrefix: string, addressPrefix: string,
numberOfDistributors: number, numberOfDistributors: number,
stargate: boolean,
logging: boolean, logging: boolean,
): Promise<ReadonlyArray<readonly [string, OfflineSigner]>> { ): Promise<ReadonlyArray<readonly [string, OfflineSigner]>> {
const createWallet = stargate ? DirectSecp256k1HdWallet.fromMnemonic : Secp256k1HdWallet.fromMnemonic;
const wallets = new Array<readonly [string, OfflineSigner]>(); const wallets = new Array<readonly [string, OfflineSigner]>();
// first account is the token holder // first account is the token holder
const numberOfIdentities = 1 + numberOfDistributors; const numberOfIdentities = 1 + numberOfDistributors;
for (let i = 0; i < numberOfIdentities; i++) { for (let i = 0; i < numberOfIdentities; i++) {
const path = pathBuilder(i); const path = pathBuilder(i);
const wallet = await createWallet(mnemonic, { hdPaths: [path], prefix: addressPrefix }); const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, {
hdPaths: [path],
prefix: addressPrefix,
});
const [{ address }] = await wallet.getAccounts(); const [{ address }] = await wallet.getAccounts();
if (logging) { if (logging) {
const role = i === 0 ? "token holder " : `distributor ${i}`; const role = i === 0 ? "token holder " : `distributor ${i}`;
@ -36,18 +35,13 @@ export async function createWallets(
export async function createClients( export async function createClients(
apiUrl: string, apiUrl: string,
wallets: ReadonlyArray<readonly [string, OfflineSigner]>, wallets: ReadonlyArray<readonly [string, OfflineSigner]>,
): Promise<ReadonlyArray<readonly [string, SigningCosmosClient | SigningStargateClient]>> { ): Promise<ReadonlyArray<readonly [string, SigningStargateClient]>> {
// we need one client per sender // we need one client per sender
return Promise.all( const pendingClients = wallets.map(
wallets.map( async ([senderAddress, wallet]): Promise<readonly [string, SigningStargateClient]> => [
async ([senderAddress, wallet]): Promise< senderAddress,
readonly [string, SigningCosmosClient | SigningStargateClient] await SigningStargateClient.connectWithSigner(apiUrl, wallet),
> => [ ],
senderAddress,
isOfflineDirectSigner(wallet)
? await SigningStargateClient.connectWithSigner(apiUrl, wallet)
: new SigningCosmosClient(apiUrl, senderAddress, wallet, constants.gasPrice, constants.gasLimits),
],
),
); );
return Promise.all(pendingClients);
} }

View File

@ -1,5 +1,5 @@
import { Coin } from "@cosmjs/launchpad";
import { Decimal, Uint53 } from "@cosmjs/math"; import { Decimal, Uint53 } from "@cosmjs/math";
import { Coin } from "@cosmjs/stargate";
import { MinimalAccount } from "./types"; import { MinimalAccount } from "./types";

View File

@ -1,4 +1,4 @@
import { Account, Coin } from "@cosmjs/launchpad"; import { Coin } from "@cosmjs/stargate";
export interface SendJob { export interface SendJob {
readonly sender: string; readonly sender: string;
@ -6,4 +6,8 @@ export interface SendJob {
readonly amount: Coin; readonly amount: Coin;
} }
export type MinimalAccount = Pick<Account, "address" | "balance">; export interface MinimalAccount {
/** Bech32 account address */
readonly address: string;
readonly balance: readonly Coin[];
}

View File

@ -1 +0,0 @@
../../.eslintignore

View File

@ -1,94 +0,0 @@
module.exports = {
env: {
es6: true,
jasmine: true,
node: true,
worker: true,
},
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 2018,
project: "./tsconfig.eslint.json",
tsconfigRootDir: __dirname,
},
plugins: ["@typescript-eslint", "prettier", "simple-import-sort", "import"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
"plugin:prettier/recommended",
"plugin:import/typescript",
],
rules: {
curly: ["warn", "multi-line", "consistent"],
"no-bitwise": "warn",
"no-console": ["warn", { allow: ["error", "info", "table", "warn"] }],
"no-param-reassign": "warn",
"no-shadow": "off", // disabled in favour of @typescript-eslint/no-shadow, see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-shadow.md
"no-unused-vars": "off", // disabled in favour of @typescript-eslint/no-unused-vars, see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-unused-vars.md
"prefer-const": "warn",
radix: ["warn", "always"],
"spaced-comment": ["warn", "always", { line: { markers: ["/ <reference"] } }],
"import/no-cycle": "warn",
"simple-import-sort/imports": "warn",
"simple-import-sort/exports": "warn",
"@typescript-eslint/array-type": ["warn", { default: "array-simple" }],
"@typescript-eslint/await-thenable": "warn",
"@typescript-eslint/ban-types": "warn",
"@typescript-eslint/explicit-function-return-type": ["warn", { allowExpressions: true }],
"@typescript-eslint/explicit-member-accessibility": "warn",
"@typescript-eslint/naming-convention": [
"warn",
{
selector: "default",
format: ["strictCamelCase"],
},
{
selector: "typeLike",
format: ["StrictPascalCase"],
},
{
selector: "enumMember",
format: ["StrictPascalCase"],
},
{
selector: "variable",
format: ["strictCamelCase"],
leadingUnderscore: "allow",
},
{
selector: "parameter",
format: ["strictCamelCase"],
leadingUnderscore: "allow",
},
],
"@typescript-eslint/no-dynamic-delete": "warn",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-floating-promises": "warn",
"@typescript-eslint/no-parameter-properties": "warn",
"@typescript-eslint/no-shadow": "warn",
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
"@typescript-eslint/no-unnecessary-type-assertion": "warn",
"@typescript-eslint/no-use-before-define": "warn",
"@typescript-eslint/prefer-readonly": "warn",
},
overrides: [
{
files: "**/*.js",
rules: {
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-member-accessibility": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
},
},
{
files: "**/*.spec.ts",
rules: {
"@typescript-eslint/no-non-null-assertion": "off",
},
},
],
};

View File

@ -1,3 +0,0 @@
build/
dist/
docs/

View File

@ -1 +0,0 @@
../../.nycrc.yml

View File

@ -1,521 +0,0 @@
# @cosmjs/launchpad
[![npm version](https://img.shields.io/npm/v/@cosmjs/launchpad.svg)](https://www.npmjs.com/package/@cosmjs/launchpad)
A client library for the Cosmos SDK 0.37 (cosmoshub-3), 0.38 and 0.39
(Launchpad). See the article
[Launchpad — A pre-stargate stable version of the Cosmos SDK](https://blog.cosmos.network/launchpad-a-pre-stargate-stable-version-of-the-cosmos-sdk-e0c58d8c4e24)
to learn more about launchpad.
## Basic usage
The basic usage of the package `@cosmjs/launchpad` contains the following:
1. [Create a wallet](#create-a-wallet)
2. [Sign and broadcast transactions](#sign-and-broadcast-transactions)
### Create a wallet
For the sake of simplicity we use an in-memory wallet. This is not the safest
way to store production keys. The following demo code is intended for developers
using testnet credentials only. Integrating it into end user facing products
requires serious security review.
If you do not yet have a mnemonic, generate a new wallet with a random mnemonic:
```ts
import { Secp256k1HdWallet } from "@cosmjs/launchpad";
// …
const wallet = await Secp256k1HdWallet.generate();
console.log("Mnemonic:", wallet.mnemonic);
const [{ address }] = await wallet.getAccounts();
console.log("Address:", address);
```
Or import an existing one:
```ts
import { Secp256k1HdWallet } from "@cosmjs/launchpad";
// …
const wallet = await Secp256k1HdWallet.fromMnemonic(
// your mnemonic here 👇
"enlist hip relief stomach skate base shallow young switch frequent cry park",
);
const [{ address }] = await wallet.getAccounts();
console.log("Address:", address);
```
### Sign and broadcast transactions
A wallet holds private keys and can use them for signing transactions. To do so
we stick the wallet into a client:
```ts
import {
Secp256k1HdWallet,
SigningCosmosClient,
coins,
} from "@cosmjs/launchpad";
// …
const wallet = await Secp256k1HdWallet.generate();
const [{ address }] = await wallet.getAccounts();
console.log("Address:", address);
// Ensure the address has some tokens to spend
const lcdApi = "https://…";
const client = new SigningCosmosClient(lcdApi, address, wallet);
// check our balance
const account = await client.getAccount();
console.log("Account:", account);
// Send tokens
const recipient = "cosmos1b2340gb2…";
await client.sendTokens(recipient, coins(123, "uatom"));
```
or use custom message types:
```ts
import {
Secp256k1HdWallet,
SigningCosmosClient,
coins,
coin,
MsgDelegate,
} from "@cosmjs/launchpad";
// …
const wallet = await Secp256k1HdWallet.generate();
const [{ address }] = await wallet.getAccounts();
console.log("Address:", address);
// Ensure the address has some tokens to spend
const lcdApi = "https://…";
const client = new SigningCosmosClient(lcdApi, address, wallet);
// …
const msg: MsgDelegate = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: address,
validator_address: "cosmosvaloper1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r3arurr",
amount: coin(300000, "ustake"),
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "180000", // 180k
};
await client.signAndBroadcast([msg], fee);
```
## Advanced usage
Here you will learn a few things that are slightly more advanced:
1. [Sign only](#sign-only)
2. [Aggregate signatures](#aggregate-signatures) from multiple signers
### Sign only
You can sign a transaction without broadcasting it:
```ts
import {
Secp256k1HdWallet,
SigningCosmosClient,
coins,
coin,
MsgDelegate,
} from "@cosmjs/launchpad";
// …
const wallet = await Secp256k1HdWallet.generate();
const [{ address }] = await wallet.getAccounts();
console.log("Address:", address);
const lcdApi = "https://…"; // Signing is offline, but from this endpoint we get the account number and sequence
const client = new SigningCosmosClient(lcdApi, address, wallet);
// …
const msg: MsgDelegate = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: address,
validator_address: "cosmosvaloper1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r3arurr",
amount: coin(300000, "ustake"),
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "180000", // 180k
};
let signed = await client.sign([msg], fee);
console.log("Signed transaction:", signed);
// We can broadcast it manually later on
const result = await client.broadcastTx(signed);
console.log("Broadcasting result:", result);
```
### Aggregate signatures
In this example we use `wallet0`/`client0` and `wallet1`/`client1` which can
live on separate systems:
```ts
import {
Secp256k1HdWallet,
SigningCosmosClient,
coins,
coin,
MsgDelegate,
} from "@cosmjs/launchpad";
const wallet0 = await Secp256k1HdWallet.fromMnemonic(mnemonic0);
const [{ address: address0 }] = await wallet.getAccounts();
const client0 = new SigningCosmosClient("https://…", address0, wallet0);
const wallet1 = await Secp256k1HdWallet.fromMnemonic(mnemonic1);
const [{ address: address1 }] = await wallet.getAccounts();
const client1 = new SigningCosmosClient("https://…", address1, wallet1);
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address0,
to_address: "cosmos1b2340gb2…",
amount: coins(1234567, "ucosm"),
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
to_address: "cosmos1b2340gb2…",
amount: coins(1234567, "ucosm"),
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "160000", // 2*80k
};
const memo = "This must be authorized by the two of us";
const signed = await client0.sign([msg1, msg2], fee, memo);
const cosigned = await client1.appendSignature(signed);
const result = await client1.broadcastTx(cosigned);
console.log("Broadcasting result:", result);
```
## Cosmos SDK module support
This client library supports connecting to standard Cosmos SDK modules as well
as custom modules. The modularity has two sides that are handled separately:
- Queries are for reading data from the chain;
- Messages are for writing data to chain as part of the transaction signing.
### Query support
@cosmjs/launchpad now has a flexible
[LcdClient](https://cosmwasm.github.io/cosmjs/latest/launchpad/classes/lcdclient.html),
which can be extended with all the standard Cosmos SDK modules in a type-safe
way. With
```ts
import { LcdClient } from "@cosmjs/launchpad";
const client = new LcdClient(apiUrl);
const response = await client.nodeInfo();
```
you only get access to blocks, transaction lists and node info. In order to sign
transactions, you need to setup the auth extension with:
```ts
import { LcdClient, setupAuthExtension } from "@cosmjs/launchpad";
const client = LcdClient.withExtensions({ apiUrl }, setupAuthExtension);
const { account_number, sequence } = (await client.auth.account(myAddress))
.result.value;
```
A full client can use all of the following extensions:
```ts
import {
LcdClient,
setupAuthExtension,
setupBankExtension,
setupDistributionExtension,
setupGovExtension,
setupMintExtension,
setupSlashingExtension,
setupStakingExtension,
setupSupplyExtension,
} from "@cosmjs/launchpad";
const client = LcdClient.withExtensions(
{ apiUrl },
setupAuthExtension,
setupBankExtension,
setupDistributionExtension,
setupGovExtension,
setupMintExtension,
setupSlashingExtension,
setupStakingExtension,
setupSupplyExtension,
);
// Example queries
const balances = await client.bank.balances(myAddress);
const distParameters = await client.distribution.parameters();
const proposals = await client.gov.proposals();
const inflation = await client.mint.inflation();
const signingInfos = await client.slashing.signingInfos();
const redelegations = await client.staking.redelegations();
const supply = await client.supply.totalAll();
```
### Messages suppport
Every Amino-JSON compatible message can be used for the
[Msg](https://cosmwasm.github.io/cosmjs/latest/launchpad/interfaces/msg.html)
interface like this:
```ts
const voteMsg: Msg = {
type: "cosmos-sdk/MsgVote",
value: {
proposal_id: proposalId,
voter: faucet.address,
option: "Yes",
},
};
```
This is most flexible since you are not restricted to known messages, but gives
you very little type safety. For improved type safety, we added TypeScript
definitions for all common Cosmos SDK messages:
- MsgSend
- MsgMultiSend
- MsgVerifyInvariant
- MsgSetWithdrawAddress
- MsgWithdrawDelegatorReward
- MsgWithdrawValidatorCommission
- MsgFundCommunityPool
- MsgSubmitEvidence
- MsgSubmitProposal
- MsgVote
- MsgDeposit
- MsgUnjail
- MsgCreateValidator
- MsgEditValidator
- MsgDelegate
- MsgBeginRedelegate
- MsgUndelegate
Those can be signed and broadcast the manual way or by using a higher level
[SigningCosmosClient](https://cosmwasm.github.io/cosmjs/latest/launchpad/classes/signingcosmosclient.html):
```ts
import {
coins,
BroadcastTxResult,
MsgSubmitProposal,
OfflineSigner,
SigningCosmosClient,
StdFee,
} from "@cosmjs/launchpad";
async function publishProposal(
apiUrl: string,
signer: OfflineSigner,
): Promise<BroadcastTxResult> {
const [{ address: authorAddress }] = await signer.getAccounts();
const memo = "My first proposal on chain";
const msg: MsgSubmitProposal = {
type: "cosmos-sdk/MsgSubmitProposal",
value: {
content: {
type: "cosmos-sdk/TextProposal",
value: {
description:
"This proposal proposes to test whether this proposal passes",
title: "Test Proposal",
},
},
proposer: authorAddress,
initial_deposit: coins(25000000, "ustake"),
},
};
const fee: StdFee = {
amount: coins(5000000, "ucosm"),
gas: "89000000",
};
const client = new SigningCosmosClient(apiUrl, authorAddress, signer);
return client.signAndBroadcast([msg], fee, memo);
}
```
### Custom modules
Both query and message support is implemented in a decentralized fashion, which
allows applications to add their extensions without changing CosmJS. As an
example we show how to build a client for a blockchain with a wasm module:
```ts
import {
MsgExecuteContract,
setupWasmExtension,
} from "@cosmjs/cosmwasm-launchpad";
import {
assertIsBroadcastTxResult,
LcdClient,
makeSignDoc,
setupAuthExtension,
StdFee,
StdTx,
} from "@cosmjs/launchpad";
const client = LcdClient.withExtensions(
{ apiUrl },
setupAuthExtension,
// 👇 this extension can come from a chain-specific package or the application itself
setupWasmExtension,
);
// 👇 this message type can come from a chain-specific package or the application itself
const msg: MsgExecuteContract = {
type: "wasm/MsgExecuteContract",
value: {
sender: myAddress,
contract: contractAddress,
msg: wasmMsg,
sent_funds: [],
},
};
const fee: StdFee = {
amount: coins(5000000, "ucosm"),
gas: "89000000",
};
const memo = "Time for action";
const { account_number, sequence } = (await client.auth.account(myAddress))
.result.value;
const signDoc = makeSignDoc([msg], fee, apiUrl, memo, account_number, sequence);
const { signed, signature } = await signer.sign(myAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const result = await client.broadcastTx(signedTx);
assertIsBroadcastTxResult(result);
```
## Secure key storage
[Secp256k1HdWallet](https://cosmwasm.github.io/cosmjs/latest/launchpad/classes/secp256k1hdwallet.html)
supports securely encrypted serialization/deserialization using Argon2 for key
derivation and XChaCha20Poly1305 for authenticated encryption. It can be used as
easily as:
```ts
// generate an 18 word mnemonic
const wallet = await Secp256k1HdWallet.generate(18);
const serialized = await original.serialize("my password");
// serialized is encrypted and can now be stored in an application-specific way
const restored = await Secp256k1HdWallet.deserialize(serialized, "my password");
```
If you want to use really strong KDF parameters in a user interface, you should
offload the KDF execution to a separate thread in order to avoid freezing the
UI. This can be done in the advanced mode:
**Session 1 (main thread)**
```ts
const wallet = await Secp256k1HdWallet.generate(18);
```
**Session 1 (WebWorker)**
This operation can now run a couple of seconds without freezing the UI.
```ts
import { executeKdf } from "@cosmjs/launchpad";
// pass password to the worker
const strongKdfParams: KdfConfiguration = {
algorithm: "argon2id",
params: {
outputLength: 32,
opsLimit: 5000,
memLimitKib: 15 * 1024,
},
};
const encryptionKey = await executeKdf(password, strongKdfParams);
// pass encryptionKey to the main thread
```
**Session 1 (main thread)**
```ts
const serialized = await wallet.serializeWithEncryptionKey(
encryptionKey,
anyKdfParams,
);
```
**Session 2 (WebWorker)**
```ts
import { executeKdf, extractKdfConfiguration } from "@cosmjs/launchpad";
// pass serialized and password to the worker
const kdfConfiguration = extractKdfConfiguration(serialized);
const encryptionKey = await executeKdf(password, kdfConfiguration);
// pass encryptionKey to the main thread
```
**Session 2 (main thead)**
```ts
const restored = await Secp256k1HdWallet.deserializeWithEncryptionKey(
serialized,
encryptionKey,
);
// use restored for signing
```
## License
This package is part of the cosmjs repository, licensed under the Apache License
2.0 (see [NOTICE](https://github.com/cosmos/cosmjs/blob/main/NOTICE) and
[LICENSE](https://github.com/cosmos/cosmjs/blob/main/LICENSE)).

View File

@ -1,38 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
if (process.env.SES_ENABLED) {
require("ses/lockdown");
// eslint-disable-next-line no-undef
lockdown();
}
require("source-map-support").install();
const defaultSpecReporterConfig = require("../../jasmine-spec-reporter.config.json");
// setup Jasmine
const Jasmine = require("jasmine");
const jasmine = new Jasmine();
jasmine.loadConfig({
spec_dir: "build",
spec_files: ["**/*.spec.js"],
helpers: [],
random: false,
seed: null,
stopSpecOnExpectationFailure: false,
});
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 15 * 1000;
// setup reporter
const { SpecReporter } = require("jasmine-spec-reporter");
const reporter = new SpecReporter({
...defaultSpecReporterConfig,
spec: {
...defaultSpecReporterConfig.spec,
displaySuccessful: !process.argv.includes("--quiet"),
},
});
// initialize and execute
jasmine.env.clearReporters();
jasmine.addReporter(reporter);
void jasmine.execute();

View File

@ -1,54 +0,0 @@
const chrome = require("karma-chrome-launcher");
const firefox = require("karma-firefox-launcher");
const jasmine = require("karma-jasmine");
const kjhtml = require("karma-jasmine-html-reporter");
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: ".",
// registers plugins but does not activate them
plugins: [jasmine, kjhtml, chrome, firefox],
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ["jasmine"],
// list of files / patterns to load in the browser
files: ["dist/web/tests.js"],
client: {
jasmine: {
random: false,
timeoutInterval: 15000,
},
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ["progress", "kjhtml"],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ["Firefox"],
browserNoActivityTimeout: 90000,
// Keep brower open for debugging. This is overridden by yarn scripts
singleRun: false,
});
};

View File

@ -1,88 +0,0 @@
{
"name": "@cosmjs/launchpad",
"version": "0.27.1",
"description": "A client library for the Cosmos SDK 0.37 (cosmoshub-3), 0.38 and 0.39 (Launchpad)",
"contributors": [
"Ethan Frey <ethanfrey@users.noreply.github.com>",
"Simon Warta <webmaster128@users.noreply.github.com>"
],
"license": "Apache-2.0",
"main": "build/index.js",
"types": "build/index.d.ts",
"files": [
"build/",
"*.md",
"!*.spec.*",
"!**/testdata/"
],
"repository": {
"type": "git",
"url": "https://github.com/cosmos/cosmjs/tree/main/packages/launchpad"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"scripts": {
"docs": "typedoc --options typedoc.js",
"format": "prettier --write --loglevel warn \"./src/**/*.ts\"",
"format-text": "prettier --write \"./*.md\"",
"lint": "eslint --max-warnings 0 \"./**/*.ts\" \"./*.js\"",
"lint-fix": "eslint --fix --max-warnings 0 \"./**/*.ts\" \"./*.js\"",
"build": "rm -rf ./build && tsc",
"build-or-skip": "[ -n \"$SKIP_BUILD\" ] || yarn build",
"test-node": "yarn node jasmine-testrunner.js",
"test-firefox": "yarn pack-web && karma start --single-run --browsers Firefox",
"test-chrome": "yarn pack-web && karma start --single-run --browsers ChromeHeadless",
"test": "yarn build-or-skip && yarn test-node",
"coverage": "nyc --reporter=text --reporter=lcov yarn test --quiet",
"pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js"
},
"dependencies": {
"@cosmjs/amino": "workspace:packages/amino",
"@cosmjs/crypto": "workspace:packages/crypto",
"@cosmjs/encoding": "workspace:packages/encoding",
"@cosmjs/math": "workspace:packages/math",
"@cosmjs/utils": "workspace:packages/utils",
"axios": "^0.21.2",
"fast-deep-equal": "^3.1.3"
},
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@types/eslint-plugin-prettier": "^3",
"@types/jasmine": "^3.8",
"@types/karma-firefox-launcher": "^2",
"@types/karma-jasmine": "^4",
"@types/karma-jasmine-html-reporter": "^1",
"@types/node": "^15.0.1",
"@typescript-eslint/eslint-plugin": "^4.28",
"@typescript-eslint/parser": "^4.28",
"eslint": "^7.5",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-node": "^0.3.4",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"esm": "^3.2.25",
"glob": "^7.1.6",
"jasmine": "^3.8",
"jasmine-core": "^3.7.1",
"jasmine-spec-reporter": "^6",
"karma": "^6.1.1",
"karma-chrome-launcher": "^3.1.0",
"karma-firefox-launcher": "^2.1.0",
"karma-jasmine": "^4.0.1",
"karma-jasmine-html-reporter": "^1.5.4",
"nyc": "^15.1.0",
"prettier": "^2.4.1",
"readonly-date": "^1.0.0",
"ses": "^0.11.0",
"source-map-support": "^0.5.19",
"stream-browserify": "^3.0.0",
"ts-node": "^8",
"typedoc": "^0.22",
"typescript": "~4.4",
"webpack": "^5.32.0",
"webpack-cli": "^4.6.0"
}
}

View File

@ -1,304 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { coins, makeSignDoc, makeStdTx, Secp256k1HdWallet } from "@cosmjs/amino";
import { assert, sleep } from "@cosmjs/utils";
import { CosmosClient, isBroadcastTxFailure } from "./cosmosclient";
import { LcdClient } from "./lcdapi";
import { isMsgSend, MsgSend } from "./msgs";
import { SigningCosmosClient } from "./signingcosmosclient";
import {
faucet,
fromOneElementArray,
launchpad,
launchpadEnabled,
makeRandomAddress,
pendingWithoutLaunchpad,
} from "./testutils.spec";
import { WrappedStdTx } from "./tx";
interface TestTxSend {
readonly sender: string;
readonly recipient: string;
readonly hash: string;
readonly height: number;
readonly tx: WrappedStdTx;
}
describe("CosmosClient.getTx and .searchTx", () => {
let sendUnsuccessful: TestTxSend | undefined;
let sendSuccessful: TestTxSend | undefined;
beforeAll(async () => {
if (launchpadEnabled()) {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
{
const memo = "Sending more than I can afford";
const recipient = makeRandomAddress();
const amount = coins(123456700000000, "ucosm");
const sendMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address0,
to_address: recipient,
amount: amount,
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "80000", // 80k
};
const { accountNumber, sequence } = await client.getSequence();
const chainId = await client.getChainId();
const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence);
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
const tx: WrappedStdTx = {
type: "cosmos-sdk/StdTx",
value: makeStdTx(signed, signature),
};
const transactionId = await client.getIdentifier(tx);
const result = await client.broadcastTx(tx.value);
if (isBroadcastTxFailure(result)) {
sendUnsuccessful = {
sender: faucet.address0,
recipient: recipient,
hash: transactionId,
height: result.height,
tx: tx,
};
}
}
{
const recipient = makeRandomAddress();
const amount = coins(1234567, "ucosm");
const result = await client.sendTokens(recipient, amount);
await sleep(75); // wait until tx is indexed
const txDetails = await new LcdClient(launchpad.endpoint).txById(result.transactionHash);
sendSuccessful = {
sender: faucet.address0,
recipient: recipient,
hash: result.transactionHash,
height: Number.parseInt(txDetails.height, 10),
tx: txDetails.tx,
};
}
}
});
describe("getTx", () => {
it("can get successful tx by ID", async () => {
pendingWithoutLaunchpad();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = new CosmosClient(launchpad.endpoint);
const result = await client.getTx(sendSuccessful.hash);
expect(result).toEqual(
jasmine.objectContaining({
height: sendSuccessful.height,
hash: sendSuccessful.hash,
code: 0,
tx: sendSuccessful.tx,
}),
);
});
it("can get unsuccessful tx by ID", async () => {
pendingWithoutLaunchpad();
assert(sendUnsuccessful, "value must be set in beforeAll()");
const client = new CosmosClient(launchpad.endpoint);
const result = await client.getTx(sendUnsuccessful.hash);
expect(result).toEqual(
jasmine.objectContaining({
height: sendUnsuccessful.height,
hash: sendUnsuccessful.hash,
code: 5,
tx: sendUnsuccessful.tx,
}),
);
});
it("can get by ID (non existent)", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
const nonExistentId = "0000000000000000000000000000000000000000000000000000000000000000";
const result = await client.getTx(nonExistentId);
expect(result).toBeNull();
});
});
describe("with SearchByHeightQuery", () => {
it("can search successful tx by height", async () => {
pendingWithoutLaunchpad();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = new CosmosClient(launchpad.endpoint);
const result = await client.searchTx({ height: sendSuccessful.height });
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result).toContain(
jasmine.objectContaining({
height: sendSuccessful.height,
hash: sendSuccessful.hash,
code: 0,
tx: sendSuccessful.tx,
}),
);
});
it("can search unsuccessful tx by height", async () => {
pendingWithoutLaunchpad();
assert(sendUnsuccessful, "value must be set in beforeAll()");
const client = new CosmosClient(launchpad.endpoint);
const result = await client.searchTx({ height: sendUnsuccessful.height });
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result).toContain(
jasmine.objectContaining({
height: sendUnsuccessful.height,
hash: sendUnsuccessful.hash,
code: 5,
tx: sendUnsuccessful.tx,
}),
);
});
});
describe("with SearchBySentFromOrToQuery", () => {
it("can search by sender", async () => {
pendingWithoutLaunchpad();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = new CosmosClient(launchpad.endpoint);
const results = await client.searchTx({ sentFromOrTo: sendSuccessful.sender });
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
for (const result of results) {
const containsMsgWithSender = !!result.tx.value.msg.find(
(msg) => isMsgSend(msg) && msg.value.from_address == sendSuccessful!.sender,
);
const containsMsgWithRecipient = !!result.tx.value.msg.find(
(msg) => isMsgSend(msg) && msg.value.to_address === sendSuccessful!.sender,
);
expect(containsMsgWithSender || containsMsgWithRecipient).toEqual(true);
}
// Check details of most recent result
expect(results[results.length - 1]).toEqual(
jasmine.objectContaining({
height: sendSuccessful.height,
hash: sendSuccessful.hash,
tx: sendSuccessful.tx,
}),
);
});
it("can search by recipient", async () => {
pendingWithoutLaunchpad();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = new CosmosClient(launchpad.endpoint);
const results = await client.searchTx({ sentFromOrTo: sendSuccessful.recipient });
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
for (const result of results) {
const msg = fromOneElementArray(result.tx.value.msg);
assert(isMsgSend(msg), `${result.hash} (height ${result.height}) is not a bank send transaction`);
expect(
msg.value.to_address === sendSuccessful.recipient ||
msg.value.from_address == sendSuccessful.recipient,
).toEqual(true);
}
// Check details of most recent result
expect(results[results.length - 1]).toEqual(
jasmine.objectContaining({
height: sendSuccessful.height,
hash: sendSuccessful.hash,
tx: sendSuccessful.tx,
}),
);
});
it("can search by recipient and filter by minHeight", async () => {
pendingWithoutLaunchpad();
assert(sendSuccessful);
const client = new CosmosClient(launchpad.endpoint);
const query = { sentFromOrTo: sendSuccessful.recipient };
{
const result = await client.searchTx(query, { minHeight: 0 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height - 1 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height + 1 });
expect(result.length).toEqual(0);
}
});
it("can search by recipient and filter by maxHeight", async () => {
pendingWithoutLaunchpad();
assert(sendSuccessful);
const client = new CosmosClient(launchpad.endpoint);
const query = { sentFromOrTo: sendSuccessful.recipient };
{
const result = await client.searchTx(query, { maxHeight: 9999999999999 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height + 1 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height - 1 });
expect(result.length).toEqual(0);
}
});
});
describe("with SearchByTagsQuery", () => {
it("can search by transfer.recipient", async () => {
pendingWithoutLaunchpad();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = new CosmosClient(launchpad.endpoint);
const results = await client.searchTx({
tags: [{ key: "transfer.recipient", value: sendSuccessful.recipient }],
});
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
for (const result of results) {
const msg = fromOneElementArray(result.tx.value.msg);
assert(isMsgSend(msg), `${result.hash} (height ${result.height}) is not a bank send transaction`);
expect(msg.value.to_address).toEqual(sendSuccessful.recipient);
}
// Check details of most recent result
expect(results[results.length - 1]).toEqual(
jasmine.objectContaining({
height: sendSuccessful.height,
hash: sendSuccessful.hash,
tx: sendSuccessful.tx,
}),
);
});
});
});

View File

@ -1,243 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { makeSignDoc, makeStdTx, Secp256k1HdWallet, StdFee } from "@cosmjs/amino";
import { assert, sleep } from "@cosmjs/utils";
import { ReadonlyDate } from "readonly-date";
import { assertIsBroadcastTxSuccess, CosmosClient, PrivateCosmosClient } from "./cosmosclient";
import { findAttribute } from "./logs";
import { MsgSend } from "./msgs";
import cosmoshub from "./testdata/cosmoshub.json";
import {
faucet,
launchpad,
makeRandomAddress,
pendingWithoutLaunchpad,
tendermintIdMatcher,
unused,
} from "./testutils.spec";
import { isWrappedStdTx } from "./tx";
const blockTime = 1_000; // ms
const guest = {
address: "cosmos17d0jcz59jf68g52vq38tuuncmwwjk42u6mcxej",
};
describe("CosmosClient", () => {
describe("constructor", () => {
it("can be constructed", () => {
const client = new CosmosClient(launchpad.endpoint);
expect(client).toBeTruthy();
});
});
describe("getChainId", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
expect(await client.getChainId()).toEqual(launchpad.chainId);
});
it("caches chain ID", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
const openedClient = client as unknown as PrivateCosmosClient;
const getCodeSpy = spyOn(openedClient.lcdClient, "nodeInfo").and.callThrough();
expect(await client.getChainId()).toEqual(launchpad.chainId); // from network
expect(await client.getChainId()).toEqual(launchpad.chainId); // from cache
expect(getCodeSpy).toHaveBeenCalledTimes(1);
});
});
describe("getHeight", () => {
it("gets height via last block", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
const openedClient = client as unknown as PrivateCosmosClient;
const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough();
const height1 = await client.getHeight();
expect(height1).toBeGreaterThan(0);
await sleep(blockTime * 1.4); // tolerate chain being 40% slower than expected
const height2 = await client.getHeight();
expect(height2).toBeGreaterThanOrEqual(height1 + 1);
expect(height2).toBeLessThanOrEqual(height1 + 2);
expect(blockLatestSpy).toHaveBeenCalledTimes(2);
});
it("gets height via authAccount once an address is known", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
const openedClient = client as unknown as PrivateCosmosClient;
const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough();
const authAccountsSpy = spyOn(openedClient.lcdClient.auth, "account").and.callThrough();
const height1 = await client.getHeight();
expect(height1).toBeGreaterThan(0);
await client.getAccount(guest.address); // warm up the client
const height2 = await client.getHeight();
expect(height2).toBeGreaterThan(0);
await sleep(blockTime * 1.3); // tolerate chain being 30% slower than expected
const height3 = await client.getHeight();
expect(height3).toBeGreaterThanOrEqual(height2 + 1);
expect(height3).toBeLessThanOrEqual(height2 + 2);
expect(blockLatestSpy).toHaveBeenCalledTimes(1);
expect(authAccountsSpy).toHaveBeenCalledTimes(3);
});
});
describe("getSequence", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
expect(await client.getSequence(unused.address)).toEqual({
accountNumber: unused.accountNumber,
sequence: unused.sequence,
});
});
it("throws for missing accounts", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
const missing = makeRandomAddress();
await client.getSequence(missing).then(
() => fail("this must not succeed"),
(error) => expect(error).toMatch(/account does not exist on chain/i),
);
});
});
describe("getAccount", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
expect(await client.getAccount(unused.address)).toEqual({
address: unused.address,
accountNumber: unused.accountNumber,
sequence: unused.sequence,
pubkey: undefined,
balance: [
{ denom: "ucosm", amount: "1000000000" },
{ denom: "ustake", amount: "1000000000" },
],
});
});
it("returns undefined for missing accounts", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
const missing = makeRandomAddress();
expect(await client.getAccount(missing)).toBeUndefined();
});
});
describe("getBlock", () => {
it("works for latest block", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
const response = await client.getBlock();
expect(response).toEqual(
jasmine.objectContaining({
id: jasmine.stringMatching(tendermintIdMatcher),
header: jasmine.objectContaining({
chainId: await client.getChainId(),
}),
txs: [],
}),
);
expect(response.header.height).toBeGreaterThanOrEqual(1);
expect(new ReadonlyDate(response.header.time).getTime()).toBeLessThan(ReadonlyDate.now());
expect(new ReadonlyDate(response.header.time).getTime()).toBeGreaterThanOrEqual(
ReadonlyDate.now() - 5_000,
);
});
it("works for block by height", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
const height = (await client.getBlock()).header.height;
const response = await client.getBlock(height - 1);
expect(response).toEqual(
jasmine.objectContaining({
id: jasmine.stringMatching(tendermintIdMatcher),
header: jasmine.objectContaining({
height: height - 1,
chainId: await client.getChainId(),
}),
txs: [],
}),
);
expect(new ReadonlyDate(response.header.time).getTime()).toBeLessThan(ReadonlyDate.now());
expect(new ReadonlyDate(response.header.time).getTime()).toBeGreaterThanOrEqual(
ReadonlyDate.now() - 5_000,
);
});
});
describe("getIdentifier", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = new CosmosClient(launchpad.endpoint);
assert(isWrappedStdTx(cosmoshub.tx));
expect(await client.getIdentifier(cosmoshub.tx)).toEqual(cosmoshub.id);
});
});
describe("broadcastTx", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const client = new CosmosClient(launchpad.endpoint);
const memo = "Test send";
const sendMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address0,
to_address: makeRandomAddress(),
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const chainId = await client.getChainId();
const { accountNumber, sequence } = await client.getSequence(faucet.address0);
const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence);
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const txResult = await client.broadcastTx(signedTx);
assertIsBroadcastTxSuccess(txResult);
const { logs, transactionHash } = txResult;
const amountAttr = findAttribute(logs, "transfer", "amount");
expect(amountAttr.value).toEqual("1234567ucosm");
expect(transactionHash).toMatch(/^[0-9A-F]{64}$/);
});
});
});

View File

@ -1,347 +0,0 @@
import { Coin, Pubkey, StdTx } from "@cosmjs/amino";
import { sha256 } from "@cosmjs/crypto";
import { fromBase64, fromHex, toHex } from "@cosmjs/encoding";
import { Uint53 } from "@cosmjs/math";
import {
AuthExtension,
BroadcastMode,
LcdClient,
normalizePubkey,
setupAuthExtension,
uint64ToNumber,
} from "./lcdapi";
import { Log, parseLogs } from "./logs";
import { WrappedStdTx } from "./tx";
export interface GetSequenceResult {
readonly accountNumber: number;
readonly sequence: number;
}
export interface Account {
/** Bech32 account address */
readonly address: string;
readonly balance: readonly Coin[];
readonly pubkey: Pubkey | undefined;
readonly accountNumber: number;
readonly sequence: number;
}
export interface BroadcastTxFailure {
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
readonly transactionHash: string;
readonly height: number;
readonly code: number;
readonly rawLog: string;
}
export interface BroadcastTxSuccess {
readonly logs: readonly Log[];
readonly rawLog: string;
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
readonly transactionHash: string;
readonly data?: Uint8Array;
}
export type BroadcastTxResult = BroadcastTxSuccess | BroadcastTxFailure;
export function isBroadcastTxFailure(result: BroadcastTxResult): result is BroadcastTxFailure {
return !!(result as BroadcastTxFailure).code;
}
export function isBroadcastTxSuccess(result: BroadcastTxResult): result is BroadcastTxSuccess {
return !isBroadcastTxFailure(result);
}
/**
* Ensures the given result is a success. Throws a detailed error message otherwise.
*/
export function assertIsBroadcastTxSuccess(result: BroadcastTxResult): asserts result is BroadcastTxSuccess {
if (isBroadcastTxFailure(result)) {
throw new Error(
`Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`,
);
}
}
export interface SearchByHeightQuery {
readonly height: number;
}
export interface SearchBySentFromOrToQuery {
readonly sentFromOrTo: string;
}
/**
* This query type allows you to pass arbitrary key/value pairs to the backend. It is
* more powerful and slightly lower level than the other search options.
*/
export interface SearchByTagsQuery {
readonly tags: ReadonlyArray<{ readonly key: string; readonly value: string }>;
}
export type SearchTxQuery = SearchByHeightQuery | SearchBySentFromOrToQuery | SearchByTagsQuery;
export function isSearchByHeightQuery(query: SearchTxQuery): query is SearchByHeightQuery {
return (query as SearchByHeightQuery).height !== undefined;
}
export function isSearchBySentFromOrToQuery(query: SearchTxQuery): query is SearchBySentFromOrToQuery {
return (query as SearchBySentFromOrToQuery).sentFromOrTo !== undefined;
}
export function isSearchByTagsQuery(query: SearchTxQuery): query is SearchByTagsQuery {
return (query as SearchByTagsQuery).tags !== undefined;
}
export interface SearchTxFilter {
readonly minHeight?: number;
readonly maxHeight?: number;
}
/** A transaction that is indexed as part of the transaction history */
export interface IndexedTx {
readonly height: number;
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
readonly hash: string;
/** Transaction execution error code. 0 on success. */
readonly code: number;
readonly rawLog: string;
readonly logs: readonly Log[];
readonly tx: WrappedStdTx;
/** The gas limit as set by the user */
readonly gasWanted?: number;
/** The gas used by the execution */
readonly gasUsed?: number;
/** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */
readonly timestamp: string;
}
export interface BlockHeader {
readonly version: {
readonly block: string;
readonly app: string;
};
readonly height: number;
readonly chainId: string;
/** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */
readonly time: string;
}
export interface Block {
/** The ID is a hash of the block header (uppercase hex) */
readonly id: string;
readonly header: BlockHeader;
/** Array of raw transactions */
readonly txs: readonly Uint8Array[];
}
/** Use for testing only */
export interface PrivateCosmosClient {
readonly lcdClient: LcdClient & AuthExtension;
}
export class CosmosClient {
protected readonly lcdClient: LcdClient & AuthExtension;
/** Any address the chain considers valid (valid bech32 with proper prefix) */
protected anyValidAddress: string | undefined;
private chainId: string | undefined;
/**
* Creates a new client to interact with a CosmWasm blockchain.
*
* This instance does a lot of caching. In order to benefit from that you should try to use one instance
* for the lifetime of your application. When switching backends, a new instance must be created.
*
* @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API)
* @param broadcastMode Defines at which point of the transaction processing the broadcastTx method returns
*/
public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) {
this.lcdClient = LcdClient.withExtensions(
{ apiUrl: apiUrl, broadcastMode: broadcastMode },
setupAuthExtension,
);
}
public async getChainId(): Promise<string> {
if (!this.chainId) {
const response = await this.lcdClient.nodeInfo();
const chainId = response.node_info.network;
if (!chainId) throw new Error("Chain ID must not be empty");
this.chainId = chainId;
}
return this.chainId;
}
public async getHeight(): Promise<number> {
if (this.anyValidAddress) {
const { height } = await this.lcdClient.auth.account(this.anyValidAddress);
return parseInt(height, 10);
} else {
// Note: this gets inefficient when blocks contain a lot of transactions since it
// requires downloading and deserializing all transactions in the block.
const latest = await this.lcdClient.blocksLatest();
return parseInt(latest.block.header.height, 10);
}
}
/**
* Returns a 32 byte upper-case hex transaction hash (typically used as the transaction ID)
*/
public async getIdentifier(tx: WrappedStdTx): Promise<string> {
// We consult the REST API because we don't have a local amino encoder
const response = await this.lcdClient.encodeTx(tx);
const hash = sha256(fromBase64(response.tx));
return toHex(hash).toUpperCase();
}
/**
* Returns account number and sequence.
*
* Throws if the account does not exist on chain.
*
* @param address returns data for this address. When unset, the client's sender adddress is used.
*/
public async getSequence(address: string): Promise<GetSequenceResult> {
const account = await this.getAccount(address);
if (!account) {
throw new Error(
"Account does not exist on chain. Send some tokens there before trying to query sequence.",
);
}
return {
accountNumber: account.accountNumber,
sequence: account.sequence,
};
}
public async getAccount(address: string): Promise<Account | undefined> {
const account = await this.lcdClient.auth.account(address);
const value = account.result.value;
if (value.address === "") {
return undefined;
} else {
this.anyValidAddress = value.address;
return {
address: value.address,
balance: value.coins,
pubkey: normalizePubkey(value.public_key) || undefined,
accountNumber: uint64ToNumber(value.account_number),
sequence: uint64ToNumber(value.sequence),
};
}
}
/**
* Gets block header and meta
*
* @param height The height of the block. If undefined, the latest height is used.
*/
public async getBlock(height?: number): Promise<Block> {
const response =
height !== undefined ? await this.lcdClient.blocks(height) : await this.lcdClient.blocksLatest();
return {
id: response.block_id.hash,
header: {
version: response.block.header.version,
time: response.block.header.time,
height: parseInt(response.block.header.height, 10),
chainId: response.block.header.chain_id,
},
txs: (response.block.data.txs || []).map(fromBase64),
};
}
public async getTx(id: string): Promise<IndexedTx | null> {
const results = await this.txsQuery(`tx.hash=${id}`);
return results[0] ?? null;
}
public async searchTx(query: SearchTxQuery, filter: SearchTxFilter = {}): Promise<readonly IndexedTx[]> {
const minHeight = filter.minHeight || 0;
const maxHeight = filter.maxHeight || Number.MAX_SAFE_INTEGER;
if (maxHeight < minHeight) return []; // optional optimization
function withFilters(originalQuery: string): string {
return `${originalQuery}&tx.minheight=${minHeight}&tx.maxheight=${maxHeight}`;
}
let txs: readonly IndexedTx[];
if (isSearchByHeightQuery(query)) {
// optional optimization to avoid network request
if (query.height < minHeight || query.height > maxHeight) {
txs = [];
} else {
txs = await this.txsQuery(`tx.height=${query.height}`);
}
} else if (isSearchBySentFromOrToQuery(query)) {
// We cannot get both in one request (see https://github.com/cosmos/gaia/issues/75)
const sentQuery = withFilters(`message.module=bank&message.sender=${query.sentFromOrTo}`);
const receivedQuery = withFilters(`message.module=bank&transfer.recipient=${query.sentFromOrTo}`);
const sent = await this.txsQuery(sentQuery);
const received = await this.txsQuery(receivedQuery);
const sentHashes = sent.map((t) => t.hash);
txs = [...sent, ...received.filter((t) => !sentHashes.includes(t.hash))];
} else if (isSearchByTagsQuery(query)) {
const rawQuery = withFilters(query.tags.map((t) => `${t.key}=${t.value}`).join("&"));
txs = await this.txsQuery(rawQuery);
} else {
throw new Error("Unknown query type");
}
// backend sometimes messes up with min/max height filtering
const filtered = txs.filter((tx) => tx.height >= minHeight && tx.height <= maxHeight);
return filtered;
}
public async broadcastTx(tx: StdTx): Promise<BroadcastTxResult> {
const result = await this.lcdClient.broadcastTx(tx);
if (!result.txhash.match(/^([0-9A-F][0-9A-F])+$/)) {
throw new Error("Received ill-formatted txhash. Must be non-empty upper-case hex");
}
return result.code !== undefined
? {
height: Uint53.fromString(result.height).toNumber(),
transactionHash: result.txhash,
code: result.code,
rawLog: result.raw_log || "",
}
: {
logs: result.logs ? parseLogs(result.logs) : [],
rawLog: result.raw_log || "",
transactionHash: result.txhash,
data: result.data ? fromHex(result.data) : undefined,
};
}
private async txsQuery(query: string): Promise<readonly IndexedTx[]> {
// TODO: we need proper pagination support
const limit = 100;
const result = await this.lcdClient.txsQuery(`${query}&limit=${limit}`);
const pages = parseInt(result.page_total, 10);
if (pages > 1) {
throw new Error(
`Found more results on the backend than we can process currently. Results: ${result.total_count}, supported: ${limit}`,
);
}
return result.txs.map(
(restItem): IndexedTx => ({
height: parseInt(restItem.height, 10),
hash: restItem.txhash,
code: restItem.code || 0,
rawLog: restItem.raw_log,
logs: parseLogs(restItem.logs || []),
tx: restItem.tx,
timestamp: restItem.timestamp,
}),
);
}
}

View File

@ -1,55 +0,0 @@
import { Decimal } from "@cosmjs/math";
import { GasPrice } from "./fee";
describe("GasPrice", () => {
it("can be constructed", () => {
const inputs = ["3.14", "3", "0.14"];
inputs.forEach((input) => {
const gasPrice = new GasPrice(Decimal.fromUserInput(input, 18), "utest");
expect(gasPrice.amount.toString()).toEqual(input);
expect(gasPrice.denom).toEqual("utest");
});
});
describe("fromString", () => {
it("works", () => {
const inputs: Record<string, { amount: string; denom: string }> = {
// Test amounts
"3.14utest": { amount: "3.14", denom: "utest" },
"3utest": { amount: "3", denom: "utest" },
"0.14utest": { amount: "0.14", denom: "utest" },
// Test denoms
"0.14sht": { amount: "0.14", denom: "sht" },
"0.14testtesttesttest": { amount: "0.14", denom: "testtesttesttest" },
"0.14ucoin2": { amount: "0.14", denom: "ucoin2" },
};
for (const [input, expected] of Object.entries(inputs)) {
const gasPrice = GasPrice.fromString(input);
expect(gasPrice.amount.toString()).withContext(`Input: ${input}`).toEqual(expected.amount);
expect(gasPrice.denom).withContext(`Input: ${input}`).toEqual(expected.denom);
}
});
it("errors for invalid gas price", () => {
// Checks basic format <amount><denom>
expect(() => GasPrice.fromString("")).toThrowError(/Invalid gas price string/i);
expect(() => GasPrice.fromString("utkn")).toThrowError(/Invalid gas price string/i);
expect(() => GasPrice.fromString("@utkn")).toThrowError(/Invalid gas price string/i);
expect(() => GasPrice.fromString("234")).toThrowError(/Invalid gas price string/i);
expect(() => GasPrice.fromString("-234tkn")).toThrowError(/Invalid gas price string/i);
// Checks details of <denom>
expect(() => GasPrice.fromString("234t")).toThrowError(/denom must be between 3 and 16 characters/i);
expect(() => GasPrice.fromString("234tt")).toThrowError(/denom must be between 3 and 16 characters/i);
expect(() => GasPrice.fromString("234ttttttttttttttttt")).toThrowError(
/denom must be between 3 and 16 characters/i,
);
expect(() => GasPrice.fromString("234ATOM")).toThrowError(
/denom must only contain lower case letters a-z and digits 0-9/i,
);
// Checks details of <amount>
expect(() => GasPrice.fromString("3.utkn")).toThrowError(/Fractional part missing/i);
expect(() => GasPrice.fromString("..utkn")).toThrowError(/More than one separator found/i);
});
});
});

View File

@ -1,78 +0,0 @@
import { coins, StdFee } from "@cosmjs/amino";
import { Decimal, Uint53 } from "@cosmjs/math";
export type FeeTable = Record<string, StdFee>;
/**
* Denom checker for the Cosmos SDK 0.39 denom pattern
* (https://github.com/cosmos/cosmos-sdk/blob/v0.39.3/types/coin.go#L597-L598).
*
* This is like a regexp but with helpful error messages.
*/
function checkDenom(denom: string): void {
if (denom.length < 3 || denom.length > 16) {
throw new Error("Denom must be between 3 and 16 characters");
}
if (denom.match(/[^a-z0-9]/)) {
throw new Error("Denom must only contain lower case letters a-z and digits 0-9");
}
}
/**
* A gas price, i.e. the price of a single unit of gas. This is typically a fraction of
* the smallest fee token unit, such as 0.012utoken.
*/
export class GasPrice {
public readonly amount: Decimal;
public readonly denom: string;
public constructor(amount: Decimal, denom: string) {
this.amount = amount;
this.denom = denom;
}
/**
* Parses a gas price formatted as `<amount><denom>`, e.g. `GasPrice.fromString("0.012utoken")`.
*
* The denom must match the Cosmos SDK 0.39 pattern (https://github.com/cosmos/cosmos-sdk/blob/v0.39.3/types/coin.go#L597-L598).
* See `GasPrice` in @cosmjs/stargate for a more generic matcher.
*/
public static fromString(gasPrice: string): GasPrice {
// Use Decimal.fromUserInput and checkDenom for detailed checks and helpful error messages
const matchResult = gasPrice.match(/^([0-9.]+)([a-z][a-z0-9]*)$/i);
if (!matchResult) {
throw new Error("Invalid gas price string");
}
const [_, amount, denom] = matchResult;
checkDenom(denom);
const fractionalDigits = 18;
const decimalAmount = Decimal.fromUserInput(amount, fractionalDigits);
return new GasPrice(decimalAmount, denom);
}
}
export type GasLimits<T extends Record<string, StdFee>> = {
readonly [key in keyof T]: number;
};
function calculateFee(gasLimit: number, { denom, amount: gasPriceAmount }: GasPrice): StdFee {
const amount = Math.ceil(gasPriceAmount.multiply(new Uint53(gasLimit)).toFloatApproximation());
return {
amount: coins(amount, denom),
gas: gasLimit.toString(),
};
}
export function buildFeeTable<T extends Record<string, StdFee>>(
gasPrice: GasPrice,
defaultGasLimits: GasLimits<T>,
gasLimits: Partial<GasLimits<T>>,
): T {
return Object.entries(defaultGasLimits).reduce(
(feeTable, [type, defaultGasLimit]) => ({
...feeTable,
[type]: calculateFee(gasLimits[type] || defaultGasLimit, gasPrice),
}),
{} as T,
);
}

View File

@ -1,163 +0,0 @@
// Re-exports for backwards compatibility
export {
AccountData,
Algo,
AminoSignResponse,
Coin,
coin,
coins,
decodeAminoPubkey,
decodeBech32Pubkey,
decodeSignature,
encodeAminoPubkey,
encodeBech32Pubkey,
encodeSecp256k1Pubkey,
encodeSecp256k1Signature,
executeKdf,
extractKdfConfiguration,
isStdTx,
KdfConfiguration,
makeCosmoshubPath,
makeSignDoc,
makeStdTx,
AminoMsg as Msg,
OfflineAminoSigner as OfflineSigner,
parseCoins,
pubkeyToAddress,
pubkeyType,
Secp256k1HdWallet,
Secp256k1Wallet,
serializeSignDoc,
StdFee,
StdSignature,
StdSignDoc,
StdTx,
} from "@cosmjs/amino";
import { SinglePubkey } from "@cosmjs/amino";
/** @deprecated PubKey is deprecated. Use `SinglePubkey` or the more general `Pubkey` from `@cosmjs/amino`. */
export type PubKey = SinglePubkey;
import * as logs from "./logs";
export { logs };
export {
Account,
assertIsBroadcastTxSuccess,
Block,
BlockHeader,
BroadcastTxFailure,
BroadcastTxResult,
BroadcastTxSuccess,
CosmosClient,
GetSequenceResult,
IndexedTx,
isBroadcastTxFailure,
isBroadcastTxSuccess,
isSearchByHeightQuery,
isSearchBySentFromOrToQuery,
isSearchByTagsQuery,
SearchByHeightQuery,
SearchBySentFromOrToQuery,
SearchByTagsQuery,
SearchTxFilter,
SearchTxQuery,
} from "./cosmosclient";
export { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./fee";
export {
AuthAccountsResponse,
AuthExtension,
BankBalancesResponse,
BankExtension,
BaseAccount,
BlockResponse,
BroadcastMode,
BroadcastTxsResponse,
DistributionCommunityPoolResponse,
DistributionDelegatorRewardResponse,
DistributionDelegatorRewardsResponse,
DistributionExtension,
DistributionParametersResponse,
DistributionValidatorOutstandingRewardsResponse,
DistributionValidatorResponse,
DistributionValidatorRewardsResponse,
DistributionWithdrawAddressResponse,
EncodeTxResponse,
GovDepositResponse,
GovDepositsResponse,
GovExtension,
GovParametersResponse,
GovProposalResponse,
GovProposalsResponse,
GovProposerResponse,
GovTallyResponse,
GovVoteResponse,
GovVotesResponse,
LcdApiArray,
LcdClient,
MintAnnualProvisionsResponse,
MintExtension,
MintInflationResponse,
MintParametersResponse,
NodeInfoResponse,
normalizeLcdApiArray,
normalizePubkey,
SearchTxsResponse,
setupAuthExtension,
setupBankExtension,
setupDistributionExtension,
setupGovExtension,
setupMintExtension,
setupSlashingExtension,
setupStakingExtension,
setupSupplyExtension,
SlashingExtension,
SlashingParametersResponse,
SlashingSigningInfosResponse,
StakingDelegationResponse,
StakingDelegatorDelegationsResponse,
StakingDelegatorTransactionsResponse,
StakingDelegatorUnbondingDelegationsResponse,
StakingDelegatorValidatorResponse,
StakingDelegatorValidatorsResponse,
StakingExtension,
StakingHistoricalInfoResponse,
StakingParametersResponse,
StakingPoolResponse,
StakingRedelegationsResponse,
StakingUnbondingDelegationResponse,
StakingValidatorDelegationsResponse,
StakingValidatorResponse,
StakingValidatorsResponse,
StakingValidatorUnbondingDelegationsResponse,
SupplyExtension,
TxsResponse,
uint64ToNumber,
uint64ToString,
} from "./lcdapi";
export {
isMsgBeginRedelegate,
isMsgCreateValidator,
isMsgDelegate,
isMsgEditValidator,
isMsgFundCommunityPool,
isMsgMultiSend,
isMsgSend,
isMsgSetWithdrawAddress,
isMsgUndelegate,
isMsgWithdrawDelegatorReward,
isMsgWithdrawValidatorCommission,
MsgBeginRedelegate,
MsgCreateValidator,
MsgDelegate,
MsgEditValidator,
MsgFundCommunityPool,
MsgMultiSend,
MsgSend,
MsgSetWithdrawAddress,
MsgUndelegate,
MsgWithdrawDelegatorReward,
MsgWithdrawValidatorCommission,
} from "./msgs";
export { findSequenceForSignedTx } from "./sequence";
export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient";
export { CosmosSdkTx, isWrappedStdTx, WrappedStdTx, WrappedTx } from "./tx";

View File

@ -1,67 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import {
faucet,
launchpad,
makeRandomAddress,
nonNegativeIntegerMatcher,
pendingWithoutLaunchpad,
unused,
} from "../testutils.spec";
import { AuthExtension, setupAuthExtension } from "./auth";
import { LcdClient } from "./lcdclient";
function makeAuthClient(apiUrl: string): LcdClient & AuthExtension {
return LcdClient.withExtensions({ apiUrl }, setupAuthExtension);
}
describe("AuthExtension", () => {
it("works for unused account without pubkey", async () => {
pendingWithoutLaunchpad();
const client = makeAuthClient(launchpad.endpoint);
const { height, result } = await client.auth.account(unused.address);
expect(height).toMatch(nonNegativeIntegerMatcher);
expect(result).toEqual({
type: "cosmos-sdk/Account",
value: {
address: unused.address,
public_key: null, // not known to the chain
coins: [
{
amount: "1000000000",
denom: "ucosm",
},
{
amount: "1000000000",
denom: "ustake",
},
],
account_number: unused.accountNumber.toString(),
sequence: unused.sequence.toString(),
},
});
});
// This fails in the first test run if you forget to run `./scripts/launchpad/init.sh`
it("has correct pubkey for faucet", async () => {
pendingWithoutLaunchpad();
const client = makeAuthClient(launchpad.endpoint);
const { result } = await client.auth.account(faucet.address0);
expect(result.value).toEqual(
jasmine.objectContaining({
public_key: faucet.pubkey0,
}),
);
});
// This property is used by CosmWasmClient.getAccount
it("returns empty address for non-existent account", async () => {
pendingWithoutLaunchpad();
const client = makeAuthClient(launchpad.endpoint);
const nonExistentAccount = makeRandomAddress();
const { result } = await client.auth.account(nonExistentAccount);
expect(result).toEqual({
type: "cosmos-sdk/Account",
value: jasmine.objectContaining({ address: "" }),
});
});
});

View File

@ -1,78 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Coin, Pubkey } from "@cosmjs/amino";
import { LcdClient } from "./lcdclient";
/**
* A Cosmos SDK base account.
*
* This type describes the base account representation as returned
* by the Cosmos SDK 0.370.39 LCD API.
*
* @see https://docs.cosmos.network/master/modules/auth/02_state.html#base-account
*/
export interface BaseAccount {
/** Bech32 account address */
readonly address: string;
readonly coins: readonly Coin[];
/**
* The public key of the account. This is not available on-chain as long as the account
* did not send a transaction.
*
* This was a type/value object in Cosmos SDK 0.37, changed to bech32 in Cosmos SDK 0.38 ([1])
* and changed back to type/value object in Cosmos SDK 0.39 ([2]).
*
* [1]: https://github.com/cosmos/cosmos-sdk/pull/5280
* [2]: https://github.com/cosmos/cosmos-sdk/pull/6749
*/
readonly public_key: string | Pubkey | null;
/**
* The account number assigned by the blockchain.
*
* This was string encoded in Cosmos SDK 0.37, changed to number in Cosmos SDK 0.38 ([1])
* and changed back to string in Cosmos SDK 0.39 ([2]).
*
* [1]: https://github.com/cosmos/cosmos-sdk/pull/5280
* [2]: https://github.com/cosmos/cosmos-sdk/pull/6749
*/
readonly account_number: number | string;
/**
* The sequence number for replay protection.
*
* This was string encoded in Cosmos SDK 0.37, changed to number in Cosmos SDK 0.38 ([1])
* and changed back to string in Cosmos SDK 0.39 ([2]).
*
* [1]: https://github.com/cosmos/cosmos-sdk/pull/5280
* [2]: https://github.com/cosmos/cosmos-sdk/pull/6749
*/
readonly sequence: number | string;
}
export interface AuthAccountsResponse {
readonly height: string;
readonly result: {
readonly type: "cosmos-sdk/Account";
readonly value: BaseAccount;
};
}
export interface AuthExtension {
readonly auth: {
readonly account: (address: string) => Promise<AuthAccountsResponse>;
};
}
export function setupAuthExtension(base: LcdClient): AuthExtension {
return {
auth: {
account: async (address: string) => {
const path = `/auth/accounts/${address}`;
const responseData = await base.get(path);
if (responseData.result.type !== "cosmos-sdk/Account") {
throw new Error("Unexpected response data format");
}
return responseData as AuthAccountsResponse;
},
},
};
}

View File

@ -1,45 +0,0 @@
import {
launchpad,
makeRandomAddress,
nonNegativeIntegerMatcher,
pendingWithoutLaunchpad,
unused,
} from "../testutils.spec";
import { BankExtension, setupBankExtension } from "./bank";
import { LcdClient } from "./lcdclient";
function makeBankClient(apiUrl: string): LcdClient & BankExtension {
return LcdClient.withExtensions({ apiUrl }, setupBankExtension);
}
describe("BankExtension", () => {
it("returns correct values for the unused account", async () => {
pendingWithoutLaunchpad();
const client = makeBankClient(launchpad.endpoint);
const balances = await client.bank.balances(unused.address);
expect(balances).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
denom: "ucosm",
amount: "1000000000",
},
{
denom: "ustake",
amount: "1000000000",
},
],
});
});
it("returns an empty result for a non-existent account", async () => {
pendingWithoutLaunchpad();
const client = makeBankClient(launchpad.endpoint);
const nonExistentAddress = makeRandomAddress();
const balances = await client.bank.balances(nonExistentAddress);
expect(balances).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [],
});
});
});

View File

@ -1,25 +0,0 @@
import { Coin } from "@cosmjs/amino";
import { LcdClient } from "./lcdclient";
export interface BankBalancesResponse {
readonly height: string;
readonly result: readonly Coin[];
}
export interface BankExtension {
readonly bank: {
readonly balances: (address: string) => Promise<BankBalancesResponse>;
};
}
export function setupBankExtension(base: LcdClient): BankExtension {
return {
bank: {
balances: async (address: string) => {
const path = `/bank/balances/${address}`;
return base.get(path);
},
},
};
}

View File

@ -1,146 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { WrappedStdTx } from "../tx";
/**
* The mode used to send transaction
*
* @see https://cosmos.network/rpc/#/Transactions/post_txs
*/
export enum BroadcastMode {
/** Return after tx commit */
Block = "block",
/** Return after CheckTx */
Sync = "sync",
/** Return right away */
Async = "async",
}
/** A response from the /txs/encode endpoint */
export interface EncodeTxResponse {
/** base64-encoded amino-binary encoded representation */
readonly tx: string;
}
interface NodeInfo {
readonly protocol_version: {
readonly p2p: string;
readonly block: string;
readonly app: string;
};
readonly id: string;
readonly listen_addr: string;
readonly network: string;
readonly version: string;
readonly channels: string;
readonly moniker: string;
readonly other: {
readonly tx_index: string;
readonly rpc_address: string;
};
}
interface ApplicationVersion {
readonly name: string;
readonly server_name: string;
readonly client_name: string;
readonly version: string;
readonly commit: string;
readonly build_tags: string;
readonly go: string;
}
export interface NodeInfoResponse {
readonly node_info: NodeInfo;
readonly application_version: ApplicationVersion;
}
interface BlockId {
readonly hash: string;
// TODO: here we also have this
// parts: {
// total: '1',
// hash: '7AF200C78FBF9236944E1AB270F4045CD60972B7C265E3A9DA42973397572931'
// }
}
export interface BlockHeader {
readonly version: {
readonly block: string;
readonly app: string;
};
readonly height: string;
readonly chain_id: string;
/** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */
readonly time: string;
readonly last_commit_hash: string;
readonly last_block_id: BlockId;
/** Can be empty */
readonly data_hash: string;
readonly validators_hash: string;
readonly next_validators_hash: string;
readonly consensus_hash: string;
readonly app_hash: string;
/** Can be empty */
readonly last_results_hash: string;
/** Can be empty */
readonly evidence_hash: string;
readonly proposer_address: string;
}
interface Block {
readonly header: BlockHeader;
readonly data: {
/** Array of base64 encoded transactions */
readonly txs: readonly string[] | null;
};
}
export interface BlockResponse {
readonly block_id: BlockId;
readonly block: Block;
}
export interface TxsResponse {
readonly height: string;
readonly txhash: string;
/** 🤷‍♂️ */
readonly codespace?: string;
/** Falsy when transaction execution succeeded. Contains error code on error. */
readonly code?: number;
readonly raw_log: string;
readonly logs?: unknown[];
readonly tx: WrappedStdTx;
/** The gas limit as set by the user */
readonly gas_wanted?: string;
/** The gas used by the execution */
readonly gas_used?: string;
readonly timestamp: string;
}
export interface SearchTxsResponse {
readonly total_count: string;
readonly count: string;
readonly page_number: string;
readonly page_total: string;
readonly limit: string;
readonly txs: readonly TxsResponse[];
}
export interface BroadcastTxsResponse {
readonly height: string;
readonly txhash: string;
readonly code?: number;
/**
* The result data of the execution (hex encoded).
*
* @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/types/result.go#L101
*/
readonly data?: string;
readonly raw_log?: string;
/** The same as `raw_log` but deserialized? */
readonly logs?: unknown[];
/** The gas limit as set by the user */
readonly gas_wanted?: string;
/** The gas used by the execution */
readonly gas_used?: string;
}

View File

@ -1,188 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { coin, coins, makeSignDoc, makeStdTx, Secp256k1HdWallet } from "@cosmjs/amino";
import { Bech32 } from "@cosmjs/encoding";
import { sleep } from "@cosmjs/utils";
import { assertIsBroadcastTxSuccess } from "../cosmosclient";
import { MsgDelegate } from "../msgs";
import { SigningCosmosClient } from "../signingcosmosclient";
import {
bigDecimalMatcher,
faucet,
launchpad,
launchpadEnabled,
nonNegativeIntegerMatcher,
pendingWithoutLaunchpad,
} from "../testutils.spec";
import { DistributionExtension, setupDistributionExtension } from "./distribution";
import { LcdClient } from "./lcdclient";
function makeDistributionClient(apiUrl: string): LcdClient & DistributionExtension {
return LcdClient.withExtensions({ apiUrl }, setupDistributionExtension);
}
describe("DistributionExtension", () => {
const defaultFee = {
amount: coins(25000, "ucosm"),
gas: "1500000", // 1.5 million
};
beforeAll(async () => {
if (launchpadEnabled()) {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const chainId = await client.getChainId();
const msg: MsgDelegate = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
amount: coin(25000, "ustake"),
},
};
const memo = "Test delegation for launchpad";
const { accountNumber, sequence } = await client.getSequence();
const signDoc = makeSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence);
const { signed, signature } = await wallet.signAmino(faucet.address0, signDoc);
const signedTx = makeStdTx(signed, signature);
const result = await client.broadcastTx(signedTx);
assertIsBroadcastTxSuccess(result);
await sleep(75); // wait until transactions are indexed
}
});
describe("delegatorRewards", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeDistributionClient(launchpad.endpoint);
const response = await client.distribution.delegatorRewards(faucet.address0);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
rewards: [
{
validator_address: launchpad.validator.address,
reward: null,
},
],
total: null,
},
});
});
});
describe("delegatorReward", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeDistributionClient(launchpad.endpoint);
const response = await client.distribution.delegatorReward(
faucet.address0,
launchpad.validator.address,
);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [],
});
});
});
describe("withdrawAddress", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeDistributionClient(launchpad.endpoint);
const response = await client.distribution.withdrawAddress(faucet.address0);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: faucet.address0,
});
});
});
describe("validator", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeDistributionClient(launchpad.endpoint);
const response = await client.distribution.validator(launchpad.validator.address);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
// TODO: This smells like a bug in the backend to me
operator_address: Bech32.encode("cosmos", Bech32.decode(launchpad.validator.address).data),
self_bond_rewards: [
{ denom: "ucosm", amount: jasmine.stringMatching(bigDecimalMatcher) },
{ denom: "ustake", amount: jasmine.stringMatching(bigDecimalMatcher) },
],
val_commission: [
{ denom: "ucosm", amount: jasmine.stringMatching(bigDecimalMatcher) },
{ denom: "ustake", amount: jasmine.stringMatching(bigDecimalMatcher) },
],
},
});
});
});
describe("validatorRewards", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeDistributionClient(launchpad.endpoint);
const response = await client.distribution.validatorRewards(launchpad.validator.address);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{ denom: "ucosm", amount: jasmine.stringMatching(bigDecimalMatcher) },
{ denom: "ustake", amount: jasmine.stringMatching(bigDecimalMatcher) },
],
});
});
});
describe("validatorOutstandingRewards", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeDistributionClient(launchpad.endpoint);
const response = await client.distribution.validatorOutstandingRewards(launchpad.validator.address);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{ denom: "ucosm", amount: jasmine.stringMatching(bigDecimalMatcher) },
{ denom: "ustake", amount: jasmine.stringMatching(bigDecimalMatcher) },
],
});
});
});
describe("parameters", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeDistributionClient(launchpad.endpoint);
const response = await client.distribution.parameters();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
community_tax: "0.020000000000000000",
base_proposer_reward: "0.010000000000000000",
bonus_proposer_reward: "0.040000000000000000",
withdraw_addr_enabled: true,
},
});
});
});
describe("communityPool", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeDistributionClient(launchpad.endpoint);
const response = await client.distribution.communityPool();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{ denom: "ucosm", amount: jasmine.stringMatching(bigDecimalMatcher) },
{ denom: "ustake", amount: jasmine.stringMatching(bigDecimalMatcher) },
],
});
});
});
});

View File

@ -1,100 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Coin } from "@cosmjs/amino";
import { LcdClient } from "./lcdclient";
export interface RewardContainer {
readonly validator_address: string;
readonly reward: readonly Coin[] | null;
}
export interface DistributionDelegatorRewardsResponse {
readonly height: string;
readonly result: {
readonly rewards: readonly RewardContainer[] | null;
readonly total: readonly Coin[] | null;
};
}
export interface DistributionDelegatorRewardResponse {
readonly height: string;
readonly result: readonly Coin[];
}
export interface DistributionWithdrawAddressResponse {
readonly height: string;
readonly result: string;
}
export interface DistributionValidatorResponse {
readonly height: string;
readonly result: {
readonly operator_address: string;
readonly self_bond_rewards: readonly Coin[];
readonly val_commission: readonly Coin[];
};
}
export interface DistributionValidatorRewardsResponse {
readonly height: string;
readonly result: readonly Coin[];
}
export interface DistributionValidatorOutstandingRewardsResponse {
readonly height: string;
readonly result: readonly Coin[];
}
export interface DistributionParametersResponse {
readonly height: string;
readonly result: {
readonly community_tax: string;
readonly base_proposer_reward: string;
readonly bonus_proposer_reward: string;
readonly withdraw_addr_enabled: boolean;
};
}
export interface DistributionCommunityPoolResponse {
readonly height: string;
readonly result: readonly Coin[];
}
export interface DistributionExtension {
readonly distribution: {
readonly delegatorRewards: (delegatorAddress: string) => Promise<DistributionDelegatorRewardsResponse>;
readonly delegatorReward: (
delegatorAddress: string,
validatorAddress: string,
) => Promise<DistributionDelegatorRewardResponse>;
readonly withdrawAddress: (delegatorAddress: string) => Promise<DistributionWithdrawAddressResponse>;
readonly validator: (validatorAddress: string) => Promise<DistributionValidatorResponse>;
readonly validatorRewards: (validatorAddress: string) => Promise<DistributionValidatorRewardsResponse>;
readonly validatorOutstandingRewards: (
validatorAddress: string,
) => Promise<DistributionValidatorOutstandingRewardsResponse>;
readonly parameters: () => Promise<DistributionParametersResponse>;
readonly communityPool: () => Promise<DistributionCommunityPoolResponse>;
};
}
export function setupDistributionExtension(base: LcdClient): DistributionExtension {
return {
distribution: {
delegatorRewards: async (delegatorAddress: string) =>
base.get(`/distribution/delegators/${delegatorAddress}/rewards`),
delegatorReward: async (delegatorAddress: string, validatorAddress: string) =>
base.get(`/distribution/delegators/${delegatorAddress}/rewards/${validatorAddress}`),
withdrawAddress: async (delegatorAddress: string) =>
base.get(`/distribution/delegators/${delegatorAddress}/withdraw_address`),
validator: async (validatorAddress: string) => base.get(`/distribution/validators/${validatorAddress}`),
validatorRewards: async (validatorAddress: string) =>
base.get(`/distribution/validators/${validatorAddress}/rewards`),
validatorOutstandingRewards: async (validatorAddress: string) =>
base.get(`/distribution/validators/${validatorAddress}/outstanding_rewards`),
parameters: async () => base.get(`/distribution/parameters`),
communityPool: async () => base.get(`/distribution/community_pool`),
},
};
}

View File

@ -1,302 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { coins, makeSignDoc, Secp256k1HdWallet } from "@cosmjs/amino";
import { sleep } from "@cosmjs/utils";
import { assertIsBroadcastTxSuccess } from "../cosmosclient";
import { SigningCosmosClient } from "../signingcosmosclient";
import {
dateTimeStampMatcher,
faucet,
launchpad,
launchpadEnabled,
nonNegativeIntegerMatcher,
pendingWithoutLaunchpad,
} from "../testutils.spec";
import { GovExtension, GovParametersType, setupGovExtension } from "./gov";
import { LcdClient } from "./lcdclient";
function makeGovClient(apiUrl: string): LcdClient & GovExtension {
return LcdClient.withExtensions({ apiUrl }, setupGovExtension);
}
describe("GovExtension", () => {
const defaultFee = {
amount: coins(25000, "ucosm"),
gas: "1500000", // 1.5 million
};
let proposalId: string;
beforeAll(async () => {
if (launchpadEnabled()) {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const chainId = await client.getChainId();
const proposalMsg = {
type: "cosmos-sdk/MsgSubmitProposal",
value: {
content: {
type: "cosmos-sdk/TextProposal",
value: {
description: "This proposal proposes to test whether this proposal passes",
title: "Test Proposal",
},
},
proposer: faucet.address0,
initial_deposit: coins(25000000, "ustake"),
},
};
const proposalMemo = "Test proposal for wasmd";
const { accountNumber: proposalAccountNumber, sequence: proposalSequence } = await client.getSequence();
const proposalSignDoc = makeSignDoc(
[proposalMsg],
defaultFee,
chainId,
proposalMemo,
proposalAccountNumber,
proposalSequence,
);
const { signature: proposalSignature } = await wallet.signAmino(faucet.address0, proposalSignDoc);
const proposalTx = {
msg: [proposalMsg],
fee: defaultFee,
memo: proposalMemo,
signatures: [proposalSignature],
};
const proposalResult = await client.broadcastTx(proposalTx);
assertIsBroadcastTxSuccess(proposalResult);
proposalId = proposalResult.logs[0].events
.find(({ type }) => type === "submit_proposal")!
.attributes.find(({ key }) => key === "proposal_id")!.value;
const voteMsg = {
type: "cosmos-sdk/MsgVote",
value: {
proposal_id: proposalId,
voter: faucet.address0,
option: "Yes",
},
};
const voteMemo = "Test vote for wasmd";
const { accountNumber: voteAccountNumber, sequence: voteSequence } = await client.getSequence();
const voteSignDoc = makeSignDoc(
[voteMsg],
defaultFee,
chainId,
voteMemo,
voteAccountNumber,
voteSequence,
);
const { signature: voteSignature } = await wallet.signAmino(faucet.address0, voteSignDoc);
const voteTx = {
msg: [voteMsg],
fee: defaultFee,
memo: voteMemo,
signatures: [voteSignature],
};
await client.broadcastTx(voteTx);
await sleep(75); // wait until transactions are indexed
}
});
describe("parameters", () => {
it("works for deposit", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const paramsType = GovParametersType.Deposit;
const response = await client.gov.parameters(paramsType);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
min_deposit: [{ denom: "ustake", amount: "10000000" }],
max_deposit_period: "172800000000000",
},
});
});
it("works for tallying", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const paramsType = GovParametersType.Tallying;
const response = await client.gov.parameters(paramsType);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
quorum: "0.334000000000000000",
threshold: "0.500000000000000000",
veto: "0.334000000000000000",
},
});
});
it("works for voting", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const paramsType = GovParametersType.Voting;
const response = await client.gov.parameters(paramsType);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
voting_period: "172800000000000",
},
});
});
});
describe("proposals", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const response = await client.gov.proposals();
expect(response.height).toMatch(nonNegativeIntegerMatcher);
expect(response.result.length).toBeGreaterThanOrEqual(1);
expect(response.result[response.result.length - 1]).toEqual({
content: {
type: "cosmos-sdk/TextProposal",
value: {
title: "Test Proposal",
description: "This proposal proposes to test whether this proposal passes",
},
},
id: proposalId,
proposal_status: "VotingPeriod",
final_tally_result: { yes: "0", abstain: "0", no: "0", no_with_veto: "0" },
submit_time: jasmine.stringMatching(dateTimeStampMatcher),
deposit_end_time: jasmine.stringMatching(dateTimeStampMatcher),
total_deposit: [{ denom: "ustake", amount: "25000000" }],
voting_start_time: jasmine.stringMatching(dateTimeStampMatcher),
voting_end_time: jasmine.stringMatching(dateTimeStampMatcher),
});
});
});
describe("proposal", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const response = await client.gov.proposal(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
content: {
type: "cosmos-sdk/TextProposal",
value: {
title: "Test Proposal",
description: "This proposal proposes to test whether this proposal passes",
},
},
id: proposalId,
proposal_status: "VotingPeriod",
final_tally_result: { yes: "0", abstain: "0", no: "0", no_with_veto: "0" },
submit_time: jasmine.stringMatching(dateTimeStampMatcher),
deposit_end_time: jasmine.stringMatching(dateTimeStampMatcher),
total_deposit: [{ denom: "ustake", amount: "25000000" }],
voting_start_time: jasmine.stringMatching(dateTimeStampMatcher),
voting_end_time: jasmine.stringMatching(dateTimeStampMatcher),
},
});
});
});
describe("proposer", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const response = await client.gov.proposer(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
proposal_id: proposalId,
proposer: faucet.address0,
},
});
});
});
describe("deposits", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const response = await client.gov.deposits(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
proposal_id: proposalId,
depositor: faucet.address0,
amount: [{ denom: "ustake", amount: "25000000" }],
},
],
});
});
});
describe("deposit", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const response = await client.gov.deposit(proposalId, faucet.address0);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
proposal_id: proposalId,
depositor: faucet.address0,
amount: [{ denom: "ustake", amount: "25000000" }],
},
});
});
});
describe("tally", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const response = await client.gov.tally(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
yes: jasmine.stringMatching(nonNegativeIntegerMatcher),
abstain: jasmine.stringMatching(nonNegativeIntegerMatcher),
no: jasmine.stringMatching(nonNegativeIntegerMatcher),
no_with_veto: jasmine.stringMatching(nonNegativeIntegerMatcher),
},
});
});
});
describe("votes", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const response = await client.gov.votes(proposalId);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
proposal_id: proposalId,
voter: faucet.address0,
option: "Yes",
},
],
});
});
});
describe("vote", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeGovClient(launchpad.endpoint);
const response = await client.gov.vote(proposalId, faucet.address0);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
voter: faucet.address0,
proposal_id: proposalId,
option: "Yes",
},
});
});
});
});

View File

@ -1,151 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Coin } from "@cosmjs/amino";
import { LcdClient } from "./lcdclient";
export enum GovParametersType {
Deposit = "deposit",
Tallying = "tallying",
Voting = "voting",
}
export interface GovParametersDepositResponse {
readonly height: string;
readonly result: {
readonly min_deposit: readonly Coin[];
readonly max_deposit_period: string;
};
}
export interface GovParametersTallyingResponse {
readonly height: string;
readonly result: {
readonly quorum: string;
readonly threshold: string;
readonly veto: string;
};
}
export interface GovParametersVotingResponse {
readonly height: string;
readonly result: {
readonly voting_period: string;
};
}
export type GovParametersResponse =
| GovParametersDepositResponse
| GovParametersTallyingResponse
| GovParametersVotingResponse;
export interface Tally {
readonly yes: string;
readonly abstain: string;
readonly no: string;
readonly no_with_veto: string;
}
export interface Proposal {
readonly id: string;
readonly proposal_status: string;
readonly final_tally_result: Tally;
readonly submit_time: string;
readonly total_deposit: readonly Coin[];
readonly deposit_end_time: string;
readonly voting_start_time: string;
readonly voting_end_time: string;
readonly content: {
readonly type: string;
readonly value: {
readonly title: string;
readonly description: string;
};
};
}
export interface GovProposalsResponse {
readonly height: string;
readonly result: readonly Proposal[];
}
export interface GovProposalResponse {
readonly height: string;
readonly result: Proposal;
}
export interface GovProposerResponse {
readonly height: string;
readonly result: {
readonly proposal_id: string;
readonly proposer: string;
};
}
export interface Deposit {
readonly amount: readonly Coin[];
readonly proposal_id: string;
readonly depositor: string;
}
export interface GovDepositsResponse {
readonly height: string;
readonly result: readonly Deposit[];
}
export interface GovDepositResponse {
readonly height: string;
readonly result: Deposit;
}
export interface GovTallyResponse {
readonly height: string;
readonly result: Tally;
}
export interface Vote {
readonly voter: string;
readonly proposal_id: string;
readonly option: string;
}
export interface GovVotesResponse {
readonly height: string;
readonly result: readonly Vote[];
}
export interface GovVoteResponse {
readonly height: string;
readonly result: Vote;
}
export interface GovExtension {
readonly gov: {
readonly parameters: (parametersType: GovParametersType) => Promise<GovParametersResponse>;
readonly proposals: () => Promise<GovProposalsResponse>;
readonly proposal: (proposalId: string) => Promise<GovProposalResponse>;
readonly proposer: (proposalId: string) => Promise<GovProposerResponse>;
readonly deposits: (proposalId: string) => Promise<GovDepositsResponse>;
readonly deposit: (proposalId: string, depositorAddress: string) => Promise<GovDepositResponse>;
readonly tally: (proposalId: string) => Promise<GovTallyResponse>;
readonly votes: (proposalId: string) => Promise<GovVotesResponse>;
readonly vote: (proposalId: string, voterAddress: string) => Promise<GovVoteResponse>;
};
}
export function setupGovExtension(base: LcdClient): GovExtension {
return {
gov: {
parameters: async (parametersType: GovParametersType) => base.get(`/gov/parameters/${parametersType}`),
proposals: async () => base.get("/gov/proposals"),
proposal: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}`),
proposer: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}/proposer`),
deposits: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}/deposits`),
deposit: async (proposalId: string, depositorAddress: string) =>
base.get(`/gov/proposals/${proposalId}/deposits/${depositorAddress}`),
tally: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}/tally`),
votes: async (proposalId: string) => base.get(`/gov/proposals/${proposalId}/votes`),
vote: async (proposalId: string, voterAddress: string) =>
base.get(`/gov/proposals/${proposalId}/votes/${voterAddress}`),
},
};
}

View File

@ -1,84 +0,0 @@
//
// Standard modules (see tracking issue https://github.com/cosmos/cosmjs/issues/276)
//
export { AuthAccountsResponse, AuthExtension, BaseAccount, setupAuthExtension } from "./auth";
export { BankBalancesResponse, BankExtension, setupBankExtension } from "./bank";
export {
DistributionCommunityPoolResponse,
DistributionDelegatorRewardResponse,
DistributionDelegatorRewardsResponse,
DistributionExtension,
DistributionParametersResponse,
DistributionValidatorOutstandingRewardsResponse,
DistributionValidatorResponse,
DistributionValidatorRewardsResponse,
DistributionWithdrawAddressResponse,
setupDistributionExtension,
} from "./distribution";
export {
GovDepositResponse,
GovDepositsResponse,
GovExtension,
GovParametersResponse,
GovProposalResponse,
GovProposalsResponse,
GovProposerResponse,
GovTallyResponse,
GovVoteResponse,
GovVotesResponse,
setupGovExtension,
} from "./gov";
export {
MintAnnualProvisionsResponse,
MintExtension,
MintInflationResponse,
MintParametersResponse,
setupMintExtension,
} from "./mint";
export {
setupSlashingExtension,
SlashingExtension,
SlashingParametersResponse,
SlashingSigningInfosResponse,
} from "./slashing";
export {
setupStakingExtension,
StakingDelegationResponse,
StakingDelegatorDelegationsResponse,
StakingDelegatorTransactionsResponse,
StakingDelegatorUnbondingDelegationsResponse,
StakingDelegatorValidatorResponse,
StakingDelegatorValidatorsResponse,
StakingExtension,
StakingHistoricalInfoResponse,
StakingParametersResponse,
StakingPoolResponse,
StakingRedelegationsResponse,
StakingUnbondingDelegationResponse,
StakingValidatorDelegationsResponse,
StakingValidatorResponse,
StakingValidatorsResponse,
StakingValidatorUnbondingDelegationsResponse,
} from "./staking";
export { setupSupplyExtension, SupplyExtension, TotalSupplyAllResponse, TotalSupplyResponse } from "./supply";
//
// Base types
//
export {
BlockResponse,
BroadcastMode,
BroadcastTxsResponse,
EncodeTxResponse,
NodeInfoResponse,
SearchTxsResponse,
TxsResponse,
} from "./base";
export { LcdApiArray, LcdClient, normalizeLcdApiArray } from "./lcdclient";
//
// Utils for interacting with the client/API
//
export { normalizePubkey, uint64ToNumber, uint64ToString } from "./utils";

View File

@ -1,872 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import {
Coin,
coins,
makeCosmoshubPath,
makeSignDoc,
makeStdTx,
Secp256k1HdWallet,
StdFee,
StdTx,
} from "@cosmjs/amino";
import { assert, sleep } from "@cosmjs/utils";
import { isBroadcastTxFailure } from "../cosmosclient";
import { parseLogs } from "../logs";
import { MsgSend } from "../msgs";
import { SigningCosmosClient } from "../signingcosmosclient";
import cosmoshub from "../testdata/cosmoshub.json";
import {
faucet,
launchpad,
launchpadEnabled,
makeRandomAddress,
nonNegativeIntegerMatcher,
pendingWithoutLaunchpad,
tendermintIdMatcher,
unused,
} from "../testutils.spec";
import { isWrappedStdTx } from "../tx";
import { setupAuthExtension } from "./auth";
import { TxsResponse } from "./base";
import { LcdApiArray, LcdClient } from "./lcdclient";
describe("LcdClient", () => {
const defaultRecipientAddress = makeRandomAddress();
it("can be constructed", () => {
const client = new LcdClient(launchpad.endpoint);
expect(client).toBeTruthy();
});
describe("withModules", () => {
interface TotalSupplyAllResponse {
readonly height: string;
readonly result: LcdApiArray<Coin>;
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function setupSupplyExtension(base: LcdClient) {
return {
supply: {
totalAll: async (): Promise<TotalSupplyAllResponse> => {
return base.get(`/supply/total`);
},
},
};
}
interface BankBalancesResponse {
readonly height: string;
readonly result: readonly Coin[];
}
interface BankExtension {
readonly bank: {
readonly balances: (address: string) => Promise<BankBalancesResponse>;
};
}
function setupBankExtension(base: LcdClient): BankExtension {
return {
bank: {
balances: async (address: string) => {
const path = `/bank/balances/${address}`;
return base.get(path);
},
},
};
}
it("works for no extension", async () => {
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint });
expect(client).toBeTruthy();
});
it("works for one extension", async () => {
pendingWithoutLaunchpad();
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupSupplyExtension);
const supply = await client.supply.totalAll();
expect(supply).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
amount: jasmine.stringMatching(nonNegativeIntegerMatcher),
denom: "ucosm",
},
{
amount: jasmine.stringMatching(nonNegativeIntegerMatcher),
denom: "ustake",
},
],
});
});
it("works for two extensions", async () => {
pendingWithoutLaunchpad();
const client = LcdClient.withExtensions(
{ apiUrl: launchpad.endpoint },
setupSupplyExtension,
setupBankExtension,
);
const supply = await client.supply.totalAll();
expect(supply).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
amount: jasmine.stringMatching(nonNegativeIntegerMatcher),
denom: "ucosm",
},
{
amount: jasmine.stringMatching(nonNegativeIntegerMatcher),
denom: "ustake",
},
],
});
const balances = await client.bank.balances(unused.address);
expect(balances).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
denom: "ucosm",
amount: "1000000000",
},
{
denom: "ustake",
amount: "1000000000",
},
],
});
});
it("can merge two extensions into the same module", async () => {
pendingWithoutLaunchpad();
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function setupSupplyExtensionBasic(base: LcdClient) {
return {
supply: {
totalAll: async () => {
const path = `/supply/total`;
return base.get(path);
},
},
};
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function setupSupplyExtensionPremium(base: LcdClient) {
return {
supply: {
total: async (denom: string) => {
return base.get(`/supply/total/${denom}`);
},
},
};
}
const client = LcdClient.withExtensions(
{ apiUrl: launchpad.endpoint },
setupSupplyExtensionBasic,
setupSupplyExtensionPremium,
);
expect(client.supply.totalAll).toEqual(jasmine.any(Function));
expect(client.supply.total).toEqual(jasmine.any(Function));
});
});
// The /txs endpoints
describe("txById", () => {
let successful:
| {
readonly sender: string;
readonly recipient: string;
readonly hash: string;
}
| undefined;
let unsuccessful:
| {
readonly sender: string;
readonly recipient: string;
readonly hash: string;
}
| undefined;
beforeAll(async () => {
if (launchpadEnabled()) {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
{
const recipient = makeRandomAddress();
const amount = coins(1234567, "ucosm");
const result = await client.sendTokens(recipient, amount);
successful = {
sender: faucet.address0,
recipient: recipient,
hash: result.transactionHash,
};
}
{
const memo = "Sending more than I can afford";
const recipient = makeRandomAddress();
const amount = coins(123456700000000, "ucosm");
const sendMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address0,
to_address: recipient,
amount: amount,
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "80000", // 80k
};
const { accountNumber, sequence } = await client.getSequence();
const chainId = await client.getChainId();
const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence);
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const transactionId = await client.getIdentifier({ type: "cosmos-sdk/StdTx", value: signedTx });
const result = await client.broadcastTx(signedTx);
assert(isBroadcastTxFailure(result));
unsuccessful = {
sender: faucet.address0,
recipient: recipient,
hash: transactionId,
};
}
await sleep(75); // wait until transactions are indexed
}
});
it("works for successful transaction", async () => {
pendingWithoutLaunchpad();
assert(successful);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txById(successful.hash);
expect(result.height).toBeGreaterThanOrEqual(1);
expect(result.txhash).toEqual(successful.hash);
expect(result.codespace).toBeUndefined();
expect(result.code).toBeUndefined();
const logs = parseLogs(result.logs);
expect(logs).toEqual([
{
msg_index: 0,
log: "",
events: [
{
type: "message",
attributes: [
{ key: "action", value: "send" },
{ key: "sender", value: successful.sender },
{ key: "module", value: "bank" },
],
},
{
type: "transfer",
attributes: [
{ key: "recipient", value: successful.recipient },
{ key: "sender", value: successful.sender },
{ key: "amount", value: "1234567ucosm" },
],
},
],
},
]);
});
it("works for unsuccessful transaction", async () => {
pendingWithoutLaunchpad();
assert(unsuccessful);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txById(unsuccessful.hash);
expect(result.height).toBeGreaterThanOrEqual(1);
expect(result.txhash).toEqual(unsuccessful.hash);
expect(result.codespace).toEqual("sdk");
expect(result.code).toEqual(5);
expect(result.logs).toBeUndefined();
expect(result.raw_log).toContain("insufficient funds");
});
});
describe("txsQuery", () => {
let broadcasted:
| {
readonly sender: string;
readonly recipient: string;
readonly hash: string;
readonly height: number;
readonly tx: TxsResponse;
}
| undefined;
beforeAll(async () => {
if (launchpadEnabled()) {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const recipient = makeRandomAddress();
const amount = coins(1234567, "ucosm");
const result = await client.sendTokens(recipient, amount);
await sleep(75); // wait until tx is indexed
const txDetails = await new LcdClient(launchpad.endpoint).txById(result.transactionHash);
broadcasted = {
sender: faucet.address0,
recipient: recipient,
hash: result.transactionHash,
height: Number.parseInt(txDetails.height, 10),
tx: txDetails,
};
}
});
it("can query transactions by height", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txsQuery(`tx.height=${broadcasted.height}&limit=26`);
expect(result).toEqual({
count: jasmine.stringMatching(/^(1|2|3|4|5)$/), // 1-5 transactions as string
limit: "26",
page_number: "1",
page_total: "1",
total_count: jasmine.stringMatching(/^(1|2|3|4|5)$/), // 1-5 transactions as string
txs: jasmine.arrayContaining([broadcasted.tx]),
});
});
it("can query transactions by ID", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txsQuery(`tx.hash=${broadcasted.hash}&limit=26`);
expect(result).toEqual({
count: "1",
limit: "26",
page_number: "1",
page_total: "1",
total_count: "1",
txs: [broadcasted.tx],
});
});
it("can query transactions by sender", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txsQuery(`message.sender=${broadcasted.sender}&limit=200`);
expect(parseInt(result.count, 10)).toBeGreaterThanOrEqual(1);
expect(parseInt(result.limit, 10)).toEqual(200);
expect(parseInt(result.page_number, 10)).toEqual(1);
expect(parseInt(result.page_total, 10)).toEqual(1);
expect(parseInt(result.total_count, 10)).toBeGreaterThanOrEqual(1);
expect(result.txs.length).toBeGreaterThanOrEqual(1);
expect(result.txs[result.txs.length - 1]).toEqual(broadcasted.tx);
});
it("can query transactions by recipient", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const result = await client.txsQuery(`transfer.recipient=${broadcasted.recipient}&limit=200`);
expect(parseInt(result.count, 10)).toEqual(1);
expect(parseInt(result.limit, 10)).toEqual(200);
expect(parseInt(result.page_number, 10)).toEqual(1);
expect(parseInt(result.page_total, 10)).toEqual(1);
expect(parseInt(result.total_count, 10)).toEqual(1);
expect(result.txs.length).toBeGreaterThanOrEqual(1);
expect(result.txs[result.txs.length - 1]).toEqual(broadcasted.tx);
});
it("can filter by tx.hash and tx.minheight", async () => {
pending("This combination is broken 🤷‍♂️. Handle client-side at higher level.");
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const hashQuery = `tx.hash=${broadcasted.hash}`;
{
const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=0`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${broadcasted.height - 1}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${broadcasted.height}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${broadcasted.height + 1}`);
expect(count).toEqual("0");
}
});
it("can filter by recipient and tx.minheight", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const recipientQuery = `transfer.recipient=${broadcasted.recipient}`;
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=0`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${broadcasted.height - 1}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${broadcasted.height}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${broadcasted.height + 1}`);
expect(count).toEqual("0");
}
});
it("can filter by recipient and tx.maxheight", async () => {
pendingWithoutLaunchpad();
assert(broadcasted);
const client = new LcdClient(launchpad.endpoint);
const recipientQuery = `transfer.recipient=${broadcasted.recipient}`;
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=9999999999999`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${broadcasted.height + 1}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${broadcasted.height}`);
expect(count).toEqual("1");
}
{
const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${broadcasted.height - 1}`);
expect(count).toEqual("0");
}
});
});
describe("encodeTx", () => {
it("works for cosmoshub example", async () => {
pendingWithoutLaunchpad();
const client = new LcdClient(launchpad.endpoint);
assert(isWrappedStdTx(cosmoshub.tx));
const response = await client.encodeTx(cosmoshub.tx);
expect(response).toEqual(
jasmine.objectContaining({
tx: cosmoshub.tx_data,
}),
);
});
});
describe("broadcastTx", () => {
it("can send tokens", async () => {
pendingWithoutLaunchpad();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const memo = "My first contract on chain";
const theMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address0,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number, sequence } = (await client.auth.account(faucet.address0)).result.value;
const signDoc = makeSignDoc([theMsg], fee, launchpad.chainId, memo, account_number, sequence);
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const result = await client.broadcastTx(signedTx);
expect(result.code).toBeUndefined();
expect(result).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
txhash: jasmine.stringMatching(tendermintIdMatcher),
// code is not set
raw_log: jasmine.stringMatching(/^\[.+\]$/i),
logs: jasmine.any(Array),
gas_wanted: jasmine.stringMatching(nonNegativeIntegerMatcher),
gas_used: jasmine.stringMatching(nonNegativeIntegerMatcher),
});
});
it("can't send transaction with additional signatures", async () => {
pendingWithoutLaunchpad();
const account1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const account2 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(1)],
});
const account3 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(2)],
});
const [address1, address2, address3] = await Promise.all(
[account1, account2, account3].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const theMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value;
const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value;
const { account_number: an3, sequence: sequence3 } = (await client.auth.account(address3)).result.value;
const signDoc1 = makeSignDoc([theMsg], fee, launchpad.chainId, memo, an1, sequence1);
const signDoc2 = makeSignDoc([theMsg], fee, launchpad.chainId, memo, an2, sequence2);
const signDoc3 = makeSignDoc([theMsg], fee, launchpad.chainId, memo, an3, sequence3);
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
const { signature: signature3 } = await account3.signAmino(address3, signDoc3);
const signedTx: StdTx = {
msg: [theMsg],
fee: fee,
memo: memo,
signatures: [signature1, signature2, signature3],
};
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toEqual(4);
expect(broadcastResult.raw_log).toContain("wrong number of signers");
});
it("can send multiple messages with one signature", async () => {
pendingWithoutLaunchpad();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const accounts = await wallet.getAccounts();
const [{ address: walletAddress }] = accounts;
const memo = "My first contract on chain";
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: walletAddress,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: walletAddress,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "7654321",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number, sequence } = (await client.auth.account(walletAddress)).result.value;
const signDoc = makeSignDoc([msg1, msg2], fee, launchpad.chainId, memo, account_number, sequence);
const { signed, signature } = await wallet.signAmino(walletAddress, signDoc);
const signedTx = makeStdTx(signed, signature);
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toBeUndefined();
});
it("can send multiple messages with multiple signatures", async () => {
pendingWithoutLaunchpad();
const account1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const account2 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(1)],
});
const [address1, address2] = await Promise.all(
[account1, account2].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address2,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "7654321",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value;
const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value;
const signDoc1 = makeSignDoc([msg2, msg1], fee, launchpad.chainId, memo, an1, sequence1);
const signDoc2 = makeSignDoc([msg2, msg1], fee, launchpad.chainId, memo, an2, sequence2);
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
const signedTx: StdTx = {
msg: [msg2, msg1],
fee: fee,
memo: memo,
signatures: [signature2, signature1],
};
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toBeUndefined();
await sleep(500);
const searched = await client.txsQuery(`tx.hash=${broadcastResult.txhash}`);
expect(searched.txs.length).toEqual(1);
expect(searched.txs[0].tx.value.signatures).toEqual([signature2, signature1]);
});
it("can't send transaction with wrong signature order (1)", async () => {
pendingWithoutLaunchpad();
const account1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const account2 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(1)],
});
const [address1, address2] = await Promise.all(
[account1, account2].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address2,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "7654321",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value;
const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value;
const signDoc1 = makeSignDoc([msg1, msg2], fee, launchpad.chainId, memo, an1, sequence1);
const signDoc2 = makeSignDoc([msg1, msg2], fee, launchpad.chainId, memo, an2, sequence2);
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
const signedTx: StdTx = {
msg: [msg1, msg2],
fee: fee,
memo: memo,
signatures: [signature2, signature1],
};
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toEqual(8);
});
it("can't send transaction with wrong signature order (2)", async () => {
pendingWithoutLaunchpad();
const account1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const account2 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(1)],
});
const [address1, address2] = await Promise.all(
[account1, account2].map(async (wallet) => {
return (await wallet.getAccounts())[0].address;
}),
);
const memo = "My first contract on chain";
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address1,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: address2,
to_address: defaultRecipientAddress,
amount: [
{
denom: "ucosm",
amount: "7654321",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupAuthExtension);
const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value;
const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value;
const signDoc1 = makeSignDoc([msg2, msg1], fee, launchpad.chainId, memo, an1, sequence1);
const signDoc2 = makeSignDoc([msg2, msg1], fee, launchpad.chainId, memo, an2, sequence2);
const { signature: signature1 } = await account1.signAmino(address1, signDoc1);
const { signature: signature2 } = await account2.signAmino(address2, signDoc2);
const signedTx: StdTx = {
msg: [msg2, msg1],
fee: fee,
memo: memo,
signatures: [signature1, signature2],
};
const broadcastResult = await client.broadcastTx(signedTx);
expect(broadcastResult.code).toEqual(8);
});
});
});

View File

@ -1,314 +0,0 @@
/* eslint-disable no-dupe-class-members, @typescript-eslint/ban-types, @typescript-eslint/naming-convention */
import { StdTx } from "@cosmjs/amino";
import { assert, isNonNullObject } from "@cosmjs/utils";
import axios, { AxiosError, AxiosInstance } from "axios";
import { WrappedStdTx } from "../tx";
import {
BlockResponse,
BroadcastMode,
BroadcastTxsResponse,
EncodeTxResponse,
NodeInfoResponse,
SearchTxsResponse,
TxsResponse,
} from "./base";
/** Unfortunately, Cosmos SDK encodes empty arrays as null */
export type LcdApiArray<T> = readonly T[] | null;
export function normalizeLcdApiArray<T>(backend: LcdApiArray<T>): readonly T[] {
return backend || [];
}
type LcdExtensionSetup<P> = (base: LcdClient) => P;
export interface LcdClientBaseOptions {
readonly apiUrl: string;
readonly broadcastMode?: BroadcastMode;
}
// We want to get message data from 500 errors
// https://stackoverflow.com/questions/56577124/how-to-handle-500-error-message-with-axios
// this should be chained to catch one error and throw a more informative one
function parseAxiosError(err: AxiosError): never {
// use the error message sent from server, not default 500 msg
if (err.response?.data) {
let errorText: string;
const data = err.response.data;
// expect { error: string }, but otherwise dump
if (data.error && typeof data.error === "string") {
errorText = data.error;
} else if (typeof data === "string") {
errorText = data;
} else {
errorText = JSON.stringify(data);
}
throw new Error(`${errorText} (HTTP ${err.response.status})`);
} else {
throw err;
}
}
/**
* A client to the LCD's (light client daemon) API.
* This light client connects to Tendermint (i.e. the chain), encodes/decodes Amino data for us and provides a convenient JSON interface.
*
* This _JSON over HTTP_ API is sometimes referred to as "REST" or "RPC", which are both misleading terms
* for the same thing.
*
* Please note that the client to the LCD can not verify light client proofs. When using this,
* you need to trust the API provider as well as the network connection between client and API.
*
* @see https://cosmos.network/rpc
*/
export class LcdClient {
/** Constructs an LCD client with 0 extensions */
public static withExtensions(options: LcdClientBaseOptions): LcdClient;
/** Constructs an LCD client with 1 extension */
public static withExtensions<A extends object>(
options: LcdClientBaseOptions,
setupExtensionA: LcdExtensionSetup<A>,
): LcdClient & A;
/** Constructs an LCD client with 2 extensions */
public static withExtensions<A extends object, B extends object>(
options: LcdClientBaseOptions,
setupExtensionA: LcdExtensionSetup<A>,
setupExtensionB: LcdExtensionSetup<B>,
): LcdClient & A & B;
/** Constructs an LCD client with 3 extensions */
public static withExtensions<A extends object, B extends object, C extends object>(
options: LcdClientBaseOptions,
setupExtensionA: LcdExtensionSetup<A>,
setupExtensionB: LcdExtensionSetup<B>,
setupExtensionC: LcdExtensionSetup<C>,
): LcdClient & A & B & C;
/** Constructs an LCD client with 4 extensions */
public static withExtensions<A extends object, B extends object, C extends object, D extends object>(
options: LcdClientBaseOptions,
setupExtensionA: LcdExtensionSetup<A>,
setupExtensionB: LcdExtensionSetup<B>,
setupExtensionC: LcdExtensionSetup<C>,
setupExtensionD: LcdExtensionSetup<D>,
): LcdClient & A & B & C & D;
/** Constructs an LCD client with 5 extensions */
public static withExtensions<
A extends object,
B extends object,
C extends object,
D extends object,
E extends object,
>(
options: LcdClientBaseOptions,
setupExtensionA: LcdExtensionSetup<A>,
setupExtensionB: LcdExtensionSetup<B>,
setupExtensionC: LcdExtensionSetup<C>,
setupExtensionD: LcdExtensionSetup<D>,
setupExtensionE: LcdExtensionSetup<E>,
): LcdClient & A & B & C & D & E;
/** Constructs an LCD client with 6 extensions */
public static withExtensions<
A extends object,
B extends object,
C extends object,
D extends object,
E extends object,
F extends object,
>(
options: LcdClientBaseOptions,
setupExtensionA: LcdExtensionSetup<A>,
setupExtensionB: LcdExtensionSetup<B>,
setupExtensionC: LcdExtensionSetup<C>,
setupExtensionD: LcdExtensionSetup<D>,
setupExtensionE: LcdExtensionSetup<E>,
setupExtensionF: LcdExtensionSetup<F>,
): LcdClient & A & B & C & D & E & F;
/** Constructs an LCD client with 7 extensions */
public static withExtensions<
A extends object,
B extends object,
C extends object,
D extends object,
E extends object,
F extends object,
G extends object,
>(
options: LcdClientBaseOptions,
setupExtensionA: LcdExtensionSetup<A>,
setupExtensionB: LcdExtensionSetup<B>,
setupExtensionC: LcdExtensionSetup<C>,
setupExtensionD: LcdExtensionSetup<D>,
setupExtensionE: LcdExtensionSetup<E>,
setupExtensionF: LcdExtensionSetup<F>,
setupExtensionG: LcdExtensionSetup<G>,
): LcdClient & A & B & C & D & E & F & G;
/** Constructs an LCD client with 8 extensions */
public static withExtensions<
A extends object,
B extends object,
C extends object,
D extends object,
E extends object,
F extends object,
G extends object,
H extends object,
>(
options: LcdClientBaseOptions,
setupExtensionA: LcdExtensionSetup<A>,
setupExtensionB: LcdExtensionSetup<B>,
setupExtensionC: LcdExtensionSetup<C>,
setupExtensionD: LcdExtensionSetup<D>,
setupExtensionE: LcdExtensionSetup<E>,
setupExtensionF: LcdExtensionSetup<F>,
setupExtensionG: LcdExtensionSetup<G>,
setupExtensionH: LcdExtensionSetup<H>,
): LcdClient & A & B & C & D & E & F & G & H;
public static withExtensions(
options: LcdClientBaseOptions,
...extensionSetups: Array<LcdExtensionSetup<object>>
): any {
const client = new LcdClient(options.apiUrl, options.broadcastMode);
const extensions = extensionSetups.map((setupExtension) => setupExtension(client));
for (const extension of extensions) {
assert(isNonNullObject(extension), `Extension must be a non-null object`);
for (const [moduleKey, moduleValue] of Object.entries(extension)) {
assert(
isNonNullObject(moduleValue),
`Module must be a non-null object. Found type ${typeof moduleValue} for module "${moduleKey}".`,
);
const current = (client as any)[moduleKey] || {};
(client as any)[moduleKey] = {
...current,
...moduleValue,
};
}
}
return client;
}
private readonly client: AxiosInstance;
private readonly broadcastMode: BroadcastMode;
/**
* Creates a new client to interact with a Cosmos SDK light client daemon.
* This class tries to be a direct mapping onto the API. Some basic decoding and normalizatin is done
* but things like caching are done at a higher level.
*
* When building apps, you should not need to use this class directly. If you do, this indicates a missing feature
* in higher level components. Feel free to raise an issue in this case.
*
* @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API)
* @param broadcastMode Defines at which point of the transaction processing the broadcastTx method returns
*/
public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) {
const headers = {
post: { "Content-Type": "application/json" },
};
this.client = axios.create({
baseURL: apiUrl,
headers: headers,
});
this.broadcastMode = broadcastMode;
}
public async get(path: string, params?: Record<string, any>): Promise<any> {
const { data } = await this.client.get(path, { params }).catch(parseAxiosError);
if (data === null) {
throw new Error("Received null response from server");
}
return data;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
public async post(path: string, params: any): Promise<any> {
if (!isNonNullObject(params)) throw new Error("Got unexpected type of params. Expected object.");
const { data } = await this.client.post(path, params).catch(parseAxiosError);
if (data === null) {
throw new Error("Received null response from server");
}
return data;
}
// The /blocks endpoints
public async blocksLatest(): Promise<BlockResponse> {
const responseData = await this.get("/blocks/latest");
if (!responseData.block) {
throw new Error("Unexpected response data format");
}
return responseData as BlockResponse;
}
public async blocks(height: number): Promise<BlockResponse> {
const responseData = await this.get(`/blocks/${height}`);
if (!responseData.block) {
throw new Error("Unexpected response data format");
}
return responseData as BlockResponse;
}
// The /node_info endpoint
public async nodeInfo(): Promise<NodeInfoResponse> {
const responseData = await this.get("/node_info");
if (!responseData.node_info) {
throw new Error("Unexpected response data format");
}
return responseData as NodeInfoResponse;
}
// The /txs endpoints
public async txById(id: string): Promise<TxsResponse> {
const responseData = await this.get(`/txs/${id}`);
if (!responseData.tx) {
throw new Error("Unexpected response data format");
}
return responseData as TxsResponse;
}
public async txsQuery(query: string): Promise<SearchTxsResponse> {
const responseData = await this.get(`/txs?${query}`);
if (!responseData.txs) {
throw new Error("Unexpected response data format");
}
return responseData as SearchTxsResponse;
}
/** returns the amino-encoding of the transaction performed by the server */
public async encodeTx(tx: WrappedStdTx): Promise<EncodeTxResponse> {
const responseData = await this.post("/txs/encode", tx);
if (!responseData.tx) {
throw new Error("Unexpected response data format");
}
return responseData as EncodeTxResponse;
}
/**
* Broadcasts a signed transaction to the transaction pool.
* Depending on the client's broadcast mode, this might or might
* wait for checkTx or deliverTx to be executed before returning.
*
* @param tx a signed transaction as StdTx (i.e. not wrapped in type/value container)
*/
public async broadcastTx(tx: StdTx): Promise<BroadcastTxsResponse> {
const params = {
tx: tx,
mode: this.broadcastMode,
};
const responseData = await this.post("/txs", params);
if (!responseData.txhash) {
throw new Error("Unexpected response data format");
}
return responseData as BroadcastTxsResponse;
}
}

View File

@ -1,59 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import {
bigDecimalMatcher,
launchpad,
nonNegativeIntegerMatcher,
pendingWithoutLaunchpad,
smallDecimalMatcher,
} from "../testutils.spec";
import { LcdClient } from "./lcdclient";
import { MintExtension, setupMintExtension } from "./mint";
function makeMintClient(apiUrl: string): LcdClient & MintExtension {
return LcdClient.withExtensions({ apiUrl }, setupMintExtension);
}
describe("MintExtension", () => {
describe("parameters", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeMintClient(launchpad.endpoint);
const response = await client.mint.parameters();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
mint_denom: "ustake",
inflation_rate_change: "0.130000000000000000",
inflation_max: "0.200000000000000000",
inflation_min: "0.070000000000000000",
goal_bonded: "0.670000000000000000",
blocks_per_year: "6311520",
},
});
});
});
describe("inflation", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeMintClient(launchpad.endpoint);
const response = await client.mint.inflation();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: jasmine.stringMatching(smallDecimalMatcher),
});
});
});
describe("annualProvisions", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeMintClient(launchpad.endpoint);
const response = await client.mint.annualProvisions();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: jasmine.stringMatching(bigDecimalMatcher),
});
});
});
});

View File

@ -1,42 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { LcdClient } from "./lcdclient";
export interface MintParametersResponse {
readonly height: string;
readonly result: {
readonly mint_denom: string;
readonly inflation_rate_change: string;
readonly inflation_max: string;
readonly inflation_min: string;
readonly goal_bonded: string;
readonly blocks_per_year: string;
};
}
export interface MintInflationResponse {
readonly height: string;
readonly result: string;
}
export interface MintAnnualProvisionsResponse {
readonly height: string;
readonly result: string;
}
export interface MintExtension {
readonly mint: {
readonly parameters: () => Promise<MintParametersResponse>;
readonly inflation: () => Promise<MintInflationResponse>;
readonly annualProvisions: () => Promise<MintAnnualProvisionsResponse>;
};
}
export function setupMintExtension(base: LcdClient): MintExtension {
return {
mint: {
parameters: async () => base.get(`/minting/parameters`),
inflation: async () => base.get(`/minting/inflation`),
annualProvisions: async () => base.get(`/minting/annual-provisions`),
},
};
}

View File

@ -1,49 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { launchpad, nonNegativeIntegerMatcher, pendingWithoutLaunchpad } from "../testutils.spec";
import { LcdClient } from "./lcdclient";
import { setupSlashingExtension, SlashingExtension } from "./slashing";
function makeSlashingClient(apiUrl: string): LcdClient & SlashingExtension {
return LcdClient.withExtensions({ apiUrl }, setupSlashingExtension);
}
describe("SlashingExtension", () => {
describe("signingInfos", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeSlashingClient(launchpad.endpoint);
const response = await client.slashing.signingInfos();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
address: "cosmosvalcons1m74e42saykuj34ugc7weqq4kc97fn5ecqpdl2q",
start_height: "0",
index_offset: jasmine.stringMatching(nonNegativeIntegerMatcher),
jailed_until: "1970-01-01T00:00:00Z",
tombstoned: false,
missed_blocks_counter: "0",
},
],
});
});
});
describe("parameters", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeSlashingClient(launchpad.endpoint);
const response = await client.slashing.parameters();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
signed_blocks_window: "100",
min_signed_per_window: "0.500000000000000000",
downtime_jail_duration: "600000000000",
slash_fraction_double_sign: "0.050000000000000000",
slash_fraction_downtime: "0.010000000000000000",
},
});
});
});
});

View File

@ -1,47 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { LcdClient } from "./lcdclient";
interface SlashingSigningInfo {
readonly address: string;
readonly start_height: string;
readonly index_offset: string;
readonly jailed_until: string;
readonly tombstoned: boolean;
readonly missed_blocks_counter: string;
}
export interface SlashingSigningInfosResponse {
readonly height: string;
readonly result: readonly SlashingSigningInfo[];
}
export interface SlashingParametersResponse {
readonly height: string;
readonly result: {
readonly signed_blocks_window: string;
readonly min_signed_per_window: string;
readonly downtime_jail_duration: string;
readonly slash_fraction_double_sign: string;
readonly slash_fraction_downtime: string;
};
}
export interface SlashingExtension {
readonly slashing: {
readonly signingInfos: () => Promise<SlashingSigningInfosResponse>;
readonly parameters: () => Promise<SlashingParametersResponse>;
};
}
export function setupSlashingExtension(base: LcdClient): SlashingExtension {
return {
slashing: {
signingInfos: async () => {
return base.get(`/slashing/signing_infos`);
},
parameters: async () => {
return base.get(`/slashing/parameters`);
},
},
};
}

View File

@ -1,477 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { coin, coins, makeSignDoc, makeStdTx, Secp256k1HdWallet } from "@cosmjs/amino";
import { assert, sleep } from "@cosmjs/utils";
import { assertIsBroadcastTxSuccess } from "../cosmosclient";
import { MsgDelegate, MsgUndelegate } from "../msgs";
import { SigningCosmosClient } from "../signingcosmosclient";
import {
bigDecimalMatcher,
dateTimeStampMatcher,
faucet,
launchpad,
launchpadEnabled,
nonNegativeIntegerMatcher,
pendingWithoutLaunchpad,
} from "../testutils.spec";
import { LcdClient } from "./lcdclient";
import { BondStatus, setupStakingExtension, StakingExtension } from "./staking";
function makeStakingClient(apiUrl: string): LcdClient & StakingExtension {
return LcdClient.withExtensions({ apiUrl }, setupStakingExtension);
}
describe("StakingExtension", () => {
const defaultFee = {
amount: coins(25000, "ucosm"),
gas: "1500000", // 1.5 million
};
beforeAll(async () => {
if (launchpadEnabled()) {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const chainId = await client.getChainId();
{
const msg: MsgDelegate = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
amount: coin(25000, "ustake"),
},
};
const memo = "Test delegation for wasmd";
const { accountNumber, sequence } = await client.getSequence();
const signDoc = makeSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence);
const { signed, signature } = await wallet.signAmino(faucet.address0, signDoc);
const signedTx = makeStdTx(signed, signature);
const result = await client.broadcastTx(signedTx);
assertIsBroadcastTxSuccess(result);
}
{
const msg: MsgUndelegate = {
type: "cosmos-sdk/MsgUndelegate",
value: {
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
amount: coin(100, "ustake"),
},
};
const memo = "Test undelegation for wasmd";
const { accountNumber, sequence } = await client.getSequence();
const signDoc = makeSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence);
const { signed, signature } = await wallet.signAmino(faucet.address0, signDoc);
const signedTx = makeStdTx(signed, signature);
const result = await client.broadcastTx(signedTx);
assertIsBroadcastTxSuccess(result);
}
await sleep(75); // wait until transactions are indexed
}
});
describe("delegatorDelegations", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.delegatorDelegations(faucet.address0);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
shares: jasmine.stringMatching(bigDecimalMatcher),
balance: { denom: "ustake", amount: jasmine.stringMatching(nonNegativeIntegerMatcher) },
},
],
});
});
});
describe("delegatorUnbondingDelegations", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const { height, result } = await client.staking.delegatorUnbondingDelegations(faucet.address0);
expect(height).toMatch(nonNegativeIntegerMatcher);
assert(result);
expect(result).toEqual([
{
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
entries: jasmine.arrayContaining([
{
creation_height: jasmine.stringMatching(nonNegativeIntegerMatcher),
completion_time: jasmine.stringMatching(dateTimeStampMatcher),
initial_balance: "100",
balance: "100",
},
]),
},
]);
});
});
describe("delegatorTransactions", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.delegatorTransactions(faucet.address0);
expect(response.length).toEqual(3);
});
});
describe("delegatorValidators", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.delegatorValidators(faucet.address0);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
operator_address: launchpad.validator.address,
consensus_pubkey: launchpad.validator.pubkey,
jailed: false,
status: BondStatus.Bonded,
tokens: jasmine.stringMatching(nonNegativeIntegerMatcher),
delegator_shares: jasmine.stringMatching(bigDecimalMatcher),
description: {
moniker: launchpad.moniker,
identity: "",
website: "",
security_contact: "",
details: "",
},
unbonding_height: "0",
unbonding_time: "1970-01-01T00:00:00Z",
commission: {
commission_rates: {
rate: "0.100000000000000000",
max_rate: "0.200000000000000000",
max_change_rate: "0.010000000000000000",
},
update_time: launchpad.commissionUpdateTime,
},
min_self_delegation: "1",
},
],
});
});
});
describe("delegatorValidator", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.delegatorValidator(faucet.address0, launchpad.validator.address);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
operator_address: launchpad.validator.address,
consensus_pubkey: launchpad.validator.pubkey,
jailed: false,
status: BondStatus.Bonded,
tokens: jasmine.stringMatching(nonNegativeIntegerMatcher),
delegator_shares: jasmine.stringMatching(bigDecimalMatcher),
description: {
moniker: launchpad.moniker,
identity: "",
website: "",
security_contact: "",
details: "",
},
unbonding_height: "0",
unbonding_time: "1970-01-01T00:00:00Z",
commission: {
commission_rates: {
rate: "0.100000000000000000",
max_rate: "0.200000000000000000",
max_change_rate: "0.010000000000000000",
},
update_time: launchpad.commissionUpdateTime,
},
min_self_delegation: "1",
},
});
});
});
describe("delegation", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.delegation(faucet.address0, launchpad.validator.address);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
shares: jasmine.stringMatching(bigDecimalMatcher),
balance: { denom: "ustake", amount: jasmine.stringMatching(nonNegativeIntegerMatcher) },
},
});
});
});
describe("unbondingDelegation", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const { height, result } = await client.staking.unbondingDelegation(
faucet.address0,
launchpad.validator.address,
);
expect(height).toMatch(nonNegativeIntegerMatcher);
assert(result);
expect(result).toEqual({
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
entries: jasmine.arrayContaining([
{
creation_height: jasmine.stringMatching(nonNegativeIntegerMatcher),
completion_time: jasmine.stringMatching(dateTimeStampMatcher),
initial_balance: "100",
balance: "100",
},
]),
});
});
});
describe("redelegations", () => {
it("works", async () => {
// TODO: Set up a result for this test
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.redelegations();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [],
});
});
});
describe("validators", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.validators();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
operator_address: launchpad.validator.address,
consensus_pubkey: launchpad.validator.pubkey,
jailed: false,
status: BondStatus.Bonded,
tokens: jasmine.stringMatching(nonNegativeIntegerMatcher),
delegator_shares: jasmine.stringMatching(bigDecimalMatcher),
description: {
moniker: launchpad.moniker,
identity: "",
website: "",
security_contact: "",
details: "",
},
unbonding_height: "0",
unbonding_time: "1970-01-01T00:00:00Z",
commission: {
commission_rates: {
rate: "0.100000000000000000",
max_rate: "0.200000000000000000",
max_change_rate: "0.010000000000000000",
},
update_time: launchpad.commissionUpdateTime,
},
min_self_delegation: "1",
},
],
});
});
it("can filter by status with no results", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.validators({ status: "unbonded" });
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [],
});
});
it("can filter by status with some results", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.validators({ status: "bonded" });
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: [
{
operator_address: launchpad.validator.address,
consensus_pubkey: launchpad.validator.pubkey,
jailed: false,
status: BondStatus.Bonded,
tokens: jasmine.stringMatching(nonNegativeIntegerMatcher),
delegator_shares: jasmine.stringMatching(bigDecimalMatcher),
description: {
moniker: launchpad.moniker,
identity: "",
website: "",
security_contact: "",
details: "",
},
unbonding_height: "0",
unbonding_time: "1970-01-01T00:00:00Z",
commission: {
commission_rates: {
rate: "0.100000000000000000",
max_rate: "0.200000000000000000",
max_change_rate: "0.010000000000000000",
},
update_time: launchpad.commissionUpdateTime,
},
min_self_delegation: "1",
},
],
});
});
});
describe("validator", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.validator(launchpad.validator.address);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
operator_address: launchpad.validator.address,
consensus_pubkey: launchpad.validator.pubkey,
jailed: false,
status: BondStatus.Bonded,
tokens: jasmine.stringMatching(nonNegativeIntegerMatcher),
delegator_shares: jasmine.stringMatching(bigDecimalMatcher),
description: {
moniker: launchpad.moniker,
identity: "",
website: "",
security_contact: "",
details: "",
},
unbonding_height: "0",
unbonding_time: "1970-01-01T00:00:00Z",
commission: {
commission_rates: {
rate: "0.100000000000000000",
max_rate: "0.200000000000000000",
max_change_rate: "0.010000000000000000",
},
update_time: launchpad.commissionUpdateTime,
},
min_self_delegation: "1",
},
});
});
});
describe("validatorDelegations", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.validatorDelegations(launchpad.validator.address);
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: jasmine.arrayContaining([
{
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
shares: jasmine.stringMatching(bigDecimalMatcher),
balance: { denom: "ustake", amount: jasmine.stringMatching(nonNegativeIntegerMatcher) },
},
{
delegator_address: launchpad.validator.delegatorAddress,
validator_address: launchpad.validator.address,
shares: "250000000.000000000000000000",
balance: { denom: "ustake", amount: "250000000" },
},
]),
});
});
});
describe("validatorUnbondingDelegations", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const { height, result } = await client.staking.validatorUnbondingDelegations(
launchpad.validator.address,
);
expect(height).toMatch(nonNegativeIntegerMatcher);
assert(result);
expect(result).toEqual([
{
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
entries: jasmine.arrayContaining([
{
creation_height: jasmine.stringMatching(nonNegativeIntegerMatcher),
completion_time: jasmine.stringMatching(dateTimeStampMatcher),
initial_balance: "100",
balance: "100",
},
]),
},
]);
});
});
describe("historicalInfo", () => {
it("doesn't work yet", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const currentHeight = (await client.blocksLatest()).block.header.height;
return expectAsync(client.staking.historicalInfo(currentHeight)).toBeRejectedWithError(
/no historical info found \(HTTP 400\)/i,
);
});
});
describe("pool", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.pool();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
not_bonded_tokens: jasmine.stringMatching(nonNegativeIntegerMatcher),
bonded_tokens: jasmine.stringMatching(nonNegativeIntegerMatcher),
},
});
});
});
describe("parameters", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = makeStakingClient(launchpad.endpoint);
const response = await client.staking.parameters();
expect(response).toEqual({
height: jasmine.stringMatching(nonNegativeIntegerMatcher),
result: {
unbonding_time: "1814400000000000",
max_validators: 100,
max_entries: 7,
historical_entries: 0,
bond_denom: "ustake",
},
});
});
});
});

View File

@ -1,251 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Coin } from "@cosmjs/amino";
import { BlockHeader, SearchTxsResponse } from "./base";
import { LcdClient } from "./lcdclient";
/**
* Numeric bonding status
*
* @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.5/types/staking.go#L43-L49
*/
export enum BondStatus {
Unbonded = 0,
Unbonding = 1,
Bonded = 2,
}
interface Validator {
readonly operator_address: string;
readonly consensus_pubkey: string;
readonly jailed: boolean;
readonly status: BondStatus;
readonly tokens: string;
readonly delegator_shares: string;
readonly description: {
readonly moniker: string;
readonly identity: string;
readonly website: string;
readonly security_contact: string;
readonly details: string;
};
readonly unbonding_height: string;
readonly unbonding_time: string;
readonly commission: {
readonly commission_rates: {
readonly rate: string;
readonly max_rate: string;
readonly max_change_rate: string;
};
readonly update_time: string;
};
readonly min_self_delegation: string;
}
interface Delegation {
readonly delegator_address: string;
readonly validator_address: string;
readonly shares: string;
readonly balance: Coin;
}
export interface StakingDelegatorDelegationsResponse {
readonly height: string;
readonly result: readonly Delegation[];
}
interface UnbondingDelegationEntry {
readonly creation_height: string;
readonly completion_time: string;
readonly initial_balance: string;
readonly balance: string;
}
interface UnbondingDelegation {
readonly delegator_address: string;
readonly validator_address: string;
readonly entries: readonly UnbondingDelegationEntry[];
}
export interface StakingDelegatorUnbondingDelegationsResponse {
readonly height: string;
readonly result: readonly UnbondingDelegation[];
}
export type StakingDelegatorTransactionsResponse = readonly SearchTxsResponse[];
export interface StakingDelegatorValidatorsResponse {
readonly height: string;
readonly result: readonly Validator[];
}
export interface StakingDelegatorValidatorResponse {
readonly height: string;
readonly result: Validator;
}
export interface StakingDelegationResponse {
readonly height: string;
readonly result: Delegation;
}
export interface StakingUnbondingDelegationResponse {
readonly height: string;
readonly result: UnbondingDelegation | null;
}
interface RedelegationEntry {
readonly creation_height: string;
readonly completion_time: string;
readonly initial_balance: Coin;
readonly shares_dst: string;
}
interface Redelegation {
readonly delegator_address: string;
readonly validator_src_address: string;
readonly validator_dst_address: string;
readonly entries: readonly RedelegationEntry[];
}
export interface StakingRedelegationsResponse {
readonly height: string;
readonly result: readonly Redelegation[];
}
export interface StakingValidatorsParams {
/** @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.5/types/staking.go#L43-L49 */
readonly status?: "bonded" | "unbonded" | "unbonding";
readonly page?: number;
readonly limit?: number;
}
export interface StakingValidatorsResponse {
readonly height: string;
readonly result: readonly Validator[];
}
export interface StakingValidatorResponse {
readonly height: string;
readonly result: Validator;
}
export interface StakingValidatorDelegationsResponse {
readonly height: string;
readonly result: readonly Delegation[];
}
export interface StakingValidatorUnbondingDelegationsResponse {
readonly height: string;
readonly result: readonly UnbondingDelegation[];
}
interface HistoricalInfo {
readonly header: BlockHeader;
readonly validators: readonly Validator[];
}
export interface StakingHistoricalInfoResponse {
readonly height: string;
readonly result: HistoricalInfo;
}
export interface StakingPoolResponse {
readonly height: string;
readonly result: {
readonly not_bonded_tokens: string;
readonly bonded_tokens: string;
};
}
export interface StakingParametersResponse {
readonly height: string;
readonly result: {
readonly unbonding_time: string;
readonly max_validators: number;
readonly max_entries: number;
readonly historical_entries: number;
readonly bond_denom: string;
};
}
export interface StakingExtension {
readonly staking: {
/** Get all delegations from a delegator */
readonly delegatorDelegations: (delegatorAddress: string) => Promise<StakingDelegatorDelegationsResponse>;
/** Get all unbonding delegations from a delegator */
readonly delegatorUnbondingDelegations: (
delegatorAddress: string,
) => Promise<StakingDelegatorUnbondingDelegationsResponse>;
/** Get all staking txs (i.e msgs) from a delegator */
readonly delegatorTransactions: (
delegatorAddress: string,
) => Promise<StakingDelegatorTransactionsResponse>;
/** Query all validators that a delegator is bonded to */
readonly delegatorValidators: (delegatorAddress: string) => Promise<StakingDelegatorValidatorsResponse>;
/** Query a validator that a delegator is bonded to */
readonly delegatorValidator: (
delegatorAddress: string,
validatorAddress: string,
) => Promise<StakingDelegatorValidatorResponse>;
/** Query a delegation between a delegator and a validator */
readonly delegation: (
delegatorAddress: string,
validatorAddress: string,
) => Promise<StakingDelegationResponse>;
/** Query all unbonding delegations between a delegator and a validator */
readonly unbondingDelegation: (
delegatorAddress: string,
validatorAddress: string,
) => Promise<StakingUnbondingDelegationResponse>;
/** Query redelegations (filters in query params) */
readonly redelegations: () => Promise<StakingRedelegationsResponse>;
/** Get all validators */
readonly validators: (options?: StakingValidatorsParams) => Promise<StakingValidatorsResponse>;
/** Get a single validator info */
readonly validator: (validatorAddress: string) => Promise<StakingValidatorResponse>;
// Get all delegations to a validator
readonly validatorDelegations: (validatorAddress: string) => Promise<StakingValidatorDelegationsResponse>;
/** Get all unbonding delegations from a validator */
readonly validatorUnbondingDelegations: (
validatorAddress: string,
) => Promise<StakingValidatorUnbondingDelegationsResponse>;
/** Get HistoricalInfo at a given height */
readonly historicalInfo: (height: string) => Promise<StakingHistoricalInfoResponse>;
/** Get the current state of the staking pool */
readonly pool: () => Promise<StakingPoolResponse>;
/** Get the current staking parameter values */
readonly parameters: () => Promise<StakingParametersResponse>;
};
}
export function setupStakingExtension(base: LcdClient): StakingExtension {
return {
staking: {
delegatorDelegations: async (delegatorAddress: string) =>
base.get(`/staking/delegators/${delegatorAddress}/delegations`),
delegatorUnbondingDelegations: async (delegatorAddress: string) =>
base.get(`/staking/delegators/${delegatorAddress}/unbonding_delegations`),
delegatorTransactions: async (delegatorAddress: string) =>
base.get(`/staking/delegators/${delegatorAddress}/txs`),
delegatorValidators: async (delegatorAddress: string) =>
base.get(`/staking/delegators/${delegatorAddress}/validators`),
delegatorValidator: async (delegatorAddress: string, validatorAddress: string) =>
base.get(`/staking/delegators/${delegatorAddress}/validators/${validatorAddress}`),
delegation: async (delegatorAddress: string, validatorAddress: string) =>
base.get(`/staking/delegators/${delegatorAddress}/delegations/${validatorAddress}`),
unbondingDelegation: async (delegatorAddress: string, validatorAddress: string) =>
base.get(`/staking/delegators/${delegatorAddress}/unbonding_delegations/${validatorAddress}`),
redelegations: async () => base.get(`/staking/redelegations`),
validators: async (params?: StakingValidatorsParams) => base.get(`/staking/validators`, params),
validator: async (validatorAddress: string) => base.get(`/staking/validators/${validatorAddress}`),
validatorDelegations: async (validatorAddress: string) =>
base.get(`/staking/validators/${validatorAddress}/delegations`),
validatorUnbondingDelegations: async (validatorAddress: string) =>
base.get(`/staking/validators/${validatorAddress}/unbonding_delegations`),
historicalInfo: async (height: string) => base.get(`/staking/historical_info/${height}`),
pool: async () => base.get(`/staking/pool`),
parameters: async () => base.get(`/staking/parameters`),
},
};
}

View File

@ -1,40 +0,0 @@
import { launchpad, pendingWithoutLaunchpad } from "../testutils.spec";
import { LcdClient } from "./lcdclient";
import { setupSupplyExtension } from "./supply";
describe("SupplyExtension", () => {
describe("totalAll", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupSupplyExtension);
const supply = await client.supply.totalAll();
expect(supply).toEqual({
height: jasmine.stringMatching(/^[0-9]+$/),
result: [
{
amount: jasmine.stringMatching(/^[0-9]+$/),
denom: "ucosm",
},
{
amount: jasmine.stringMatching(/^[0-9]+$/),
denom: "ustake",
},
],
});
});
});
describe("total", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const client = LcdClient.withExtensions({ apiUrl: launchpad.endpoint }, setupSupplyExtension);
const supply = await client.supply.total("ucosm");
expect(supply).toEqual({
height: jasmine.stringMatching(/^[0-9]+$/),
result: jasmine.stringMatching(/^[0-9]+$/),
});
});
});
});

View File

@ -1,34 +0,0 @@
import { Coin } from "@cosmjs/amino";
import { LcdApiArray, LcdClient } from "./lcdclient";
export interface TotalSupplyAllResponse {
readonly height: string;
readonly result: LcdApiArray<Coin>;
}
export interface TotalSupplyResponse {
readonly height: string;
/** The amount */
readonly result: string;
}
export interface SupplyExtension {
readonly supply: {
readonly totalAll: () => Promise<TotalSupplyAllResponse>;
readonly total: (denom: string) => Promise<TotalSupplyResponse>;
};
}
export function setupSupplyExtension(base: LcdClient): SupplyExtension {
return {
supply: {
totalAll: async () => {
return base.get(`/supply/total`);
},
total: async (denom: string) => {
return base.get(`/supply/total/${denom}`);
},
},
};
}

View File

@ -1,95 +0,0 @@
import { Pubkey } from "@cosmjs/amino";
import { normalizePubkey, uint64ToNumber, uint64ToString } from "./utils";
describe("utils", () => {
describe("uint64ToNumber", () => {
it("works for numeric inputs", () => {
expect(uint64ToNumber(0)).toEqual(0);
expect(uint64ToNumber(1)).toEqual(1);
expect(uint64ToNumber(Number.MAX_SAFE_INTEGER)).toEqual(Number.MAX_SAFE_INTEGER);
});
it("works for string inputs", () => {
expect(uint64ToNumber("0")).toEqual(0);
expect(uint64ToNumber("1")).toEqual(1);
expect(uint64ToNumber("9007199254740991")).toEqual(Number.MAX_SAFE_INTEGER);
});
it("throws for invalid numbers", () => {
expect(() => uint64ToNumber(NaN)).toThrow();
expect(() => uint64ToNumber(1.1)).toThrow();
expect(() => uint64ToNumber(-1)).toThrow();
expect(() => uint64ToNumber(Number.MAX_SAFE_INTEGER + 1)).toThrow();
});
it("throws for invalid strings", () => {
expect(() => uint64ToNumber("")).toThrow();
expect(() => uint64ToNumber("0x22")).toThrow();
expect(() => uint64ToNumber("-1")).toThrow();
expect(() => uint64ToNumber("1.1")).toThrow();
expect(() => uint64ToNumber("9007199254740992")).toThrow();
});
});
describe("uint64ToString", () => {
it("works for numeric inputs", () => {
expect(uint64ToString(0)).toEqual("0");
expect(uint64ToString(1)).toEqual("1");
expect(uint64ToString(Number.MAX_SAFE_INTEGER)).toEqual("9007199254740991");
});
it("works for string inputs", () => {
expect(uint64ToString("0")).toEqual("0");
expect(uint64ToString("1")).toEqual("1");
expect(uint64ToString("9007199254740991")).toEqual("9007199254740991");
});
it("works for large string values", () => {
// for the string -> string version, the full uint64 range is supported
expect(uint64ToString("9007199254740992")).toEqual("9007199254740992");
expect(uint64ToString("18446744073709551615")).toEqual("18446744073709551615");
});
it("throws for invalid numbers", () => {
expect(() => uint64ToString(NaN)).toThrow();
expect(() => uint64ToString(1.1)).toThrow();
expect(() => uint64ToString(-1)).toThrow();
expect(() => uint64ToString(Number.MAX_SAFE_INTEGER + 1)).toThrow();
});
it("throws for invalid strings", () => {
expect(() => uint64ToString("")).toThrow();
expect(() => uint64ToString("0x22")).toThrow();
expect(() => uint64ToString("-1")).toThrow();
expect(() => uint64ToString("1.1")).toThrow();
expect(() => uint64ToString("18446744073709551616")).toThrow();
});
});
describe("normalizePubkey", () => {
it("interprets empty bech32 string as unset", () => {
expect(normalizePubkey("")).toBeNull();
});
it("decodes bech32 pubkey", () => {
const input = "cosmospub1addwnpepqd8sgxq7aw348ydctp3n5ajufgxp395hksxjzc6565yfp56scupfqhlgyg5";
expect(normalizePubkey(input)).toEqual({
type: "tendermint/PubKeySecp256k1",
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
});
});
it("interprets null as unset", () => {
expect(normalizePubkey(null)).toBeNull();
});
it("passes PubKey unchanged", () => {
const original: Pubkey = {
type: "tendermint/PubKeySecp256k1",
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
};
expect(normalizePubkey(original)).toEqual(original);
});
});
});

View File

@ -1,36 +0,0 @@
import { decodeBech32Pubkey, Pubkey } from "@cosmjs/amino";
import { Uint64 } from "@cosmjs/math";
/**
* Converts an integer expressed as number or string to a number.
* Throws if input is not a valid uint64 or if the value exceeds MAX_SAFE_INTEGER.
*
* This is needed for supporting Comsos SDK 0.37/0.38/0.39 with one client.
*/
export function uint64ToNumber(input: number | string): number {
const value = typeof input === "number" ? Uint64.fromNumber(input) : Uint64.fromString(input);
return value.toNumber();
}
/**
* Converts an integer expressed as number or string to a string.
* Throws if input is not a valid uint64.
*
* This is needed for supporting Comsos SDK 0.37/0.38/0.39 with one client.
*/
export function uint64ToString(input: number | string): string {
const value = typeof input === "number" ? Uint64.fromNumber(input) : Uint64.fromString(input);
return value.toString();
}
/**
* Normalizes a pubkey as in `BaseAccount.public_key` to allow supporting
* Comsos SDK 0.370.39.
*
* Returns null when unset.
*/
export function normalizePubkey(input: string | Pubkey | null): Pubkey | null {
if (!input) return null;
if (typeof input === "string") return decodeBech32Pubkey(input);
return input;
}

View File

@ -1,165 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { parseAttribute, parseEvent, parseLog, parseLogs } from "./logs";
describe("logs", () => {
describe("parseAttribute", () => {
it("works", () => {
const attr = parseAttribute({ key: "a", value: "b" });
expect(attr).toEqual({ key: "a", value: "b" });
});
it("works for empty value", () => {
const attr = parseAttribute({ key: "foobar", value: "" });
expect(attr).toEqual({ key: "foobar", value: "" });
});
it("normalized unset value to empty string", () => {
const attr = parseAttribute({ key: "amount" });
expect(attr).toEqual({ key: "amount", value: "" });
});
});
describe("parseEvent", () => {
it("works", () => {
const original = {
type: "message",
attributes: [
{
key: "action",
value: "store-code",
},
{
key: "module",
value: "wasm",
},
{
key: "action",
value: "store-code",
},
{
key: "sender",
value: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
},
{
key: "code_id",
value: "1",
},
],
} as const;
const event = parseEvent(original);
expect(event).toEqual(original);
});
it("works for transfer event", () => {
const original = {
type: "transfer",
attributes: [
{
key: "recipient",
value: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5",
},
{
key: "amount",
},
],
} as const;
const expected = {
type: "transfer",
attributes: [
{
key: "recipient",
value: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5",
},
{
key: "amount",
value: "",
},
],
} as const;
const event = parseEvent(original);
expect(event).toEqual(expected);
});
});
describe("parseLog", () => {
it("works", () => {
const original = {
msg_index: 0,
log: "",
events: [
{
type: "message",
attributes: [
{
key: "action",
value: "store-code",
},
{
key: "module",
value: "wasm",
},
{
key: "action",
value: "store-code",
},
{
key: "sender",
value: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
},
{
key: "code_id",
value: "1",
},
],
},
],
} as const;
const log = parseLog(original);
expect(log).toEqual(original);
});
});
describe("parseLogs", () => {
it("works", () => {
const original = [
{
msg_index: 0,
log: "",
events: [
{
type: "message",
attributes: [
{
key: "action",
value: "store-code",
},
{
key: "module",
value: "wasm",
},
{
key: "action",
value: "store-code",
},
{
key: "sender",
value: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
},
{
key: "code_id",
value: "1",
},
],
},
],
},
] as const;
const logs = parseLogs(original);
expect(logs).toEqual(original);
});
});
});

View File

@ -1,82 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { isNonNullObject } from "@cosmjs/utils";
export interface Attribute {
readonly key: string;
readonly value: string;
}
export interface Event {
readonly type: string;
readonly attributes: readonly Attribute[];
}
export interface Log {
readonly msg_index: number;
readonly log: string;
readonly events: readonly Event[];
}
export function parseAttribute(input: unknown): Attribute {
if (!isNonNullObject(input)) throw new Error("Attribute must be a non-null object");
const { key, value } = input as any;
if (typeof key !== "string" || !key) throw new Error("Attribute's key must be a non-empty string");
if (typeof value !== "string" && typeof value !== "undefined") {
throw new Error("Attribute's value must be a string or unset");
}
return {
key: key,
value: value || "",
};
}
export function parseEvent(input: unknown): Event {
if (!isNonNullObject(input)) throw new Error("Event must be a non-null object");
const { type, attributes } = input as any;
if (typeof type !== "string" || type === "") {
throw new Error(`Event type must be a non-empty string`);
}
if (!Array.isArray(attributes)) throw new Error("Event's attributes must be an array");
return {
type: type,
attributes: attributes.map(parseAttribute),
};
}
export function parseLog(input: unknown): Log {
if (!isNonNullObject(input)) throw new Error("Log must be a non-null object");
const { msg_index, log, events } = input as any;
if (typeof msg_index !== "number") throw new Error("Log's msg_index must be a number");
if (typeof log !== "string") throw new Error("Log's log must be a string");
if (!Array.isArray(events)) throw new Error("Log's events must be an array");
return {
msg_index: msg_index,
log: log,
events: events.map(parseEvent),
};
}
export function parseLogs(input: unknown): readonly Log[] {
if (!Array.isArray(input)) throw new Error("Logs must be an array");
return input.map(parseLog);
}
/**
* Searches in logs for the first event of the given event type and in that event
* for the first first attribute with the given attribute key.
*
* Throws if the attribute was not found.
*/
export function findAttribute(logs: readonly Log[], eventType: string, attrKey: string): Attribute {
const firstLogs = logs.find(() => true);
const out = firstLogs?.events
.find((event) => event.type === eventType)
?.attributes.find((attr) => attr.key === attrKey);
if (!out) {
throw new Error(
`Could not find attribute '${attrKey}' in first event of type '${eventType}' in first log.`,
);
}
return out;
}

View File

@ -1,335 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { AminoMsg, Coin } from "@cosmjs/amino";
// auth (no messages) - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/auth/auth.proto
// bank - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/bank/bank.proto
/** A high level transaction of the coin module */
export interface MsgSend extends AminoMsg {
readonly type: "cosmos-sdk/MsgSend";
readonly value: {
/** Bech32 account address */
readonly from_address: string;
/** Bech32 account address */
readonly to_address: string;
readonly amount: readonly Coin[];
};
}
export function isMsgSend(msg: AminoMsg): msg is MsgSend {
return (msg as MsgSend).type === "cosmos-sdk/MsgSend";
}
interface Input {
/** Bech32 account address */
readonly address: string;
readonly coins: readonly Coin[];
}
interface Output {
/** Bech32 account address */
readonly address: string;
readonly coins: readonly Coin[];
}
/** A high level transaction of the coin module */
export interface MsgMultiSend extends AminoMsg {
readonly type: "cosmos-sdk/MsgMultiSend";
readonly value: {
readonly inputs: readonly Input[];
readonly outputs: readonly Output[];
};
}
export function isMsgMultiSend(msg: AminoMsg): msg is MsgMultiSend {
return (msg as MsgMultiSend).type === "cosmos-sdk/MsgMultiSend";
}
// crisis - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/crisis/crisis.proto
/** Verifies a particular invariance */
export interface MsgVerifyInvariant extends AminoMsg {
readonly type: "cosmos-sdk/MsgVerifyInvariant";
readonly value: {
/** Bech32 account address */
readonly sender: string;
readonly invariant_module_name: string;
readonly invariant_route: string;
};
}
export function isMsgVerifyInvariant(msg: AminoMsg): msg is MsgVerifyInvariant {
return (msg as MsgVerifyInvariant).type === "cosmos-sdk/MsgVerifyInvariant";
}
// distribution - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/distribution/distribution.proto
/** Changes the withdraw address for a delegator (or validator self-delegation) */
export interface MsgSetWithdrawAddress extends AminoMsg {
// NOTE: Type string and names diverge here!
readonly type: "cosmos-sdk/MsgModifyWithdrawAddress";
readonly value: {
/** Bech32 account address */
readonly delegator_address: string;
/** Bech32 account address */
readonly withdraw_address: string;
};
}
export function isMsgSetWithdrawAddress(msg: AminoMsg): msg is MsgSetWithdrawAddress {
// NOTE: Type string and names diverge here!
return (msg as MsgSetWithdrawAddress).type === "cosmos-sdk/MsgModifyWithdrawAddress";
}
/** Message for delegation withdraw from a single validator */
export interface MsgWithdrawDelegatorReward extends AminoMsg {
// NOTE: Type string and names diverge here!
readonly type: "cosmos-sdk/MsgWithdrawDelegationReward";
readonly value: {
/** Bech32 account address */
readonly delegator_address: string;
/** Bech32 account address */
readonly validator_address: string;
};
}
export function isMsgWithdrawDelegatorReward(msg: AminoMsg): msg is MsgWithdrawDelegatorReward {
// NOTE: Type string and names diverge here!
return (msg as MsgWithdrawDelegatorReward).type === "cosmos-sdk/MsgWithdrawDelegationReward";
}
/** Message for validator withdraw */
export interface MsgWithdrawValidatorCommission extends AminoMsg {
readonly type: "cosmos-sdk/MsgWithdrawValidatorCommission";
readonly value: {
/** Bech32 account address */
readonly validator_address: string;
};
}
export function isMsgWithdrawValidatorCommission(msg: AminoMsg): msg is MsgWithdrawValidatorCommission {
return (msg as MsgWithdrawValidatorCommission).type === "cosmos-sdk/MsgWithdrawValidatorCommission";
}
/** Allows an account to directly fund the community pool. */
export interface MsgFundCommunityPool extends AminoMsg {
readonly type: "cosmos-sdk/MsgFundCommunityPool";
readonly value: {
readonly amount: readonly Coin[];
/** Bech32 account address */
readonly depositor: string;
};
}
export function isMsgFundCommunityPool(msg: AminoMsg): msg is MsgFundCommunityPool {
return (msg as MsgFundCommunityPool).type === "cosmos-sdk/MsgFundCommunityPool";
}
// evidence - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/evidence/evidence.proto
interface Any {
readonly type_url: string;
readonly value: Uint8Array;
}
/** Supports submitting arbitrary evidence */
export interface MsgSubmitEvidence extends AminoMsg {
readonly type: "cosmos-sdk/MsgSubmitEvidence";
readonly value: {
/** Bech32 account address */
readonly submitter: string;
readonly evidence: Any;
};
}
export function isMsgSubmitEvidence(msg: AminoMsg): msg is MsgSubmitEvidence {
return (msg as MsgSubmitEvidence).type === "cosmos-sdk/MsgSubmitEvidence";
}
// gov - https://github.com/cosmos/cosmos-sdk/blob/efa73c7edb31a7bd65786501da213b294f89267a/proto/cosmos/gov/gov.proto
/** Supports submitting arbitrary proposal content. */
export interface MsgSubmitProposal extends AminoMsg {
readonly type: "cosmos-sdk/MsgSubmitProposal";
readonly value: {
readonly content: Any;
readonly initial_deposit: readonly Coin[];
/** Bech32 account address */
readonly proposer: string;
};
}
export function isMsgSubmitProposal(msg: AminoMsg): msg is MsgSubmitProposal {
return (msg as MsgSubmitProposal).type === "cosmos-sdk/MsgSubmitProposal";
}
enum VoteOption {
VoteOptionUnspecified,
VoteOptionYes,
VoteOptionAbstain,
VoteOptionNo,
VoteOptionNoWithVeto,
}
/** Casts a vote */
export interface MsgVote extends AminoMsg {
readonly type: "cosmos-sdk/MsgVote";
readonly value: {
readonly proposal_id: number;
/** Bech32 account address */
readonly voter: string;
readonly option: VoteOption;
};
}
export function isMsgVote(msg: AminoMsg): msg is MsgVote {
return (msg as MsgVote).type === "cosmos-sdk/MsgVote";
}
/** Submits a deposit to an existing proposal */
export interface MsgDeposit extends AminoMsg {
readonly type: "cosmos-sdk/MsgDeposit";
readonly value: {
readonly proposal_id: number;
/** Bech32 account address */
readonly depositor: string;
readonly amount: readonly Coin[];
};
}
export function isMsgDeposit(msg: AminoMsg): msg is MsgDeposit {
return (msg as MsgDeposit).type === "cosmos-sdk/MsgDeposit";
}
// ibc
// mint (no messages) - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/mint/mint.proto
// params (no messages) - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/params/params.proto
// slashing - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/slashing/slashing.proto
/** Unjails a jailed validator */
export interface MsgUnjail extends AminoMsg {
readonly type: "cosmos-sdk/MsgUnjail";
readonly value: {
/** Bech32 account address */
readonly validator_addr: string;
};
}
export function isMsgUnjail(msg: AminoMsg): msg is MsgUnjail {
return (msg as MsgUnjail).type === "cosmos-sdk/MsgUnjail";
}
// staking - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/staking/staking.proto
/** The initial commission rates to be used for creating a validator */
interface CommissionRates {
readonly rate: string;
readonly max_rate: string;
readonly max_change_rate: string;
}
/** A validator description. */
interface Description {
readonly moniker: string;
readonly identity: string;
readonly website: string;
readonly security_contact: string;
readonly details: string;
}
/** Creates a new validator. */
export interface MsgCreateValidator extends AminoMsg {
readonly type: "cosmos-sdk/MsgCreateValidator";
readonly value: {
readonly description: Description;
readonly commission: CommissionRates;
readonly min_self_delegation: string;
/** Bech32 encoded delegator address */
readonly delegator_address: string;
/** Bech32 encoded validator address */
readonly validator_address: string;
/** Bech32 encoded public key */
readonly pubkey: string;
readonly value: Coin;
};
}
export function isMsgCreateValidator(msg: AminoMsg): msg is MsgCreateValidator {
return (msg as MsgCreateValidator).type === "cosmos-sdk/MsgCreateValidator";
}
/** Edits an existing validator. */
export interface MsgEditValidator extends AminoMsg {
readonly type: "cosmos-sdk/MsgEditValidator";
readonly value: {
readonly description: Description;
/** Bech32 encoded validator address */
readonly validator_address: string;
readonly commission_rate: string;
readonly min_self_delegation: string;
};
}
export function isMsgEditValidator(msg: AminoMsg): msg is MsgEditValidator {
return (msg as MsgEditValidator).type === "cosmos-sdk/MsgEditValidator";
}
/**
* Performs a delegation from a delegate to a validator.
*
* @see https://docs.cosmos.network/master/modules/staking/03_messages.html#msgdelegate
*/
export interface MsgDelegate extends AminoMsg {
readonly type: "cosmos-sdk/MsgDelegate";
readonly value: {
/** Bech32 encoded delegator address */
readonly delegator_address: string;
/** Bech32 encoded validator address */
readonly validator_address: string;
readonly amount: Coin;
};
}
export function isMsgDelegate(msg: AminoMsg): msg is MsgDelegate {
return (msg as MsgDelegate).type === "cosmos-sdk/MsgDelegate";
}
/** Performs a redelegation from a delegate and source validator to a destination validator */
export interface MsgBeginRedelegate extends AminoMsg {
readonly type: "cosmos-sdk/MsgBeginRedelegate";
readonly value: {
/** Bech32 encoded delegator address */
readonly delegator_address: string;
/** Bech32 encoded source validator address */
readonly validator_src_address: string;
/** Bech32 encoded destination validator address */
readonly validator_dst_address: string;
readonly amount: Coin;
};
}
export function isMsgBeginRedelegate(msg: AminoMsg): msg is MsgBeginRedelegate {
return (msg as MsgBeginRedelegate).type === "cosmos-sdk/MsgBeginRedelegate";
}
/** Performs an undelegation from a delegate and a validator */
export interface MsgUndelegate extends AminoMsg {
readonly type: "cosmos-sdk/MsgUndelegate";
readonly value: {
/** Bech32 encoded delegator address */
readonly delegator_address: string;
/** Bech32 encoded validator address */
readonly validator_address: string;
readonly amount: Coin;
};
}
export function isMsgUndelegate(msg: AminoMsg): msg is MsgUndelegate {
return (msg as MsgUndelegate).type === "cosmos-sdk/MsgUndelegate";
}
// upgrade (no messages) - see https://github.com/cosmos/cosmos-sdk/blob/efa73c7/proto/cosmos/upgrade/upgrade.proto

View File

@ -1,41 +0,0 @@
import { assert } from "@cosmjs/utils";
import { findSequenceForSignedTx } from "./sequence";
import response1 from "./testdata/txresponse1.json";
import response2 from "./testdata/txresponse2.json";
import response3 from "./testdata/txresponse3.json";
import { isWrappedStdTx } from "./tx";
// Those values must match ./testdata/txresponse*.json
const chainId = "testing";
const accountNumber = 4;
describe("sequence", () => {
describe("findSequenceForSignedTx", () => {
it("works", async () => {
assert(isWrappedStdTx(response1.tx));
assert(isWrappedStdTx(response2.tx));
assert(isWrappedStdTx(response3.tx));
const current = 100; // what we get from GET /auth/accounts/{address}
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, current)).toEqual(10);
// We know response3.height > response1.height, so the sequence must be at least 10+1
expect(await findSequenceForSignedTx(response3.tx, chainId, accountNumber, current, 11)).toEqual(19);
// We know response3.height > response2.height > response1.height, so the sequence must be at least 10+1 and smaller than 19
expect(await findSequenceForSignedTx(response2.tx, chainId, accountNumber, 19, 11)).toEqual(13);
});
it("returns undefined when sequence is not in range", async () => {
assert(isWrappedStdTx(response1.tx));
assert(isWrappedStdTx(response2.tx));
assert(isWrappedStdTx(response3.tx));
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 5)).toBeUndefined();
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 20, 11)).toBeUndefined();
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 20, 50)).toBeUndefined();
// upper bound is not included in the possible results
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 10)).toBeUndefined();
});
});
});

View File

@ -1,40 +0,0 @@
import { decodeSignature, makeSignDoc, serializeSignDoc } from "@cosmjs/amino";
import { Secp256k1, Secp256k1Signature, sha256 } from "@cosmjs/crypto";
import { WrappedStdTx } from "./tx";
/**
* Serach for sequence s with `min` <= `s` < `upperBound` to find the sequence that was used to sign the transaction
*
* @param tx The signed transaction
* @param chainId The chain ID for which this transaction was signed
* @param accountNumber The account number for which this transaction was signed
* @param upperBound The upper bound for the testing, i.e. sequence must be lower than this value
* @param min The lowest sequence that is tested
*
* @returns the sequence if a match was found and undefined otherwise
*/
export async function findSequenceForSignedTx(
tx: WrappedStdTx,
chainId: string,
accountNumber: number,
upperBound: number,
min = 0,
): Promise<number | undefined> {
const firstSignature = tx.value.signatures.find(() => true);
if (!firstSignature) throw new Error("Signature missing in tx");
const { pubkey, signature } = decodeSignature(firstSignature);
const secp256keSignature = Secp256k1Signature.fromFixedLength(signature);
for (let s = min; s < upperBound; s++) {
// console.log(`Trying sequence ${s}`);
const signBytes = serializeSignDoc(
makeSignDoc(tx.value.msg, tx.value.fee, chainId, tx.value.memo || "", accountNumber, s),
);
const prehashed = sha256(signBytes);
const valid = await Secp256k1.verifySignature(secp256keSignature, prehashed, pubkey);
if (valid) return s;
}
return undefined;
}

View File

@ -1,274 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { coin, coins, makeCosmoshubPath, Secp256k1HdWallet } from "@cosmjs/amino";
import { assert } from "@cosmjs/utils";
import { assertIsBroadcastTxSuccess, PrivateCosmosClient } from "./cosmosclient";
import { GasPrice } from "./fee";
import { MsgDelegate, MsgSend } from "./msgs";
import { PrivateSigningCosmosClient, SigningCosmosClient } from "./signingcosmosclient";
import {
base64Matcher,
faucet,
launchpad,
makeRandomAddress,
pendingWithoutLaunchpad,
} from "./testutils.spec";
describe("SigningCosmosClient", () => {
describe("makeReadOnly", () => {
it("can be constructed with default fees", async () => {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const openedClient = client as unknown as PrivateSigningCosmosClient;
expect(openedClient.fees).toEqual({
send: {
amount: [
{
amount: "2000",
denom: "ucosm",
},
],
gas: "80000",
},
});
});
it("can be constructed with custom gas price", async () => {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const gasPrice = GasPrice.fromString("3.14utest");
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet, gasPrice);
const openedClient = client as unknown as PrivateSigningCosmosClient;
expect(openedClient.fees).toEqual({
send: {
amount: [
{
amount: "251200", // 3.14 * 80_000
denom: "utest",
},
],
gas: "80000",
},
});
});
it("can be constructed with custom gas limits", async () => {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const gasLimits = {
send: 160000,
};
const client = new SigningCosmosClient(
launchpad.endpoint,
faucet.address0,
wallet,
undefined,
gasLimits,
);
const openedClient = client as unknown as PrivateSigningCosmosClient;
expect(openedClient.fees).toEqual({
send: {
amount: [
{
amount: "4000", // 0.025 * 160_000
denom: "ucosm",
},
],
gas: "160000",
},
});
});
it("can be constructed with custom gas price and gas limits", async () => {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const gasPrice = GasPrice.fromString("3.14utest");
const gasLimits = {
send: 160000,
};
const client = new SigningCosmosClient(
launchpad.endpoint,
faucet.address0,
wallet,
gasPrice,
gasLimits,
);
const openedClient = client as unknown as PrivateSigningCosmosClient;
expect(openedClient.fees).toEqual({
send: {
amount: [
{
amount: "502400", // 3.14 * 160_000
denom: "utest",
},
],
gas: "160000",
},
});
});
});
describe("getHeight", () => {
it("always uses authAccount implementation", async () => {
pendingWithoutLaunchpad();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const openedClient = client as unknown as PrivateCosmosClient;
const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough();
const authAccountsSpy = spyOn(openedClient.lcdClient.auth, "account").and.callThrough();
const height = await client.getHeight();
expect(height).toBeGreaterThan(0);
expect(blockLatestSpy).toHaveBeenCalledTimes(0);
expect(authAccountsSpy).toHaveBeenCalledTimes(1);
});
});
describe("sendTokens", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const amount = coins(7890, "ucosm");
const beneficiaryAddress = makeRandomAddress();
// no tokens here
const before = await client.getAccount(beneficiaryAddress);
expect(before).toBeUndefined();
// send
const result = await client.sendTokens(beneficiaryAddress, amount, "for dinner");
assertIsBroadcastTxSuccess(result);
const [firstLog] = result.logs;
expect(firstLog).toBeTruthy();
// got tokens
const after = await client.getAccount(beneficiaryAddress);
assert(after);
expect(after.balance).toEqual(amount);
});
});
describe("signAndBroadcast", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const msg: MsgDelegate = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
amount: coin(1234, "ustake"),
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "180000", // 180k
};
const result = await client.signAndBroadcast([msg], fee, "Use your power wisely");
assertIsBroadcastTxSuccess(result);
});
});
describe("sign", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet);
const msg1: MsgDelegate = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: faucet.address0,
validator_address: launchpad.validator.address,
amount: coin(1234, "ustake"),
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address0,
to_address: makeRandomAddress(),
amount: coins(1234567, "ucosm"),
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "180000", // 180k
};
const memo = "Use your power wisely";
const signed = await client.sign([msg1, msg2], fee, memo);
expect(signed.msg).toEqual([msg1, msg2]);
expect(signed.fee).toEqual(fee);
expect(signed.memo).toEqual(memo);
expect(signed.signatures).toEqual([
{
pub_key: faucet.pubkey0,
signature: jasmine.stringMatching(base64Matcher),
},
]);
// Ensure signed transaction is valid
const broadcastResult = await client.broadcastTx(signed);
assertIsBroadcastTxSuccess(broadcastResult);
});
});
describe("appendSignature", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const wallet0 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(0)],
});
const wallet1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, {
hdPaths: [makeCosmoshubPath(1)],
});
const client0 = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet0);
const client1 = new SigningCosmosClient(launchpad.endpoint, faucet.address1, wallet1);
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address0,
to_address: makeRandomAddress(),
amount: coins(1234567, "ucosm"),
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address1,
to_address: makeRandomAddress(),
amount: coins(1234567, "ucosm"),
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "160000", // 2*80k
};
const memo = "This must be authorized by the two of us";
const signed = await client0.sign([msg1, msg2], fee, memo);
const cosigned = await client1.appendSignature(signed);
expect(cosigned.msg).toEqual([msg1, msg2]);
expect(cosigned.fee).toEqual(fee);
expect(cosigned.memo).toEqual(memo);
expect(cosigned.signatures).toEqual([
{
pub_key: faucet.pubkey0,
signature: jasmine.stringMatching(base64Matcher),
},
{
pub_key: faucet.pubkey1,
signature: jasmine.stringMatching(base64Matcher),
},
]);
// Ensure signed transaction is valid
const broadcastResult = await client0.broadcastTx(cosigned);
assertIsBroadcastTxSuccess(broadcastResult);
});
});
});

View File

@ -1,128 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { AminoMsg, Coin, makeSignDoc, makeStdTx, OfflineAminoSigner, StdFee, StdTx } from "@cosmjs/amino";
import equals from "fast-deep-equal";
import { Account, BroadcastTxResult, CosmosClient, GetSequenceResult } from "./cosmosclient";
import { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./fee";
import { BroadcastMode } from "./lcdapi";
import { MsgSend } from "./msgs";
/**
* These fees are used by the higher level methods of SigningCosmosClient
*/
export interface CosmosFeeTable extends FeeTable {
readonly send: StdFee;
}
const defaultGasPrice = GasPrice.fromString("0.025ucosm");
const defaultGasLimits: GasLimits<CosmosFeeTable> = { send: 80000 };
/** Use for testing only */
export interface PrivateSigningCosmosClient {
readonly fees: CosmosFeeTable;
}
export class SigningCosmosClient extends CosmosClient {
public readonly fees: CosmosFeeTable;
public readonly signerAddress: string;
private readonly signer: OfflineAminoSigner;
/**
* Creates a new client with signing capability to interact with a Cosmos SDK blockchain. This is the bigger brother of CosmosClient.
*
* This instance does a lot of caching. In order to benefit from that you should try to use one instance
* for the lifetime of your application. When switching backends, a new instance must be created.
*
* @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API)
* @param signerAddress The address that will sign transactions using this instance. The `signer` must be able to sign with this address.
* @param signer An implementation of OfflineAminoSigner which can provide signatures for transactions, potentially requiring user input.
* @param gasPrice The price paid per unit of gas
* @param gasLimits Custom overrides for gas limits related to specific transaction types
* @param broadcastMode Defines at which point of the transaction processing the broadcastTx method returns
*/
public constructor(
apiUrl: string,
signerAddress: string,
signer: OfflineAminoSigner,
gasPrice: GasPrice = defaultGasPrice,
gasLimits: Partial<GasLimits<CosmosFeeTable>> = {},
broadcastMode = BroadcastMode.Block,
) {
super(apiUrl, broadcastMode);
this.anyValidAddress = signerAddress;
this.signerAddress = signerAddress;
this.signer = signer;
this.fees = buildFeeTable<CosmosFeeTable>(gasPrice, defaultGasLimits, gasLimits);
}
public override async getSequence(address?: string): Promise<GetSequenceResult> {
return super.getSequence(address || this.signerAddress);
}
public override async getAccount(address?: string): Promise<Account | undefined> {
return super.getAccount(address || this.signerAddress);
}
public async sendTokens(
recipientAddress: string,
amount: readonly Coin[],
memo = "",
): Promise<BroadcastTxResult> {
const sendMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: this.signerAddress,
to_address: recipientAddress,
amount: amount,
},
};
return this.signAndBroadcast([sendMsg], this.fees.send, memo);
}
/**
* Gets account number and sequence from the API, creates a sign doc,
* creates a single signature, assembles the signed transaction and broadcasts it.
*/
public async signAndBroadcast(
msgs: readonly AminoMsg[],
fee: StdFee,
memo = "",
): Promise<BroadcastTxResult> {
const signedTx = await this.sign(msgs, fee, memo);
return this.broadcastTx(signedTx);
}
/**
* Gets account number and sequence from the API, creates a sign doc,
* creates a single signature and assembles the signed transaction.
*/
public async sign(msgs: readonly AminoMsg[], fee: StdFee, memo = ""): Promise<StdTx> {
const { accountNumber, sequence } = await this.getSequence();
const chainId = await this.getChainId();
const signDoc = makeSignDoc(msgs, fee, chainId, memo, accountNumber, sequence);
const { signed, signature } = await this.signer.signAmino(this.signerAddress, signDoc);
return makeStdTx(signed, signature);
}
/**
* Gets account number and sequence from the API, creates a sign doc,
* creates a single signature and appends it to the existing signatures.
*/
public async appendSignature(signedTx: StdTx): Promise<StdTx> {
const { msg: msgs, fee, memo } = signedTx;
const { accountNumber, sequence } = await this.getSequence();
const chainId = await this.getChainId();
const signDoc = makeSignDoc(msgs, fee, chainId, memo, accountNumber, sequence);
const { signed, signature: additionalSignature } = await this.signer.signAmino(
this.signerAddress,
signDoc,
);
if (!equals(signDoc, signed)) {
throw new Error(
"The signed document differs from the one of the original transaction. This is not allowed since the resulting transaction will be invalid.",
);
}
return makeStdTx(signed, [...signedTx.signatures, additionalSignature]);
}
}

View File

@ -1,44 +0,0 @@
{
"//source": "https://hubble.figment.network/cosmos/chains/cosmoshub-3/blocks/415777/transactions/2BD600EA6090FC75FD844CA73542CC90A828770F4C01C5B483C3C1C43CCB65F4?format=json",
"tx": {
"type": "cosmos-sdk/StdTx",
"value": {
"msg": [
{
"type": "cosmos-sdk/MsgSend",
"value": {
"from_address": "cosmos1txqfn5jmcts0x0q7krdxj8tgf98tj0965vqlmq",
"to_address": "cosmos1nynns8ex9fq6sjjfj8k79ymkdz4sqth06xexae",
"amount": [
{
"denom": "uatom",
"amount": "35997500"
}
]
}
}
],
"fee": {
"amount": [
{
"denom": "uatom",
"amount": "2500"
}
],
"gas": "100000"
},
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "A5qFcJBJvEK/fOmEAY0DHNWwSRZ9TEfNZyH8VoVvDtAq"
},
"signature": "NK1Oy4EUGAsoC03c1wi9GG03JC/39LEdautC5Jk643oIbEPqeXHMwaqbdvO/Jws0X/NAXaN8SAy2KNY5Qml+5Q=="
}
],
"memo": ""
}
},
"tx_data": "ygEoKBapCkOoo2GaChRZgJnSW8Lg8zwesNppHWhJTrk8uhIUmSc4HyYqQahKSZHt4pN2aKsALu8aEQoFdWF0b20SCDM1OTk3NTAwEhMKDQoFdWF0b20SBDI1MDAQoI0GGmoKJuta6YchA5qFcJBJvEK/fOmEAY0DHNWwSRZ9TEfNZyH8VoVvDtAqEkA0rU7LgRQYCygLTdzXCL0YbTckL/f0sR1q60LkmTrjeghsQ+p5cczBqpt2878nCzRf80Bdo3xIDLYo1jlCaX7l",
"id": "2BD600EA6090FC75FD844CA73542CC90A828770F4C01C5B483C3C1C43CCB65F4"
}

View File

@ -1,57 +0,0 @@
{
"height": "15888",
"txhash": "672DEDE8EF4DE8B5818959F417CCA357079D4D7A19C4B65443C7FBF8176AABF9",
"raw_log": "[{\"msg_index\":0,\"log\":\"\",\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"send\"},{\"key\":\"sender\",\"value\":\"cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2\"},{\"key\":\"amount\",\"value\":\"75000ucosm\"}]}]}]",
"logs": [
{
"msg_index": 0,
"log": "",
"events": [
{
"type": "message",
"attributes": [
{ "key": "action", "value": "send" },
{ "key": "sender", "value": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6" },
{ "key": "module", "value": "bank" }
]
},
{
"type": "transfer",
"attributes": [
{ "key": "recipient", "value": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2" },
{ "key": "amount", "value": "75000ucosm" }
]
}
]
}
],
"gas_wanted": "200000",
"gas_used": "65407",
"tx": {
"type": "cosmos-sdk/StdTx",
"value": {
"msg": [
{
"type": "cosmos-sdk/MsgSend",
"value": {
"from_address": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
"to_address": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2",
"amount": [{ "denom": "ucosm", "amount": "75000" }]
}
}
],
"fee": { "amount": [{ "denom": "ucosm", "amount": "5000" }], "gas": "200000" },
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"
},
"signature": "US7oH8S/8TxVrtBQkOhHxAM+oDB2spNAEawgh6H8CCFLRMOJK+uvQZZ6ceUgUsvDbxwCz7re1RU272fymMYRZQ=="
}
],
"memo": "My first payment"
}
},
"timestamp": "2020-02-14T11:25:55Z"
}

View File

@ -1,57 +0,0 @@
{
"height": "16456",
"txhash": "7BFE4B93AF190F60132C62D08FDF50BE462FBCE374EB13D3FD0C32461E771EC0",
"raw_log": "[{\"msg_index\":0,\"log\":\"\",\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"send\"},{\"key\":\"sender\",\"value\":\"cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2\"},{\"key\":\"amount\",\"value\":\"75000ucosm\"}]}]}]",
"logs": [
{
"msg_index": 0,
"log": "",
"events": [
{
"type": "message",
"attributes": [
{ "key": "action", "value": "send" },
{ "key": "sender", "value": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6" },
{ "key": "module", "value": "bank" }
]
},
{
"type": "transfer",
"attributes": [
{ "key": "recipient", "value": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2" },
{ "key": "amount", "value": "75000ucosm" }
]
}
]
}
],
"gas_wanted": "200000",
"gas_used": "65407",
"tx": {
"type": "cosmos-sdk/StdTx",
"value": {
"msg": [
{
"type": "cosmos-sdk/MsgSend",
"value": {
"from_address": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
"to_address": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2",
"amount": [{ "denom": "ucosm", "amount": "75000" }]
}
}
],
"fee": { "amount": [{ "denom": "ucosm", "amount": "5000" }], "gas": "200000" },
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"
},
"signature": "ltvd9Rb3RF4zjbUVrpDpkok34g+py7XR8ZcM0tZUYRxxVdcMEin010x+ZFd/mOuutPj9fDmSENnienc/yi4msw=="
}
],
"memo": "My first payment"
}
},
"timestamp": "2020-02-14T11:35:41Z"
}

View File

@ -1,57 +0,0 @@
{
"height": "20730",
"txhash": "625BC75E697F73DA037387C34002BB2F682E7ACDCC4E015D3E90420516C6D0C8",
"raw_log": "[{\"msg_index\":0,\"log\":\"\",\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"send\"},{\"key\":\"sender\",\"value\":\"cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2\"},{\"key\":\"amount\",\"value\":\"75000ucosm\"}]}]}]",
"logs": [
{
"msg_index": 0,
"log": "",
"events": [
{
"type": "message",
"attributes": [
{ "key": "action", "value": "send" },
{ "key": "sender", "value": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6" },
{ "key": "module", "value": "bank" }
]
},
{
"type": "transfer",
"attributes": [
{ "key": "recipient", "value": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2" },
{ "key": "amount", "value": "75000ucosm" }
]
}
]
}
],
"gas_wanted": "200000",
"gas_used": "65407",
"tx": {
"type": "cosmos-sdk/StdTx",
"value": {
"msg": [
{
"type": "cosmos-sdk/MsgSend",
"value": {
"from_address": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
"to_address": "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2",
"amount": [{ "denom": "ucosm", "amount": "75000" }]
}
}
],
"fee": { "amount": [{ "denom": "ucosm", "amount": "5000" }], "gas": "200000" },
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"
},
"signature": "eOFGl1tIHDMv3JdCK9fRSikVbYUD8+B0ksb3dJFya8MPYgpEpdSA7zZc+5n/cW6LR/BJdib4nqmJQv1yD9lm3g=="
}
],
"memo": "My first payment"
}
},
"timestamp": "2020-02-14T12:48:56Z"
}

View File

@ -1,94 +0,0 @@
import { Random } from "@cosmjs/crypto";
import { Bech32 } from "@cosmjs/encoding";
export function makeRandomAddress(): string {
return Bech32.encode("cosmos", Random.getBytes(20));
}
export const nonNegativeIntegerMatcher = /^[0-9]+$/;
/** Matches decimals < 1.0 */
export const smallDecimalMatcher = /^0\.[0-9]+$/;
/** Matches decimals >= 1.0 */
export const bigDecimalMatcher = /^[1-9][0-9]*\.[0-9]+$/;
export const tendermintIdMatcher = /^[0-9A-F]{64}$/;
export const tendermintOptionalIdMatcher = /^([0-9A-F]{64}|)$/;
export const tendermintAddressMatcher = /^[0-9A-F]{40}$/;
export const tendermintShortHashMatcher = /^[0-9a-f]{40}$/;
export const dateTimeStampMatcher = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]+)?Z$/;
export const semverMatcher = /^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/;
/** @see https://rgxdb.com/r/1NUN74O6 */
export const base64Matcher =
/^(?:[a-zA-Z0-9+/]{4})*(?:|(?:[a-zA-Z0-9+/]{3}=)|(?:[a-zA-Z0-9+/]{2}==)|(?:[a-zA-Z0-9+/]{1}===))$/;
export const hexMatcher = /^([0-9a-fA-F][0-9a-fA-F])*$/;
// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32
export const bech32AddressMatcher = /^[\x21-\x7e]{1,83}1[02-9ac-hj-np-z]{38}$/;
export const launchpad = {
endpoint: "http://localhost:1317",
chainId: "testing",
moniker: "node001",
commissionUpdateTime: "2020-10-08T10:18:11.2275025Z",
validator: {
pubkey: "cosmosvalconspub1zcjduepqf62c9h86qqn4g9s4khcng86quanw8rn5mm6lf69c99vxff0302ksv2ljyl",
address: "cosmosvaloper1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r3arurr",
delegatorAddress: "cosmos1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r5fhf0s",
},
};
export 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",
pubkey0: {
type: "tendermint/PubKeySecp256k1",
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
},
pubkey1: {
type: "tendermint/PubKeySecp256k1",
value: "AiDosfIbBi54XJ1QjCeApumcy/FjdtF+YhywPf3DKTx7",
},
pubkey2: {
type: "tendermint/PubKeySecp256k1",
value: "AzQg33JZqH7vSsm09esZY5bZvmzYwE/SY78cA0iLxpD7",
},
pubkey3: {
type: "tendermint/PubKeySecp256k1",
value: "A3gOAlB6aiRTCPvWMQg2+ZbGYNsLd8qlvV28m8p2UhY2",
},
pubkey4: {
type: "tendermint/PubKeySecp256k1",
value: "Aum2063ub/ErUnIUB36sK55LktGUStgcbSiaAnL1wadu",
},
address0: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
address1: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
address2: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
address3: "cosmos142u9fgcjdlycfcez3lw8x6x5h7rfjlnfhpw2lx",
address4: "cosmos1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r0dcjvx",
};
/** Unused account */
export const unused = {
pubkey: {
type: "tendermint/PubKeySecp256k1",
value: "ArkCaFUJ/IH+vKBmNRCdUVl3mCAhbopk9jjW4Ko4OfRQ",
},
address: "cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u",
accountNumber: 19,
sequence: 0,
};
export function launchpadEnabled(): boolean {
return !!process.env.LAUNCHPAD_ENABLED;
}
export function pendingWithoutLaunchpad(): void {
if (!launchpadEnabled()) {
return pending("Set LAUNCHPAD_ENABLED to enable Launchpad-based tests");
}
}
/** Returns first element. Throws if array has a different length than 1. */
export function fromOneElementArray<T>(elements: ArrayLike<T>): T {
if (elements.length !== 1) throw new Error(`Expected exactly one element but got ${elements.length}`);
return elements[0];
}

View File

@ -1,24 +0,0 @@
import { isStdTx, StdTx } from "@cosmjs/amino";
/**
* An Amino JSON wrapper around the Tx interface
*/
export interface WrappedTx {
readonly type: string;
readonly value: any;
}
/**
* An Amino JSON wrapper around StdTx
*/
export interface WrappedStdTx extends WrappedTx {
readonly type: "cosmos-sdk/StdTx";
readonly value: StdTx;
}
export function isWrappedStdTx(wrapped: WrappedTx): wrapped is WrappedStdTx {
return (wrapped as WrappedStdTx).type === "cosmos-sdk/StdTx" && isStdTx(wrapped.value);
}
/** @deprecated use WrappedStdTx */
export type CosmosSdkTx = WrappedStdTx;

View File

@ -1,9 +0,0 @@
{
// extend your base config so you don't have to redefine your compilerOptions
"extends": "./tsconfig.json",
"include": [
"src/**/*",
"*.js",
".eslintrc.js"
]
}

View File

@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"outDir": "build",
"rootDir": "src"
},
"include": [
"src/**/*"
]
}

View File

@ -1,11 +0,0 @@
const packageJson = require("./package.json");
module.exports = {
entryPoints: ["./src"],
out: "docs",
exclude: "**/*.spec.ts",
name: `${packageJson.name} Documentation`,
readme: "README.md",
excludeExternals: true,
excludePrivate: true,
};

View File

@ -1,35 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
const glob = require("glob");
const path = require("path");
const webpack = require("webpack");
const target = "web";
const distdir = path.join(__dirname, "dist", "web");
module.exports = [
{
// bundle used for Karma tests
target: target,
entry: glob.sync("./build/**/*.spec.js"),
output: {
path: distdir,
filename: "tests.js",
},
plugins: [
new webpack.EnvironmentPlugin({ LAUNCHPAD_ENABLED: "" }),
new webpack.ProvidePlugin({
Buffer: ["buffer", "Buffer"],
}),
],
resolve: {
fallback: {
buffer: false,
crypto: false,
events: false,
path: false,
stream: require.resolve("stream-browserify"),
string_decoder: false,
},
},
},
];

View File

@ -46,7 +46,6 @@
"semver": "^7.3.2" "semver": "^7.3.2"
}, },
"devDependencies": { "devDependencies": {
"@cosmjs/launchpad": "workspace:packages/launchpad",
"@cosmjs/stargate": "workspace:packages/stargate", "@cosmjs/stargate": "workspace:packages/stargate",
"@istanbuljs/nyc-config-typescript": "^1.0.1", "@istanbuljs/nyc-config-typescript": "^1.0.1",
"@ledgerhq/hw-transport": "^5.25.0", "@ledgerhq/hw-transport": "^5.25.0",

View File

@ -9,10 +9,6 @@ import {
} from "@cosmjs/amino"; } from "@cosmjs/amino";
import { Secp256k1, Secp256k1Signature, sha256 } from "@cosmjs/crypto"; import { Secp256k1, Secp256k1Signature, sha256 } from "@cosmjs/crypto";
import { fromBase64 } from "@cosmjs/encoding"; import { fromBase64 } from "@cosmjs/encoding";
import {
assertIsBroadcastTxSuccess as assertIsBroadcastTxSuccessLaunchpad,
SigningCosmosClient,
} from "@cosmjs/launchpad";
import { import {
assertIsDeliverTxSuccess as assertIsDeliverTxSuccessStargate, assertIsDeliverTxSuccess as assertIsDeliverTxSuccessStargate,
calculateFee, calculateFee,
@ -24,9 +20,7 @@ import Transport from "@ledgerhq/hw-transport";
import { LedgerSigner } from "./ledgersigner"; import { LedgerSigner } from "./ledgersigner";
import { import {
faucet, faucet,
launchpad,
ledgerEnabled, ledgerEnabled,
pendingWithoutLaunchpad,
pendingWithoutLedger, pendingWithoutLedger,
pendingWithoutSimapp, pendingWithoutSimapp,
simapp, simapp,
@ -166,24 +160,6 @@ describe("LedgerSigner", () => {
interactiveTimeout, interactiveTimeout,
); );
it(
"creates signature accepted by Launchpad backend",
async () => {
pendingWithoutLedger();
pendingWithoutLaunchpad();
const signer = new LedgerSigner(transport, {
testModeAllowed: true,
hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(10)],
});
const [firstAccount] = await signer.getAccounts();
const client = new SigningCosmosClient(launchpad.endpoint, firstAccount.address, signer);
const result = await client.sendTokens(defaultLedgerAddress, coins(1234567, "ucosm"));
assertIsBroadcastTxSuccessLaunchpad(result);
},
interactiveTimeout,
);
it( it(
"creates signature accepted by Stargate backend", "creates signature accepted by Stargate backend",
async () => { async () => {

View File

@ -18,16 +18,6 @@ export function pendingWithoutLedger(): void {
} }
} }
export function launchpadEnabled(): boolean {
return !!process.env.LAUNCHPAD_ENABLED;
}
export function pendingWithoutLaunchpad(): void {
if (!launchpadEnabled()) {
return pending("Set LAUNCHPAD_ENABLED to enable Launchpad-based tests");
}
}
export function simappEnabled(): boolean { export function simappEnabled(): boolean {
return !!process.env.SIMAPP42_ENABLED || !!process.env.SIMAPP44_ENABLED; return !!process.env.SIMAPP42_ENABLED || !!process.env.SIMAPP44_ENABLED;
} }
@ -38,11 +28,6 @@ export function pendingWithoutSimapp(): void {
} }
} }
export const launchpad = {
endpoint: "http://localhost:1317",
chainId: "testing",
};
export const simapp = { export const simapp = {
endpoint: "ws://localhost:26658", endpoint: "ws://localhost:26658",
chainId: "simd-testing", chainId: "simd-testing",

View File

@ -4,7 +4,7 @@ import { Uint64 } from "@cosmjs/math";
/** /**
* Takes a coins list like "819966000ucosm,700000000ustake" and parses it. * Takes a coins list like "819966000ucosm,700000000ustake" and parses it.
* *
* This is a Stargate ready version of parseCoins from @cosmjs/amino and @cosmjs/launchpad. * This is a Stargate ready version of parseCoins from @cosmjs/amino.
* It supports more denoms. * It supports more denoms.
*/ */
export function parseCoins(input: string): Coin[] { export function parseCoins(input: string): Coin[] {

View File

@ -1,14 +1,7 @@
import { OfflineAminoSigner, StdSignature } from "@cosmjs/amino"; import { OfflineAminoSigner, StdSignature } from "@cosmjs/amino";
import { SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx"; import { SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx";
/**
* This is the same as Algo from @cosmjs/launchpad but those might diverge in the future.
*/
export type Algo = "secp256k1" | "ed25519" | "sr25519"; export type Algo = "secp256k1" | "ed25519" | "sr25519";
/**
* This is the same as AccountData from @cosmjs/launchpad but those might diverge in the future.
*/
export interface AccountData { export interface AccountData {
/** A printable address (typically bech32 encoded) */ /** A printable address (typically bech32 encoded) */
readonly address: string; readonly address: string;

View File

@ -17,8 +17,6 @@ function checkDenom(denom: string): void {
/** /**
* A gas price, i.e. the price of a single unit of gas. This is typically a fraction of * A gas price, i.e. the price of a single unit of gas. This is typically a fraction of
* the smallest fee token unit, such as 0.012utoken. * the smallest fee token unit, such as 0.012utoken.
*
* This is the same as GasPrice from @cosmjs/launchpad but those might diverge in the future.
*/ */
export class GasPrice { export class GasPrice {
public readonly amount: Decimal; public readonly amount: Decimal;

View File

@ -35,9 +35,6 @@ export class TimeoutError extends Error {
} }
} }
/**
* This is the same as BlockHeader from @cosmjs/launchpad but those might diverge in the future.
*/
export interface BlockHeader { export interface BlockHeader {
readonly version: { readonly version: {
readonly block: string; readonly block: string;
@ -49,9 +46,6 @@ export interface BlockHeader {
readonly time: string; readonly time: string;
} }
/**
* This is the same as Block from @cosmjs/launchpad but those might diverge in the future.
*/
export interface Block { export interface Block {
/** The ID is a hash of the block header (uppercase hex) */ /** The ID is a hash of the block header (uppercase hex) */
readonly id: string; readonly id: string;

View File

@ -1,152 +0,0 @@
# Local Launchpad development network with CosmWasm support
## Starting the blockchain
Run the following:
```
cd scripts/launchpad
./start.sh && ./init.sh
```
## CLI
Docker-friendly access to `wasmcli` is provided. Just use the `./cli.sh` script.
For example:
```
./cli.sh status
```
This should give you output similar to the following if your blockchain is
running:
```json
{
"node_info": {
"protocol_version": { "p2p": "7", "block": "10", "app": "0" },
"id": "223aedddd9442bcf16641858ca85837f27997d0d",
"listen_addr": "tcp://0.0.0.0:26656",
"network": "testing",
"version": "0.32.2",
"channels": "4020212223303800",
"moniker": "testing",
"other": { "tx_index": "on", "rpc_address": "tcp://127.0.0.1:26657" }
},
"sync_info": {
"latest_block_hash": "3E3BEBCFA4E47BC67C7DE44DD4E83D8D42235DE75DA942A6BECD1F0F5A6246E4",
"latest_app_hash": "73A3641BDEFBB728B1B48FB87B510F3E76E3B4519BC4954C6E1060738FCE8B14",
"latest_block_height": "1217",
"latest_block_time": "2019-09-26T15:44:13.0111312Z",
"catching_up": false
},
"validator_info": {
"address": "3A7EBE1A9E333146AE5D9FCB765B88BDD4D2859A",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "3ZYx1HKwT/llXzYC2yVeWEiWHd6uBQ7Bi7jiDFczx28="
},
"voting_power": "100"
}
}
```
## Adding the validator key to your keybase
The Cosmos test network is initialised with a validator (see
`.gaiad/config/genesis.json`). This validator has the following 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
```
To add the validator key to your local keybase run the following, choose an
encryption passphrase (e.g. `testing123`) and enter the above mnemonic when
prompted:
```
./cli.sh keys add validator --recover
```
You should get output matching the following:
```
- name: validator
type: local
address: cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6
pubkey: cosmospub1addwnpepqd8sgxq7aw348ydctp3n5ajufgxp395hksxjzc6565yfp56scupfqhlgyg5
mnemonic: ""
threshold: 0
pubkeys: []
```
## Preset accounts
1. **Faucet**<br>
economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone<br>
Address 0: cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6<br>
Address 1: cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5<br>
Address 2: cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k<br>
Address 3: cosmos142u9fgcjdlycfcez3lw8x6x5h7rfjlnfhpw2lx<br>
Address 4: cosmos1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r0dcjvx<br>
Pubkey 0: A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ<br>
Pubkey 1: AiDosfIbBi54XJ1QjCeApumcy/FjdtF+YhywPf3DKTx7<br>
Pubkey 2: AzQg33JZqH7vSsm09esZY5bZvmzYwE/SY78cA0iLxpD7<br>
Pubkey 3: A3gOAlB6aiRTCPvWMQg2+ZbGYNsLd8qlvV28m8p2UhY2<br>
Pubkey 4: Aum2063ub/ErUnIUB36sK55LktGUStgcbSiaAnL1wadu
2. **Alice**: Test account for the cosmwasm package that can run in parallel with faucet without sequence conflicts<br>
enlist hip relief stomach skate base shallow young switch frequent cry park<br>
Address 0: cosmos14qemq0vw6y3gc3u3e0aty2e764u4gs5le3hada<br>
Address 1: cosmos1hhg2rlu9jscacku2wwckws7932qqqu8x3gfgw0<br>
Address 2: cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5<br>
Address 3: cosmos17yg9mssjenmc3jkqth6ulcwj9cxujrxxzezwta<br>
Address 4: cosmos1f7j7ryulwjfe9ljplvhtcaxa6wqgula3etktce<br>
Pubkey 0: A9cXhWb8ZpqCzkA8dQCPV29KdeRLV3rUYxrkHudLbQtS<br>
Pubkey 1: A4XluzvcUx0ViLF0DjYW5/noArGwpltDstoUUZo+g1b0<br>
Pubkey 2: A5TKr1NKc/MKRJ7+EHDD9PlzmGaPD/di/6hzZyBwxoy5<br>
Pubkey 3: A/HSABDUqMB2qDy+PA7fiuuuA+hfrco2VwwiThMiTzUx<br>
Pubkey 4: A7usTiqgqfxL/WKhoephDUSCHBQlLagtwI/qTmEteTRM
3. **Bob**: Test account (unused for now)<br>
remain fragile remove stamp quiz bus country dress critic mammal office need<br>
Address 0: cosmos1lvrwcvrqlc5ktzp2c4t22xgkx29q3y83lktgzl<br>
Address 1: cosmos1vkv9sfwaak76weyamqx0flmng2vuquxqcuqukh<br>
Address 2: cosmos106jwym4s9aujcmes26myzzwqsccw09sdm0v5au<br>
Address 3: cosmos1c7wpeen2uv8thayf7g8q2rgpm29clj0dgrdtzw<br>
Address 4: cosmos1mjxpv9ft30wer7ma7kwfxhm42l379xutplrdk6<br>
Pubkey 0: A0d/GxY+UALE+miWJP0qyq4/EayG1G6tsg24v+cbD6By<br>
Pubkey 1: Agqd6njsVEQD1CR+F2aqEb8hil5NXZ06mjKgetaNC12t<br>
Pubkey 2: A6e9ElvKaM0DKWh1bIdK3bgB14dyEDgIXYMA0Lbs1GoQ<br>
Pubkey 3: AkAK5PQaucieWMb0+tTRY01feYI+upRnoNK556eD0Ibb<br>
Pubkey 4: A5HMVEAJsupdQWItbZv5Z1xZifDixQi6tjU/hJpZY1bF
4. **Unused**: for testing account state; this account never changes balances or sequences<br>
oyster design unusual machine spread century engine gravity focus cave carry slot<br>
ArkCaFUJ/IH+vKBmNRCdUVl3mCAhbopk9jjW4Ko4OfRQ<br>
cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u
5. **Guest**: account for manual testing<br>
degree tackle suggest window test behind mesh extra cover prepare oak script<br>
Am/+YV0LaeqQPu7BDJuDHV7J8y68ptkGs10YS+9s71Nq<br>
cosmos17d0jcz59jf68g52vq38tuuncmwwjk42u6mcxej
6. **Ledger**: accounts for Ledger based demos and tests<br>
example indicate trick cereal hub fix civil host kiss version bird dash<br>
Address 0: cosmos1p6xs63q4g7np99ttv5nd3yzkt8n4qxa47w8aea<br>
Address 1: cosmos1meeu3jl268txxytwmmrsljk8rawh6n2majstn2<br>
Address 2: cosmos1cak6lnpfxs035xd88sq8e4zujsm8g2g97dxu5c<br>
Address 3: cosmos1x3x8kyypx8z6q7fx3gw65x29mhl5gg8qp4ynlr<br>
Address 4: cosmos18c27m2rj4lg74md03ujralvt562c097n8zpdf9<br>
Address 5: cosmos1q2y53e6x7s5mlddtd2qkcjr3nwr4dszv6fr9rt<br>
Address 6: cosmos1paa2gstlk7c98n27dw2g6tp6fyqvf32mm67qz3<br>
Address 7: cosmos1rvxjd8k6xvssz2eerfzemvat35pttfgr67yyzd<br>
Address 8: cosmos12zejt8d9xl70jd2333p4p265m2nr9h8gsaewk0<br>
Address 9: cosmos1exctm2036jtwyc9v3ftqfzmgnv9tdhj26v87uh<br>
Address 10: cosmos1f3pws3ztnp3s4nn5zxqdrl9vlqv5avkqmlrus4<br>
Pubkey 0: A66JoCNaNSXDsyj4qW7JgqXPTz5rOnfE6EKEArf4jJEK<br>
Pubkey 1: AtvmGuZvEN3NwL05BQdxl3XygUf+Vl/930fhFMt1HTyU<br>
Pubkey 2: A58dfmfVoKoTCteEzTHBC0OLJIBgzejGDVVEb8YW9vtJ<br>
Pubkey 3: A1wA01EixwcWJkdhI69ckGuQDX0NimhLCYdrQCegkOJF<br>
Pubkey 4: A9juq+VbP26qtVh71ANlwwJQ+ABTWIyHEKYrVwjmbYE6<br>
Pubkey 5: Ar4VUqiRYl75+TF3AExX8at3deeLj2O9mNMtBq2aVpym<br>
Pubkey 6: Ak/JoSXzu6+Rp2W0wT6CqfZfzlDOwebl7xVF/zmKX99Y<br>
Pubkey 7: AtmLZZGHeCiNuroPAzBK2NKeXKT68SwioLj4I8Oj35Mn<br>
Pubkey 8: AuaUr9GEMUBKeZrJD/dv9QL/zJmMxX7OA/sjRrvBFXS2<br>
Pubkey 9: AiV5uMzvzoD7hlF+GhYuRCnf8tP+0AlPMbtfVoYv3InI<br>
Pubkey 10: A2ZnLEcbpyjS30H5UF1vezq29aBcT9oo5EARATIW9Cpj

View File

@ -1,21 +0,0 @@
#!/bin/bash
set -o errexit -o nounset -o pipefail
command -v shellcheck >/dev/null && shellcheck "$0"
SCRIPT_DIR="$(realpath "$(dirname "$0")")"
# shellcheck source=./env
# shellcheck disable=SC1091
source "$SCRIPT_DIR"/env
# TODO: make this run as UID? Does this matter?
HOME_DIR="/root"
docker run \
--rm \
-it \
--mount type=volume,source=launchpad_cli_data,target=/root/.wasmcli \
-w "$HOME_DIR" \
--env "HOME=$HOME_DIR" \
--net "container:$CONTAINER_NAME" \
"$REPOSITORY:$VERSION" \
wasmcli "$@"

View File

@ -1,5 +0,0 @@
# Choose from https://hub.docker.com/r/cosmwasm/wasmd/tags
REPOSITORY="cosmwasm/wasmd"
VERSION="v0.11.1"
CONTAINER_NAME="launchpad"

View File

@ -1,44 +0,0 @@
#!/bin/bash
set -o errexit -o nounset -o pipefail
command -v shellcheck >/dev/null && shellcheck "$0"
SCRIPT_DIR="$(realpath "$(dirname "$0")")"
# shellcheck source=./env
# shellcheck disable=SC1091
source "$SCRIPT_DIR"/env
rm -rf "$SCRIPT_DIR/template"
mkdir "$SCRIPT_DIR/template"
# The usage of the accounts below is documented in README.md of this directory
docker run --rm \
-e PASSWORD=my-secret-password \
--mount type=bind,source="$SCRIPT_DIR/template",target=/root \
"$REPOSITORY:$VERSION" \
./setup_wasmd.sh \
cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6 cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5 cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k cosmos142u9fgcjdlycfcez3lw8x6x5h7rfjlnfhpw2lx cosmos1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r0dcjvx \
cosmos14qemq0vw6y3gc3u3e0aty2e764u4gs5le3hada cosmos1hhg2rlu9jscacku2wwckws7932qqqu8x3gfgw0 cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5 cosmos17yg9mssjenmc3jkqth6ulcwj9cxujrxxzezwta cosmos1f7j7ryulwjfe9ljplvhtcaxa6wqgula3etktce \
cosmos1lvrwcvrqlc5ktzp2c4t22xgkx29q3y83lktgzl cosmos1vkv9sfwaak76weyamqx0flmng2vuquxqcuqukh cosmos106jwym4s9aujcmes26myzzwqsccw09sdm0v5au cosmos1c7wpeen2uv8thayf7g8q2rgpm29clj0dgrdtzw cosmos1mjxpv9ft30wer7ma7kwfxhm42l379xutplrdk6 \
cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u \
cosmos17d0jcz59jf68g52vq38tuuncmwwjk42u6mcxej \
cosmos1p6xs63q4g7np99ttv5nd3yzkt8n4qxa47w8aea cosmos1meeu3jl268txxytwmmrsljk8rawh6n2majstn2 cosmos1cak6lnpfxs035xd88sq8e4zujsm8g2g97dxu5c cosmos1x3x8kyypx8z6q7fx3gw65x29mhl5gg8qp4ynlr cosmos18c27m2rj4lg74md03ujralvt562c097n8zpdf9 cosmos1q2y53e6x7s5mlddtd2qkcjr3nwr4dszv6fr9rt cosmos1paa2gstlk7c98n27dw2g6tp6fyqvf32mm67qz3 cosmos1rvxjd8k6xvssz2eerfzemvat35pttfgr67yyzd cosmos12zejt8d9xl70jd2333p4p265m2nr9h8gsaewk0 cosmos1exctm2036jtwyc9v3ftqfzmgnv9tdhj26v87uh cosmos1f3pws3ztnp3s4nn5zxqdrl9vlqv5avkqmlrus4
# The ./template folder is created by the docker daemon's user (root on Linux, current user
# when using Docker Desktop on macOS), let's make it ours if needed
if [ ! -x "$SCRIPT_DIR/template/.wasmd/config/gentx" ]; then
sudo chown -R "$(id -u):$(id -g)" "$SCRIPT_DIR/template"
fi
function inline_jq() {
IN_OUT_PATH="$1"
shift
TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/inline_jq.XXXXXXXXX")
TMP_FILE="$TMP_DIR/$(basename "$IN_OUT_PATH")"
jq "$@" <"$IN_OUT_PATH" >"$TMP_FILE"
if ! mv "$TMP_FILE" "$IN_OUT_PATH"; then
echo >&2 "Temp file '$TMP_FILE' could not be deleted. If it contains sensitive data, you might want to delete it manually."
exit 3
fi
}
inline_jq "$SCRIPT_DIR/template/.wasmd/config/genesis.json" -S

View File

@ -1,16 +0,0 @@
#!/bin/bash
set -o errexit -o nounset -o pipefail
command -v shellcheck >/dev/null && shellcheck "$0"
echo "Waiting for blockchain and REST server to be available ..."
timeout 60 bash -c "until curl -s http://localhost:1317/node_info > /dev/null; do sleep 0.5; done"
# The chain is unreliable in the first second of its existence (https://gist.github.com/webmaster128/8175692d4af5e6c572fddda7a9ef437c)
sleep 1
echo "Okay, thank you for your patience."
SCRIPT_DIR="$(realpath "$(dirname "$0")")"
#
# Cosmos SDK init
#
"$SCRIPT_DIR/send_first.js"

View File

@ -1,41 +0,0 @@
#!/bin/bash
set -o errexit -o nounset -o pipefail
command -v shellcheck >/dev/null && shellcheck "$0"
## This is like start.sh but using local binaries, not docker images
SCRIPT_DIR="$(realpath "$(dirname "$0")")"
TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/gaia.XXXXXXXXX")
chmod 777 "$TMP_DIR"
echo "Using temporary dir $TMP_DIR"
WASMD_LOGFILE="$TMP_DIR/wasmd.log"
REST_SERVER_LOGFILE="$TMP_DIR/rest-server.log"
# move the template into our temporary home
cp -r "$SCRIPT_DIR"/template/.wasm* "$TMP_DIR"
wasmd start \
--home "$TMP_DIR/.wasmd" \
--trace \
--rpc.laddr tcp://0.0.0.0:26657 \
>"$WASMD_LOGFILE" &
echo "wasmd running and logging into $WASMD_LOGFILE"
sleep 10
cat "$WASMD_LOGFILE"
wasmcli rest-server \
--home "$TMP_DIR/.wasmcli" \
--node tcp://localhost:26657 \
--trust-node \
--laddr tcp://0.0.0.0:1317 \
>"$REST_SERVER_LOGFILE" &
echo "rest server running on http://localhost:1317 and logging into $REST_SERVER_LOGFILE"
# Debug rest server start
sleep 3
cat "$REST_SERVER_LOGFILE"
tail -f "$WASMD_LOGFILE"

View File

@ -1,32 +0,0 @@
#!/usr/bin/env -S yarn node
/* eslint-disable @typescript-eslint/naming-convention */
const { coins, Secp256k1HdWallet } = require("@cosmjs/amino");
const { Random } = require("@cosmjs/crypto");
const { Bech32 } = require("@cosmjs/encoding");
const { SigningCosmosClient, assertIsBroadcastTxSuccess } = require("@cosmjs/launchpad");
const httpUrl = "http://localhost:1317";
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: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
};
async function main() {
const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmosClient(httpUrl, faucet.address0, wallet);
const recipient = Bech32.encode("cosmos", Random.getBytes(20));
const amount = coins(226644, "ucosm");
const memo = "Ensure chain has my pubkey";
const sendResult = await client.sendTokens(recipient, amount, memo);
assertIsBroadcastTxSuccess(sendResult);
}
main().then(
() => process.exit(0),
(error) => {
console.error(error);
process.exit(1);
},
);

View File

@ -1,67 +0,0 @@
#!/bin/bash
set -o errexit -o nounset -o pipefail
command -v shellcheck >/dev/null && shellcheck "$0"
# Please keep this in sync with the Ports overview in HACKING.md
# Tendermint port (26657) and p2p port (26656) are not exposed since we don't need them for testing
LCD_API_PORT_GUEST="1317"
LCD_API_PORT_HOST="1317"
SCRIPT_DIR="$(realpath "$(dirname "$0")")"
# shellcheck source=./env
# shellcheck disable=SC1091
source "$SCRIPT_DIR"/env
TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/wasmd.XXXXXXXXX")
chmod 777 "$TMP_DIR"
echo "Using temporary dir $TMP_DIR"
WASMD_LOGFILE="$TMP_DIR/wasmd.log"
REST_SERVER_LOGFILE="$TMP_DIR/rest-server.log"
# Use a fresh volume for every start
docker volume rm -f launchpad_data
# This starts up wasmd
docker run --rm \
--name "$CONTAINER_NAME" \
-p "$LCD_API_PORT_HOST":"$LCD_API_PORT_GUEST" \
--mount type=bind,source="$SCRIPT_DIR/template",target=/template \
--mount type=volume,source=launchpad_data,target=/root \
"$REPOSITORY:$VERSION" \
./run_wasmd.sh /template \
>"$WASMD_LOGFILE" &
echo "wasmd running and logging into $WASMD_LOGFILE"
# Debug chain start
# sleep 3 && cat "$WASMD_LOGFILE"
# Use a large timeout because of potentially long image download in `docker run`
if ! timeout 180 bash -c "until [ \"\$( docker container inspect -f '{{.State.Status}}' \"$CONTAINER_NAME\" 2> /dev/null )\" = \"running\" ]; do sleep 0.5; done"; then
echo "Container named '$CONTAINER_NAME' not running. We cannot continue." \
"This can happen when 'docker run' needs too long to download and start." \
"It might be worth retrying this step once the image is in the local docker cache."
docker kill "$CONTAINER_NAME"
exit 1
fi
docker exec "$CONTAINER_NAME" \
wasmcli rest-server \
--node tcp://localhost:26657 \
--trust-node \
--unsafe-cors \
--laddr "tcp://0.0.0.0:$LCD_API_PORT_GUEST" \
>"$REST_SERVER_LOGFILE" &
echo "rest server running on http://localhost:$LCD_API_PORT_HOST and logging into $REST_SERVER_LOGFILE"
if [ -n "${CI:-}" ]; then
# Give process some time to come alive. No idea why this helps. Needed for CI.
sleep 0.5
# Follow the logs in CI's background job
tail -f "$WASMD_LOGFILE"
fi
# Debug rest server start
# sleep 3 && cat "$REST_SERVER_LOGFILE"

View File

@ -1,11 +0,0 @@
#!/bin/bash
set -o errexit -o nounset -o pipefail
command -v shellcheck >/dev/null && shellcheck "$0"
SCRIPT_DIR="$(realpath "$(dirname "$0")")"
# shellcheck source=./env
# shellcheck disable=SC1091
source "$SCRIPT_DIR"/env
echo "Killing Cosmos container..."
docker container kill "$CONTAINER_NAME"

View File

@ -1 +0,0 @@
eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMC0xMC0wOCAxMDoxODoxMS41Nzk5MTc2ICswMDAwIFVUQyBtPSswLjIwODQ1NTkwMSIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjgxOTIsInAycyI6IlBtZXB4NExXMlk4bFM2cXEifQ.NgxU8Oa0GROusbronyRkzPRHIngrFnnfNKxCVCYtsQX6rhKU8eQrig.SXfPznlVJlsFGvPi.jSjG-b9uurXrTRdXHbCi14O_XwOV-ob6cRbmWNwIcSxoib2X-zByj20XRyHzJbpJ2S7CBq-Xop0tiY6x3AIgrUSlpMdUYxEr1pHsB1pVtWVJ9QNym3KN4DFL0zDrIXKQGZZLSZddfTTe9HP7iVqApqA2uK0DSMVEAUYLe_GWqpgaIkbrZYO1YUhCaVWgZkXO7d9OLknmWtIrhE5SpC0evSkbsWlfbPulsYhEflxQA5d77-dogshyys0F4h-L410.GudaoqjS4Tzj8E4JwTXM9Q

View File

@ -1 +0,0 @@
$2a$10$YzaMyzpLWAscnjy9KQ7ujORZSelnaBAVF9WpTjrG/7L2ylc9iaEjC

View File

@ -1 +0,0 @@
eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMC0xMC0wOCAxMDoxODoxMS41NTkzMjQgKzAwMDAgVVRDIG09KzAuMTg3ODYwODAxIiwiZW5jIjoiQTI1NkdDTSIsInAyYyI6ODE5MiwicDJzIjoiV19XUWlpakllT2VNakNGSiJ9.yszrRkA7Z1aAjKwPPyQebxsMEB57B_OXryuMuZnR2cIeAfchu5x4Rg.W4qQ-Kc7TkvjH7Gr.yPYQV6Wlb-M_spbO5o2CTYMwdJUxWkUpEEZ9a72zoR3AzH3AKdv34vPHDB5bYbNXdt-f0qPWnpr2B-UsoBXSIbpY4ZjD68ry2Ct6X9cMZMN1XOCXqpN4qBG7W8Il_qcIKVMt_S9Ct1utMwhnht7uvItW3AQmp6CIj35f4mk8R3U5u3pSczI8gnuiAhrVWgjWghznsVLG2fArOs1akGIMJaQm7FaldU7gsvW_vOwD8h9zVnAGypmI37J1XDJWZVhNQAupOdaIWtzJkJSW8cEAjTIF9GhA_ysLk11nw5jXc8NR6r9Bqk3sfDph0_NFC5uSmAUCqMuGePdjmgPaQWBlmRkOzG4AXB6_PI-D83ZtRZ215PPe.UyvabgVuUaaHQWcHigiK2g

View File

@ -1,36 +0,0 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base config options #####
# The minimum gas prices a validator is willing to accept for processing a
# transaction. A transaction's fees must meet the minimum of any denomination
# specified in this config (e.g. 0.25token1;0.0001token2).
minimum-gas-prices = ""
# default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals
# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
# everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals
# custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval'
pruning = "default"
# These are applied if and only if the pruning strategy is custom.
pruning-keep-recent = "0"
pruning-keep-every = "0"
pruning-interval = "0"
# HaltHeight contains a non-zero block height at which a node will gracefully
# halt and shutdown that can be used to assist upgrades and testing.
#
# Note: Commitment of state will be attempted on the corresponding block.
halt-height = 0
# HaltTime contains a non-zero minimum block time (in Unix seconds) at which
# a node will gracefully halt and shutdown that can be used to assist upgrades
# and testing.
#
# Note: Commitment of state will be attempted on the corresponding block.
halt-time = 0
# InterBlockCache enables inter-block caching.
inter-block-cache = true

View File

@ -1,335 +0,0 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
# NOTE: Any path below can be absolute (e.g. "/var/myawesomeapp/data") or
# relative to the home directory (e.g. "data"). The home directory is
# "$HOME/.tendermint" by default, but could be changed via $TMHOME env variable
# or --home cmd flag.
##### main base config options #####
# TCP or UNIX socket address of the ABCI application,
# or the name of an ABCI application compiled in with the Tendermint binary
proxy_app = "tcp://127.0.0.1:26658"
# A custom human readable name for this node
moniker = "node001"
# If this node is many blocks behind the tip of the chain, FastSync
# allows them to catchup quickly by downloading blocks in parallel
# and verifying their commits
fast_sync = true
# Database backend: goleveldb | cleveldb | boltdb | rocksdb
# * goleveldb (github.com/syndtr/goleveldb - most popular implementation)
# - pure go
# - stable
# * cleveldb (uses levigo wrapper)
# - fast
# - requires gcc
# - use cleveldb build tag (go build -tags cleveldb)
# * boltdb (uses etcd's fork of bolt - github.com/etcd-io/bbolt)
# - EXPERIMENTAL
# - may be faster is some use-cases (random reads - indexer)
# - use boltdb build tag (go build -tags boltdb)
# * rocksdb (uses github.com/tecbot/gorocksdb)
# - EXPERIMENTAL
# - requires gcc
# - use rocksdb build tag (go build -tags rocksdb)
db_backend = "goleveldb"
# Database directory
db_dir = "data"
# Output level for logging, including package level options
log_level = "main:info,state:info,*:error"
# Output format: 'plain' (colored text) or 'json'
log_format = "plain"
##### additional base config options #####
# Path to the JSON file containing the initial validator set and other meta data
genesis_file = "config/genesis.json"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
priv_validator_key_file = "config/priv_validator_key.json"
# Path to the JSON file containing the last sign state of a validator
priv_validator_state_file = "data/priv_validator_state.json"
# TCP or UNIX socket address for Tendermint to listen on for
# connections from an external PrivValidator process
priv_validator_laddr = ""
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
node_key_file = "config/node_key.json"
# Mechanism to connect to the ABCI application: socket | grpc
abci = "socket"
# TCP or UNIX socket address for the profiling server to listen on
prof_laddr = "localhost:6060"
# If true, query the ABCI app on connecting to a new peer
# so the app can decide if we should keep the connection or not
filter_peers = false
##### advanced configuration options #####
##### rpc server configuration options #####
[rpc]
# TCP or UNIX socket address for the RPC server to listen on
laddr = "tcp://127.0.0.1:26657"
# A list of origins a cross-domain request can be executed from
# Default value '[]' disables cors support
# Use '["*"]' to allow any origin
cors_allowed_origins = []
# A list of methods the client is allowed to use with cross-domain requests
cors_allowed_methods = ["HEAD", "GET", "POST", ]
# A list of non simple headers the client is allowed to use with cross-domain requests
cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time", ]
# TCP or UNIX socket address for the gRPC server to listen on
# NOTE: This server only supports /broadcast_tx_commit
grpc_laddr = ""
# Maximum number of simultaneous connections.
# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
# If you want to accept a larger number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
# 1024 - 40 - 10 - 50 = 924 = ~900
grpc_max_open_connections = 900
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
unsafe = false
# Maximum number of simultaneous connections (including WebSocket).
# Does not include gRPC connections. See grpc_max_open_connections
# If you want to accept a larger number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
# 1024 - 40 - 10 - 50 = 924 = ~900
max_open_connections = 900
# Maximum number of unique clientIDs that can /subscribe
# If you're using /broadcast_tx_commit, set to the estimated maximum number
# of broadcast_tx_commit calls per block.
max_subscription_clients = 100
# Maximum number of unique queries a given client can /subscribe to
# If you're using GRPC (or Local RPC client) and /broadcast_tx_commit, set to
# the estimated # maximum number of broadcast_tx_commit calls per block.
max_subscriptions_per_client = 5
# How long to wait for a tx to be committed during /broadcast_tx_commit.
# WARNING: Using a value larger than 10s will result in increasing the
# global HTTP write timeout, which applies to all connections and endpoints.
# See https://github.com/tendermint/tendermint/issues/3435
timeout_broadcast_tx_commit = "10s"
# Maximum size of request body, in bytes
max_body_bytes = 1000000
# Maximum size of request header, in bytes
max_header_bytes = 1048576
# The path to a file containing certificate that is used to create the HTTPS server.
# Migth be either absolute path or path related to tendermint's config directory.
# If the certificate is signed by a certificate authority,
# the certFile should be the concatenation of the server's certificate, any intermediates,
# and the CA's certificate.
# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server.
# Otherwise, HTTP server is run.
tls_cert_file = ""
# The path to a file containing matching private key that is used to create the HTTPS server.
# Migth be either absolute path or path related to tendermint's config directory.
# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server.
# Otherwise, HTTP server is run.
tls_key_file = ""
##### peer to peer configuration options #####
[p2p]
# Address to listen for incoming connections
laddr = "tcp://0.0.0.0:26656"
# Address to advertise to peers for them to dial
# If empty, will use the same port as the laddr,
# and will introspect on the listener or use UPnP
# to figure out the address.
external_address = ""
# Comma separated list of seed nodes to connect to
seeds = ""
# Comma separated list of nodes to keep persistent connections to
persistent_peers = ""
# UPNP port forwarding
upnp = false
# Path to address book
addr_book_file = "config/addrbook.json"
# Set true for strict address routability rules
# Set false for private or local networks
addr_book_strict = true
# Maximum number of inbound peers
max_num_inbound_peers = 40
# Maximum number of outbound peers to connect to, excluding persistent peers
max_num_outbound_peers = 10
# List of node IDs, to which a connection will be (re)established ignoring any existing limits
unconditional_peer_ids = ""
# Maximum pause when redialing a persistent peer (if zero, exponential backoff is used)
persistent_peers_max_dial_period = "0s"
# Time to wait before flushing messages out on the connection
flush_throttle_timeout = "100ms"
# Maximum size of a message packet payload, in bytes
max_packet_msg_payload_size = 1024
# Rate at which packets can be sent, in bytes/second
send_rate = 5120000
# Rate at which packets can be received, in bytes/second
recv_rate = 5120000
# Set true to enable the peer-exchange reactor
pex = true
# Seed mode, in which node constantly crawls the network and looks for
# peers. If another node asks it for addresses, it responds and disconnects.
#
# Does not work if the peer-exchange reactor is disabled.
seed_mode = false
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = ""
# Toggle to disable guard against peers connecting from the same ip.
allow_duplicate_ip = false
# Peer connection configuration.
handshake_timeout = "20s"
dial_timeout = "3s"
##### mempool configuration options #####
[mempool]
recheck = true
broadcast = true
wal_dir = ""
# Maximum number of transactions in the mempool
size = 5000
# Limit the total size of all txs in the mempool.
# This only accounts for raw transactions (e.g. given 1MB transactions and
# max_txs_bytes=5MB, mempool will only accept 5 transactions).
max_txs_bytes = 1073741824
# Size of the cache (used to filter transactions we saw earlier) in transactions
cache_size = 10000
# Maximum size of a single transaction.
# NOTE: the max size of a tx transmitted over the network is {max_tx_bytes} + {amino overhead}.
max_tx_bytes = 1048576
##### fast sync configuration options #####
[fastsync]
# Fast Sync version to use:
# 1) "v0" (default) - the legacy fast sync implementation
# 2) "v1" - refactor of v0 version for better testability
# 3) "v2" - refactor of v1 version for better usability
version = "v0"
##### consensus configuration options #####
[consensus]
wal_file = "data/cs.wal/wal"
timeout_propose = "300ms"
timeout_propose_delta = "100ms"
timeout_prevote = "300ms"
timeout_prevote_delta = "100ms"
timeout_precommit = "300ms"
timeout_precommit_delta = "100ms"
timeout_commit = "1s"
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
skip_timeout_commit = false
# EmptyBlocks mode and possible interval between empty blocks
create_empty_blocks = true
create_empty_blocks_interval = "0s"
# Reactor sleep duration parameters
peer_gossip_sleep_duration = "100ms"
peer_query_maj23_sleep_duration = "2s"
##### transactions indexer configuration options #####
[tx_index]
# What indexer to use for transactions
#
# Options:
# 1) "null"
# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
indexer = "kv"
# Comma-separated list of compositeKeys to index (by default the only key is "tx.hash")
# Remember that Event has the following structure: type.key
# type: [
# key: value,
# ...
# ]
#
# You can also index transactions by height by adding "tx.height" key here.
#
# It's recommended to index only a subset of keys due to possible memory
# bloat. This is, of course, depends on the indexer's DB and the volume of
# transactions.
index_keys = ""
# When set to true, tells indexer to index all compositeKeys (predefined keys:
# "tx.hash", "tx.height" and all keys from DeliverTx responses).
#
# Note this may be not desirable (see the comment above). IndexKeys has a
# precedence over IndexAllKeys (i.e. when given both, IndexKeys will be
# indexed).
index_all_keys = true
##### instrumentation configuration options #####
[instrumentation]
# When true, Prometheus metrics are served under /metrics on
# PrometheusListenAddr.
# Check out the documentation for the list of available metrics.
prometheus = false
# Address to listen for Prometheus collector(s) connections
prometheus_listen_addr = ":26660"
# Maximum number of simultaneous connections.
# If you want to accept a larger number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
max_open_connections = 3
# Instrumentation namespace
namespace = "tendermint"

View File

@ -1,746 +0,0 @@
{
"app_hash": "",
"app_state": {
"auth": {
"accounts": [
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r5fhf0s",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos142u9fgcjdlycfcez3lw8x6x5h7rfjlnfhpw2lx",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1hsm76p4ahyhl5yh3ve9ur49r5kemhp2r0dcjvx",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos14qemq0vw6y3gc3u3e0aty2e764u4gs5le3hada",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1hhg2rlu9jscacku2wwckws7932qqqu8x3gfgw0",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos17yg9mssjenmc3jkqth6ulcwj9cxujrxxzezwta",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1f7j7ryulwjfe9ljplvhtcaxa6wqgula3etktce",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1lvrwcvrqlc5ktzp2c4t22xgkx29q3y83lktgzl",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1vkv9sfwaak76weyamqx0flmng2vuquxqcuqukh",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos106jwym4s9aujcmes26myzzwqsccw09sdm0v5au",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1c7wpeen2uv8thayf7g8q2rgpm29clj0dgrdtzw",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1mjxpv9ft30wer7ma7kwfxhm42l379xutplrdk6",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos17d0jcz59jf68g52vq38tuuncmwwjk42u6mcxej",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1p6xs63q4g7np99ttv5nd3yzkt8n4qxa47w8aea",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1meeu3jl268txxytwmmrsljk8rawh6n2majstn2",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1cak6lnpfxs035xd88sq8e4zujsm8g2g97dxu5c",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1x3x8kyypx8z6q7fx3gw65x29mhl5gg8qp4ynlr",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos18c27m2rj4lg74md03ujralvt562c097n8zpdf9",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1q2y53e6x7s5mlddtd2qkcjr3nwr4dszv6fr9rt",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1paa2gstlk7c98n27dw2g6tp6fyqvf32mm67qz3",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1rvxjd8k6xvssz2eerfzemvat35pttfgr67yyzd",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos12zejt8d9xl70jd2333p4p265m2nr9h8gsaewk0",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1exctm2036jtwyc9v3ftqfzmgnv9tdhj26v87uh",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
},
{
"type": "cosmos-sdk/Account",
"value": {
"account_number": "0",
"address": "cosmos1f3pws3ztnp3s4nn5zxqdrl9vlqv5avkqmlrus4",
"coins": [
{
"amount": "1000000000",
"denom": "ucosm"
},
{
"amount": "1000000000",
"denom": "ustake"
}
],
"public_key": null,
"sequence": "0"
}
}
],
"params": {
"max_memo_characters": "256",
"sig_verify_cost_ed25519": "590",
"sig_verify_cost_secp256k1": "1000",
"tx_sig_limit": "7",
"tx_size_cost_per_byte": "10"
}
},
"bank": {
"send_enabled": true
},
"crisis": {
"constant_fee": {
"amount": "1000",
"denom": "ustake"
}
},
"distribution": {
"delegator_starting_infos": [],
"delegator_withdraw_infos": [],
"fee_pool": {
"community_pool": []
},
"outstanding_rewards": [],
"params": {
"base_proposer_reward": "0.010000000000000000",
"bonus_proposer_reward": "0.040000000000000000",
"community_tax": "0.020000000000000000",
"withdraw_addr_enabled": true
},
"previous_proposer": "",
"validator_accumulated_commissions": [],
"validator_current_rewards": [],
"validator_historical_rewards": [],
"validator_slash_events": []
},
"evidence": {
"evidence": [],
"params": {
"max_evidence_age": "120000000000"
}
},
"genutil": {
"gentxs": [
{
"type": "cosmos-sdk/StdTx",
"value": {
"fee": {
"amount": [],
"gas": "200000"
},
"memo": "aaf7eb061829f888de02b377eeb64009317c330d@172.17.0.2:26656",
"msg": [
{
"type": "cosmos-sdk/MsgCreateValidator",
"value": {
"commission": {
"max_change_rate": "0.010000000000000000",
"max_rate": "0.200000000000000000",
"rate": "0.100000000000000000"
},
"delegator_address": "cosmos1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r5fhf0s",
"description": {
"details": "",
"identity": "",
"moniker": "node001",
"security_contact": "",
"website": ""
},
"min_self_delegation": "1",
"pubkey": "cosmosvalconspub1zcjduepqf62c9h86qqn4g9s4khcng86quanw8rn5mm6lf69c99vxff0302ksv2ljyl",
"validator_address": "cosmosvaloper1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r3arurr",
"value": {
"amount": "250000000",
"denom": "ustake"
}
}
}
],
"signatures": [
{
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "Atm7XWIgFRE+ONf4mOD15/1f8hst97O8efn8b6o2nP6Q"
},
"signature": "nc7CR9iYk1QrQX/gRkBIXh2T2/7Os0IxegsFsVuZBL1pDO4KdzjyokuS2pNdxCiL3+v9pJqVsEJ2LVhSqwGqQQ=="
}
]
}
}
]
},
"gov": {
"deposit_params": {
"max_deposit_period": "172800000000000",
"min_deposit": [
{
"amount": "10000000",
"denom": "ustake"
}
]
},
"deposits": null,
"proposals": null,
"starting_proposal_id": "1",
"tally_params": {
"quorum": "0.334000000000000000",
"threshold": "0.500000000000000000",
"veto": "0.334000000000000000"
},
"votes": null,
"voting_params": {
"voting_period": "172800000000000"
}
},
"mint": {
"minter": {
"annual_provisions": "0.000000000000000000",
"inflation": "0.130000000000000000"
},
"params": {
"blocks_per_year": "6311520",
"goal_bonded": "0.670000000000000000",
"inflation_max": "0.200000000000000000",
"inflation_min": "0.070000000000000000",
"inflation_rate_change": "0.130000000000000000",
"mint_denom": "ustake"
}
},
"params": null,
"slashing": {
"missed_blocks": {},
"params": {
"downtime_jail_duration": "600000000000",
"min_signed_per_window": "0.500000000000000000",
"signed_blocks_window": "100",
"slash_fraction_double_sign": "0.050000000000000000",
"slash_fraction_downtime": "0.010000000000000000"
},
"signing_infos": {}
},
"staking": {
"delegations": null,
"exported": false,
"last_total_power": "0",
"last_validator_powers": null,
"params": {
"bond_denom": "ustake",
"historical_entries": 0,
"max_entries": 7,
"max_validators": 100,
"unbonding_time": "1814400000000000"
},
"redelegations": null,
"unbonding_delegations": null,
"validators": null
},
"supply": {
"supply": []
},
"upgrade": {},
"wasm": {
"params": {
"code_upload_access": {
"permission": "Everybody"
},
"instantiate_default_permission": "Everybody"
}
}
},
"chain_id": "testing",
"consensus_params": {
"block": {
"max_bytes": "22020096",
"max_gas": "-1",
"time_iota_ms": "1000"
},
"evidence": {
"max_age_duration": "172800000000000",
"max_age_num_blocks": "100000"
},
"validator": {
"pub_key_types": [
"ed25519"
]
}
},
"genesis_time": "2020-10-08T10:18:11.2275025Z"
}

Some files were not shown because too many files have changed in this diff Show More