mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-10 21:49:15 +00:00
Remove laucnhpad
Applies diff from https://github.com/cosmos/cosmjs/pull/1015
This commit is contained in:
parent
1cfda9dccf
commit
9970f77f18
@ -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
211
.pnp.cjs
generated
@ -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", [
|
||||||
|
@ -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
|
||||||
|
@ -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) | [](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) | [](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) | [](https://www.npmjs.com/package/@cosmjs/launchpad) |
|
|
||||||
| [@cosmjs/faucet](packages/faucet) | A faucet application for node.js | [](https://www.npmjs.com/package/@cosmjs/faucet) |
|
| [@cosmjs/faucet](packages/faucet) | A faucet application for node.js | [](https://www.npmjs.com/package/@cosmjs/faucet) |
|
||||||
| [@cosmjs/cosmwasm-stargate](packages/cosmwasm-stargate) | Client for Stargate chains with the CosmWasm module enabled | [](https://www.npmjs.com/package/@cosmjs/cosmwasm-stargate) |
|
| [@cosmjs/cosmwasm-stargate](packages/cosmwasm-stargate) | Client for Stargate chains with the CosmWasm module enabled | [](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) | [](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) | [](https://www.npmjs.com/package/@cosmjs/crypto) |
|
||||||
|
@ -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",
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
[](https://www.npmjs.com/package/@cosmjs/amino)
|
[](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
|
||||||
|
|
||||||
|
@ -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>"
|
||||||
],
|
],
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -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",
|
||||||
|
@ -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)"
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Coin } from "@cosmjs/launchpad";
|
import { Coin } from "@cosmjs/stargate";
|
||||||
|
|
||||||
import { MinimalAccount, SendJob } from "./types";
|
import { MinimalAccount, SendJob } from "./types";
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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";
|
||||||
|
|
||||||
|
@ -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[];
|
||||||
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
../../.eslintignore
|
|
@ -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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
3
packages/launchpad/.gitignore
vendored
3
packages/launchpad/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
build/
|
|
||||||
dist/
|
|
||||||
docs/
|
|
@ -1 +0,0 @@
|
|||||||
../../.nycrc.yml
|
|
@ -1,521 +0,0 @@
|
|||||||
# @cosmjs/launchpad
|
|
||||||
|
|
||||||
[](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)).
|
|
@ -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();
|
|
@ -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,
|
|
||||||
});
|
|
||||||
};
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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}$/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
@ -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";
|
|
@ -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: "" }),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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.37–0.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;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -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: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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) },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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`),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -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",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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}`),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -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";
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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`),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -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",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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`);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -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",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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`),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -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]+$/),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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}`);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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.37–0.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;
|
|
||||||
}
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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;
|
|
||||||
}
|
|
@ -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
|
|
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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;
|
|
||||||
}
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
44
packages/launchpad/src/testdata/cosmoshub.json
vendored
44
packages/launchpad/src/testdata/cosmoshub.json
vendored
@ -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"
|
|
||||||
}
|
|
57
packages/launchpad/src/testdata/txresponse1.json
vendored
57
packages/launchpad/src/testdata/txresponse1.json
vendored
@ -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"
|
|
||||||
}
|
|
57
packages/launchpad/src/testdata/txresponse2.json
vendored
57
packages/launchpad/src/testdata/txresponse2.json
vendored
@ -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"
|
|
||||||
}
|
|
57
packages/launchpad/src/testdata/txresponse3.json
vendored
57
packages/launchpad/src/testdata/txresponse3.json
vendored
@ -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"
|
|
||||||
}
|
|
@ -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];
|
|
||||||
}
|
|
@ -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;
|
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": ".",
|
|
||||||
"outDir": "build",
|
|
||||||
"rootDir": "src"
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src/**/*"
|
|
||||||
]
|
|
||||||
}
|
|
@ -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,
|
|
||||||
};
|
|
@ -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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
@ -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",
|
||||||
|
@ -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 () => {
|
||||||
|
@ -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",
|
||||||
|
@ -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[] {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
|
@ -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 "$@"
|
|
@ -1,5 +0,0 @@
|
|||||||
# Choose from https://hub.docker.com/r/cosmwasm/wasmd/tags
|
|
||||||
REPOSITORY="cosmwasm/wasmd"
|
|
||||||
VERSION="v0.11.1"
|
|
||||||
|
|
||||||
CONTAINER_NAME="launchpad"
|
|
@ -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
|
|
@ -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"
|
|
@ -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"
|
|
@ -1 +0,0 @@
|
|||||||
{}
|
|
@ -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);
|
|
||||||
},
|
|
||||||
);
|
|
@ -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"
|
|
@ -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"
|
|
@ -1 +0,0 @@
|
|||||||
eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMC0xMC0wOCAxMDoxODoxMS41Nzk5MTc2ICswMDAwIFVUQyBtPSswLjIwODQ1NTkwMSIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjgxOTIsInAycyI6IlBtZXB4NExXMlk4bFM2cXEifQ.NgxU8Oa0GROusbronyRkzPRHIngrFnnfNKxCVCYtsQX6rhKU8eQrig.SXfPznlVJlsFGvPi.jSjG-b9uurXrTRdXHbCi14O_XwOV-ob6cRbmWNwIcSxoib2X-zByj20XRyHzJbpJ2S7CBq-Xop0tiY6x3AIgrUSlpMdUYxEr1pHsB1pVtWVJ9QNym3KN4DFL0zDrIXKQGZZLSZddfTTe9HP7iVqApqA2uK0DSMVEAUYLe_GWqpgaIkbrZYO1YUhCaVWgZkXO7d9OLknmWtIrhE5SpC0evSkbsWlfbPulsYhEflxQA5d77-dogshyys0F4h-L410.GudaoqjS4Tzj8E4JwTXM9Q
|
|
@ -1 +0,0 @@
|
|||||||
$2a$10$YzaMyzpLWAscnjy9KQ7ujORZSelnaBAVF9WpTjrG/7L2ylc9iaEjC
|
|
@ -1 +0,0 @@
|
|||||||
eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjcmVhdGVkIjoiMjAyMC0xMC0wOCAxMDoxODoxMS41NTkzMjQgKzAwMDAgVVRDIG09KzAuMTg3ODYwODAxIiwiZW5jIjoiQTI1NkdDTSIsInAyYyI6ODE5MiwicDJzIjoiV19XUWlpakllT2VNakNGSiJ9.yszrRkA7Z1aAjKwPPyQebxsMEB57B_OXryuMuZnR2cIeAfchu5x4Rg.W4qQ-Kc7TkvjH7Gr.yPYQV6Wlb-M_spbO5o2CTYMwdJUxWkUpEEZ9a72zoR3AzH3AKdv34vPHDB5bYbNXdt-f0qPWnpr2B-UsoBXSIbpY4ZjD68ry2Ct6X9cMZMN1XOCXqpN4qBG7W8Il_qcIKVMt_S9Ct1utMwhnht7uvItW3AQmp6CIj35f4mk8R3U5u3pSczI8gnuiAhrVWgjWghznsVLG2fArOs1akGIMJaQm7FaldU7gsvW_vOwD8h9zVnAGypmI37J1XDJWZVhNQAupOdaIWtzJkJSW8cEAjTIF9GhA_ysLk11nw5jXc8NR6r9Bqk3sfDph0_NFC5uSmAUCqMuGePdjmgPaQWBlmRkOzG4AXB6_PI-D83ZtRZ215PPe.UyvabgVuUaaHQWcHigiK2g
|
|
@ -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
|
|
@ -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"
|
|
@ -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
Loading…
x
Reference in New Issue
Block a user