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
|
||||
sudo dpkg -i "$HOME/git-lfs.deb"
|
||||
- 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
|
||||
name: Start wasmd
|
||||
command: ./scripts/wasmd/start.sh
|
||||
@ -142,9 +138,6 @@ jobs:
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --immutable --immutable-cache --check-cache
|
||||
- run:
|
||||
name: Initialize launchpad (deploy contracts and friends)
|
||||
command: ./scripts/launchpad/init.sh
|
||||
- run:
|
||||
name: Initialize wasmd (deploy contracts and friends)
|
||||
command: ./scripts/wasmd/init.sh
|
||||
@ -154,7 +147,6 @@ jobs:
|
||||
- run:
|
||||
name: Run tests
|
||||
environment:
|
||||
LAUNCHPAD_ENABLED: 1
|
||||
TENDERMINT_ENABLED: 1
|
||||
SOCKETSERVER_ENABLED: 1
|
||||
SKIP_BUILD: 1
|
||||
@ -173,14 +165,14 @@ jobs:
|
||||
name: Run CLI examples
|
||||
working_directory: packages/cli
|
||||
environment:
|
||||
TENDERMINT_ENABLED: 1
|
||||
SOCKETSERVER_ENABLED: 1
|
||||
SKIP_BUILD: 1
|
||||
WASMD_ENABLED: 1
|
||||
command: |
|
||||
yarn node ./bin/cosmwasm-cli --init examples/cosmwasm.ts --code "process.exit(0)"
|
||||
yarn node ./bin/cosmwasm-cli --init examples/delegate.ts --code "process.exit(0)"
|
||||
yarn node ./bin/cosmwasm-cli --init examples/faucet_addresses.ts --code "process.exit(0)"
|
||||
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)"
|
||||
[ "<< parameters.simapp >>" = "simapp42" ] && export SIMAPP42_ENABLED=1 SLOW_SIMAPP42_ENABLED=1
|
||||
[ "<< parameters.simapp >>" = "simapp44" ] && export SIMAPP44_ENABLED=1 SLOW_SIMAPP44_ENABLED=1
|
||||
./run_examples.sh
|
||||
- run:
|
||||
name: Stop chains
|
||||
command: |
|
||||
@ -189,7 +181,6 @@ jobs:
|
||||
./scripts/<< parameters.simapp >>/stop.sh
|
||||
./scripts/<< parameters.simapp >>/slow_stop.sh
|
||||
./scripts/wasmd/stop.sh
|
||||
./scripts/launchpad/stop.sh
|
||||
test-node:
|
||||
parameters:
|
||||
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
|
||||
sudo dpkg -i "$HOME/git-lfs.deb"
|
||||
- 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
|
||||
name: Start wasmd
|
||||
command: ./scripts/wasmd/start.sh
|
||||
@ -271,9 +258,6 @@ jobs:
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --immutable --immutable-cache --check-cache
|
||||
- run:
|
||||
name: Initialize launchpad (deploy contracts and friends)
|
||||
command: ./scripts/launchpad/init.sh
|
||||
- run:
|
||||
name: Initialize wasmd (deploy contracts and friends)
|
||||
command: ./scripts/wasmd/init.sh
|
||||
@ -282,7 +266,6 @@ jobs:
|
||||
command: ./scripts/socketserver/start.sh
|
||||
- run:
|
||||
environment:
|
||||
LAUNCHPAD_ENABLED: 1
|
||||
SIMAPP42_ENABLED: 1
|
||||
SLOW_SIMAPP42_ENABLED: 1
|
||||
TENDERMINT_ENABLED: 1
|
||||
@ -301,7 +284,6 @@ jobs:
|
||||
name: Run CLI examples
|
||||
working_directory: packages/cli
|
||||
environment:
|
||||
LAUNCHPAD_ENABLED: 1
|
||||
SIMAPP42_ENABLED: 1
|
||||
SLOW_SIMAPP42_ENABLED: 1
|
||||
TENDERMINT_ENABLED: 1
|
||||
@ -317,7 +299,6 @@ jobs:
|
||||
./scripts/simapp42/stop.sh
|
||||
./scripts/simapp42/slow_stop.sh
|
||||
./scripts/wasmd/stop.sh
|
||||
./scripts/launchpad/stop.sh
|
||||
test-chrome:
|
||||
machine:
|
||||
# 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
|
||||
sudo dpkg -i "$HOME/git-lfs.deb"
|
||||
- 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
|
||||
name: Start wasmd
|
||||
command: ./scripts/wasmd/start.sh
|
||||
@ -392,9 +369,6 @@ jobs:
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --immutable --immutable-cache --check-cache
|
||||
- run:
|
||||
name: Initialize launchpad (deploy contracts and friends)
|
||||
command: ./scripts/launchpad/init.sh
|
||||
- run:
|
||||
name: Initialize wasmd (deploy contracts and friends)
|
||||
command: ./scripts/wasmd/init.sh
|
||||
@ -403,7 +377,6 @@ jobs:
|
||||
command: ./scripts/socketserver/start.sh
|
||||
- run:
|
||||
environment:
|
||||
LAUNCHPAD_ENABLED: 1
|
||||
SIMAPP42_ENABLED: 1
|
||||
SLOW_SIMAPP42_ENABLED: 1
|
||||
TENDERMINT_ENABLED: 1
|
||||
@ -419,7 +392,6 @@ jobs:
|
||||
./scripts/simapp42/stop.sh
|
||||
./scripts/simapp42/slow_stop.sh
|
||||
./scripts/wasmd/stop.sh
|
||||
./scripts/launchpad/stop.sh
|
||||
coverage:
|
||||
machine:
|
||||
# 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
|
||||
sudo dpkg -i "$HOME/git-lfs.deb"
|
||||
- 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
|
||||
name: Start wasmd
|
||||
command: ./scripts/wasmd/start.sh
|
||||
@ -493,9 +461,6 @@ jobs:
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --immutable --immutable-cache --check-cache
|
||||
- run:
|
||||
name: Initialize launchpad (deploy contracts and friends)
|
||||
command: ./scripts/launchpad/init.sh
|
||||
- run:
|
||||
name: Initialize wasmd (deploy contracts and friends)
|
||||
command: ./scripts/wasmd/init.sh
|
||||
@ -504,7 +469,6 @@ jobs:
|
||||
command: ./scripts/socketserver/start.sh
|
||||
- run:
|
||||
environment:
|
||||
LAUNCHPAD_ENABLED: 1
|
||||
SIMAPP42_ENABLED: 1
|
||||
SLOW_SIMAPP42_ENABLED: 1
|
||||
TENDERMINT_ENABLED: 1
|
||||
@ -523,7 +487,6 @@ jobs:
|
||||
./scripts/simapp42/stop.sh
|
||||
./scripts/simapp42/slow_stop.sh
|
||||
./scripts/wasmd/stop.sh
|
||||
./scripts/launchpad/stop.sh
|
||||
docs-build:
|
||||
docker:
|
||||
- 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",
|
||||
"reference": "workspace:packages/json-rpc"
|
||||
},
|
||||
{
|
||||
"name": "@cosmjs/launchpad",
|
||||
"reference": "workspace:packages/launchpad"
|
||||
},
|
||||
{
|
||||
"name": "@cosmjs/ledger-amino",
|
||||
"reference": "workspace:packages/ledger-amino"
|
||||
@ -102,7 +98,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["@cosmjs/faucet", ["workspace:packages/faucet"]],
|
||||
["@cosmjs/faucet-client", ["workspace:packages/faucet-client"]],
|
||||
["@cosmjs/json-rpc", ["workspace:packages/json-rpc"]],
|
||||
["@cosmjs/launchpad", ["workspace:packages/launchpad"]],
|
||||
["@cosmjs/ledger-amino", ["workspace:packages/ledger-amino"]],
|
||||
["@cosmjs/math", ["workspace:packages/math"]],
|
||||
["@cosmjs/proto-signing", ["workspace:packages/proto-signing"]],
|
||||
@ -254,10 +249,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
"@cosmjs/json-rpc",
|
||||
"workspace:packages/json-rpc"
|
||||
],
|
||||
[
|
||||
"@cosmjs/launchpad",
|
||||
"workspace:packages/launchpad"
|
||||
],
|
||||
[
|
||||
"@cosmjs/ledger-amino",
|
||||
"workspace:packages/ledger-amino"
|
||||
@ -2856,7 +2847,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["@cosmjs/cosmwasm-stargate", "workspace:packages/cosmwasm-stargate"],
|
||||
["@cosmjs/crypto", "workspace:packages/crypto"],
|
||||
["@cosmjs/encoding", "workspace:packages/encoding"],
|
||||
["@cosmjs/launchpad", "workspace:packages/launchpad"],
|
||||
["@cosmjs/proto-signing", "workspace:packages/proto-signing"],
|
||||
["@cosmjs/stargate", "workspace:packages/stargate"],
|
||||
["eslint", "npm:7.26.0"],
|
||||
@ -3256,7 +3246,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["@cosmjs/crypto", "workspace:packages/crypto"],
|
||||
["@cosmjs/encoding", "workspace:packages/encoding"],
|
||||
["@cosmjs/faucet-client", "workspace:packages/faucet-client"],
|
||||
["@cosmjs/launchpad", "workspace:packages/launchpad"],
|
||||
["@cosmjs/math", "workspace:packages/math"],
|
||||
["@cosmjs/proto-signing", "workspace:packages/proto-signing"],
|
||||
["@cosmjs/stargate", "workspace:packages/stargate"],
|
||||
@ -3465,7 +3454,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["@cosmjs/faucet", "workspace:packages/faucet"],
|
||||
["@cosmjs/crypto", "workspace:packages/crypto"],
|
||||
["@cosmjs/encoding", "workspace:packages/encoding"],
|
||||
["@cosmjs/launchpad", "workspace:packages/launchpad"],
|
||||
["@cosmjs/math", "workspace:packages/math"],
|
||||
["@cosmjs/proto-signing", "workspace:packages/proto-signing"],
|
||||
["@cosmjs/stargate", "workspace:packages/stargate"],
|
||||
@ -3588,58 +3576,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
"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", [
|
||||
["workspace:packages/ledger-amino", {
|
||||
"packageLocation": "./packages/ledger-amino/",
|
||||
@ -3648,7 +3584,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["@cosmjs/amino", "workspace:packages/amino"],
|
||||
["@cosmjs/crypto", "workspace:packages/crypto"],
|
||||
["@cosmjs/encoding", "workspace:packages/encoding"],
|
||||
["@cosmjs/launchpad", "workspace:packages/launchpad"],
|
||||
["@cosmjs/math", "workspace:packages/math"],
|
||||
["@cosmjs/stargate", "workspace:packages/stargate"],
|
||||
["@cosmjs/utils", "workspace:packages/utils"],
|
||||
@ -5434,23 +5369,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
],
|
||||
"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", {
|
||||
"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": [
|
||||
@ -5665,20 +5583,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
],
|
||||
"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", {
|
||||
"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": [
|
||||
@ -5917,23 +5821,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
],
|
||||
"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", {
|
||||
"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": [
|
||||
@ -6999,7 +6886,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["@cosmjs/cosmwasm-stargate", "workspace:packages/cosmwasm-stargate"],
|
||||
["@cosmjs/crypto", "workspace:packages/crypto"],
|
||||
["@cosmjs/encoding", "workspace:packages/encoding"],
|
||||
["@cosmjs/launchpad", "workspace:packages/launchpad"],
|
||||
["@cosmjs/proto-signing", "workspace:packages/proto-signing"],
|
||||
["@cosmjs/stargate", "workspace:packages/stargate"],
|
||||
["eslint", "npm:7.26.0"],
|
||||
@ -11913,25 +11799,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
],
|
||||
"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", {
|
||||
"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": [
|
||||
@ -13065,42 +12932,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
"webpack-cli"
|
||||
],
|
||||
"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", [
|
||||
@ -13698,48 +13529,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
"webpack"
|
||||
],
|
||||
"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", [
|
||||
|
@ -13,6 +13,4 @@ plugins:
|
||||
|
||||
pnpMode: loose
|
||||
|
||||
preferInteractive: true
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.1.0.cjs
|
||||
|
@ -54,7 +54,6 @@ Here are some of them to get an idea:
|
||||
| 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/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/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) |
|
||||
|
@ -44,7 +44,6 @@
|
||||
"@cosmjs/cosmwasm-stargate": "workspace:packages/cosmwasm-stargate",
|
||||
"@cosmjs/crypto": "workspace:packages/crypto",
|
||||
"@cosmjs/encoding": "workspace:packages/encoding",
|
||||
"@cosmjs/launchpad": "workspace:packages/launchpad",
|
||||
"@cosmjs/proto-signing": "workspace:packages/proto-signing",
|
||||
"@cosmjs/stargate": "workspace:packages/stargate",
|
||||
"eslint": "^7.5",
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
[](https://www.npmjs.com/package/@cosmjs/amino)
|
||||
|
||||
Helpers for Amino based signing which are shared between @cosmjs/launchpad and
|
||||
@cosmjs/stargate.
|
||||
Helpers for Amino for @cosmjs/stargate.
|
||||
|
||||
## License
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@cosmjs/amino",
|
||||
"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": [
|
||||
"Simon Warta <webmaster128@users.noreply.github.com>"
|
||||
],
|
||||
|
@ -45,37 +45,29 @@ $ cosmwasm-cli
|
||||
|
||||
```ts
|
||||
// Get account information
|
||||
const { account_number, sequence } = (await client.authAccounts(faucetAddress))
|
||||
.result.value;
|
||||
const account = await client.getAccount(faucetAddress);
|
||||
|
||||
// Craft a send transaction
|
||||
const emptyAddress = Bech32.encode("cosmos", Random.getBytes(20));
|
||||
const memo = "My first contract on chain";
|
||||
const sendTokensMsg: MsgSend = {
|
||||
type: "cosmos-sdk/MsgSend",
|
||||
value: {
|
||||
from_address: faucetAddress,
|
||||
to_address: emptyAddress,
|
||||
amount: [
|
||||
{
|
||||
denom: "ucosm",
|
||||
amount: "1234567",
|
||||
},
|
||||
],
|
||||
},
|
||||
const memo = "My very first tx!";
|
||||
const msgSend = {
|
||||
fromAddress: faucetAddress,
|
||||
toAddress: emptyAddress,
|
||||
amount: coins(1234, "ucosm"),
|
||||
};
|
||||
|
||||
const signDoc = makeSignDoc(
|
||||
[sendTokensMsg],
|
||||
const msgAny = {
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: msgSend,
|
||||
};
|
||||
|
||||
// Broadcast and sign the transaction
|
||||
const broadcastResult = await client.signAndBroadcast(
|
||||
faucetAddress,
|
||||
[msgAny],
|
||||
defaultFee,
|
||||
defaultNetworkId,
|
||||
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
|
||||
|
@ -1,45 +1,48 @@
|
||||
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
|
||||
import {
|
||||
coin,
|
||||
coins,
|
||||
makeSignDoc,
|
||||
makeStdTx,
|
||||
CosmosClient,
|
||||
MsgDelegate,
|
||||
Secp256k1HdWallet,
|
||||
} from "@cosmjs/launchpad";
|
||||
MsgDelegateEncodeObject,
|
||||
SigningStargateClient,
|
||||
calculateFee,
|
||||
assertIsDeliverTxSuccess,
|
||||
GasPrice,
|
||||
} from "@cosmjs/stargate";
|
||||
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(
|
||||
// Wallet
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(
|
||||
"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 = {
|
||||
type: "cosmos-sdk/MsgDelegate",
|
||||
// Setup client
|
||||
const client = await SigningStargateClient.connectWithSigner(rpcEndpoint, wallet);
|
||||
|
||||
// Send delegate transaction
|
||||
const msg: MsgDelegateEncodeObject = {
|
||||
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
|
||||
value: {
|
||||
delegator_address: senderAddress,
|
||||
// To get the proper validator address, start the demo chain (./scripts/launchpad/start.sh), then run:
|
||||
// curl http://localhost:1317/staking/validators | jq '.result[0].operator_address'
|
||||
validator_address: "cosmosvaloper1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r3arurr",
|
||||
delegatorAddress: signerAddress,
|
||||
// To get the proper validator address, start the demo chain (./scripts/simapp42/start.sh), then run:
|
||||
// curl http://localhost:1318/staking/validators | jq '.result[0].operator_address'
|
||||
validatorAddress: "cosmosvaloper1urk9gy7cfws0ak9x5nu7lx4un9n6gqkrp230jk",
|
||||
amount: coin(300000, "ustake"),
|
||||
},
|
||||
};
|
||||
const fee = {
|
||||
amount: coins(2000, "ucosm"),
|
||||
gas: "180000", // 180k
|
||||
};
|
||||
const fee = calculateFee(180_000, gasPrice);
|
||||
const memo = "Use your power wisely";
|
||||
|
||||
const chainId = await client.getChainId();
|
||||
console.log("Connected to chain:", chainId);
|
||||
|
||||
const { accountNumber, sequence } = await client.getSequence(senderAddress);
|
||||
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);
|
||||
const result = await client.signAndBroadcast(signerAddress, [msg], fee, memo);
|
||||
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 defaultNetworkId = "testing";
|
||||
const defaultHttpUrl = "http://localhost:26658";
|
||||
const defaultFee: StdFee = {
|
||||
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";
|
||||
const faucetAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6";
|
||||
|
||||
const wallet = await Secp256k1HdWallet.fromMnemonic(faucetMnemonic);
|
||||
const client = new LcdClient(defaultHttpUrl);
|
||||
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucetMnemonic);
|
||||
const client = await SigningStargateClient.connectWithSigner(defaultHttpUrl, wallet);
|
||||
|
@ -45,7 +45,6 @@
|
||||
"@cosmjs/crypto": "workspace:packages/crypto",
|
||||
"@cosmjs/encoding": "workspace:packages/encoding",
|
||||
"@cosmjs/faucet-client": "workspace:packages/faucet-client",
|
||||
"@cosmjs/launchpad": "workspace:packages/launchpad",
|
||||
"@cosmjs/math": "workspace:packages/math",
|
||||
"@cosmjs/proto-signing": "workspace:packages/proto-signing",
|
||||
"@cosmjs/stargate": "workspace:packages/stargate",
|
||||
|
@ -5,7 +5,7 @@ command -v shellcheck >/dev/null && shellcheck "$0"
|
||||
if [ -n "${WASMD_ENABLED:-}" ]; then
|
||||
yarn node ./bin/cosmwasm-cli --init examples/cosmwasm.ts --code "process.exit(0)"
|
||||
fi
|
||||
if [ -n "${LAUNCHPAD_ENABLED:-}" ]; then
|
||||
if [ -n "${SIMAPP42_ENABLED:-}" ]; then
|
||||
yarn node ./bin/cosmwasm-cli --init examples/delegate.ts --code "process.exit(0)"
|
||||
fi
|
||||
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:
|
||||
|
||||
```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:
|
||||
|
@ -10,7 +10,7 @@ describe("FaucetClient", () => {
|
||||
const faucetUrl = "http://localhost:8000";
|
||||
const primaryToken = "ucosm";
|
||||
const secondaryToken = "ustake";
|
||||
const defaultAddress = "cosmos14qemq0vw6y3gc3u3e0aty2e764u4gs5le3hada";
|
||||
const defaultAddress = "wasm14qemq0vw6y3gc3u3e0aty2e764u4gs5lndxgyk";
|
||||
|
||||
it("can be constructed", () => {
|
||||
// http
|
||||
|
@ -41,7 +41,6 @@
|
||||
"dependencies": {
|
||||
"@cosmjs/crypto": "workspace:packages/crypto",
|
||||
"@cosmjs/encoding": "workspace:packages/encoding",
|
||||
"@cosmjs/launchpad": "workspace:packages/launchpad",
|
||||
"@cosmjs/math": "workspace:packages/math",
|
||||
"@cosmjs/proto-signing": "workspace:packages/proto-signing",
|
||||
"@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}"`);
|
||||
|
||||
// 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 { Webserver } from "../api/webserver";
|
||||
@ -17,14 +16,8 @@ export async function start(args: readonly string[]): Promise<void> {
|
||||
// Connection
|
||||
const blockchainBaseUrl = args[0];
|
||||
console.info(`Connecting to blockchain ${blockchainBaseUrl} ...`);
|
||||
let chainId;
|
||||
let stargate = true;
|
||||
try {
|
||||
chainId = await (await StargateClient.connect(blockchainBaseUrl)).getChainId();
|
||||
} catch (_error) {
|
||||
chainId = await new CosmosClient(blockchainBaseUrl).getChainId();
|
||||
stargate = false;
|
||||
}
|
||||
const client = await StargateClient.connect(blockchainBaseUrl);
|
||||
const chainId = await client.getChainId();
|
||||
console.info(`Connected to network: ${chainId}`);
|
||||
|
||||
// Faucet
|
||||
@ -38,7 +31,6 @@ export async function start(args: readonly string[]): Promise<void> {
|
||||
constants.mnemonic,
|
||||
pathBuilder,
|
||||
constants.concurrency,
|
||||
stargate,
|
||||
logging,
|
||||
);
|
||||
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 { parseBankTokens } from "./tokens";
|
||||
@ -6,9 +6,7 @@ import { parseBankTokens } from "./tokens";
|
||||
export const binaryName = "cosmos-faucet";
|
||||
export const memo: string | undefined = process.env.FAUCET_MEMO;
|
||||
export const gasPrice = GasPrice.fromString(process.env.FAUCET_GAS_PRICE || "0.025ucosm");
|
||||
export const gasLimits: GasLimits<CosmosFeeTable> = {
|
||||
send: parseInt(process.env.FAUCET_GAS_LIMIT || "80000", 10),
|
||||
};
|
||||
export const gasLimitSend = parseInt(process.env.FAUCET_GAS_LIMIT || "200000", 10);
|
||||
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 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";
|
||||
|
||||
|
@ -1,18 +1,11 @@
|
||||
import { Random } from "@cosmjs/crypto";
|
||||
import { Bech32 } from "@cosmjs/encoding";
|
||||
import { CosmosClient } from "@cosmjs/launchpad";
|
||||
import { makeCosmoshubPath, StargateClient } from "@cosmjs/stargate";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
|
||||
import { Faucet } from "./faucet";
|
||||
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 {
|
||||
if (!process.env.SIMAPP42_ENABLED && !process.env.SIMAPP44_ENABLED) {
|
||||
return pending("Set SIMAPP42_ENABLED or SIMAPP44_ENABLED to enabled Stargate node-based tests");
|
||||
@ -34,224 +27,6 @@ const faucetMnemonic =
|
||||
describe("Faucet", () => {
|
||||
const pathBuilder = makeCosmoshubPath;
|
||||
|
||||
describe("launchpad", () => {
|
||||
const apiUrl = "http://localhost:1317";
|
||||
const stargate = false;
|
||||
|
||||
describe("constructor", () => {
|
||||
it("can be constructed", async () => {
|
||||
pendingWithoutLaunchpad();
|
||||
const faucet = await Faucet.make(
|
||||
apiUrl,
|
||||
defaultAddressPrefix,
|
||||
defaultTokenConfig,
|
||||
faucetMnemonic,
|
||||
pathBuilder,
|
||||
3,
|
||||
stargate,
|
||||
);
|
||||
expect(faucet).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("availableTokens", () => {
|
||||
it("is empty when no tokens are configured", async () => {
|
||||
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", () => {
|
||||
const apiUrl = "localhost:26658";
|
||||
const stargate = true;
|
||||
let originalEnvVariable: string | undefined;
|
||||
@ -265,6 +40,7 @@ describe("Faucet", () => {
|
||||
process.env.FAUCET_CREDIT_AMOUNT_USTAKE = originalEnvVariable;
|
||||
});
|
||||
|
||||
describe("stargate", () => {
|
||||
describe("constructor", () => {
|
||||
it("can be constructed", async () => {
|
||||
pendingWithoutSimapp();
|
||||
|
@ -1,8 +1,3 @@
|
||||
import {
|
||||
assertIsBroadcastTxSuccess as assertIsBroadcastTxSuccessLaunchpad,
|
||||
CosmosClient,
|
||||
SigningCosmosClient,
|
||||
} from "@cosmjs/launchpad";
|
||||
import {
|
||||
assertIsDeliverTxSuccess as assertIsDeliverTxSuccessStargate,
|
||||
calculateFee,
|
||||
@ -30,19 +25,11 @@ export class Faucet {
|
||||
mnemonic: string,
|
||||
pathBuilder: PathBuilder,
|
||||
numberOfDistributors: number,
|
||||
stargate = true,
|
||||
logging = false,
|
||||
): Promise<Faucet> {
|
||||
const wallets = await createWallets(
|
||||
mnemonic,
|
||||
pathBuilder,
|
||||
addressPrefix,
|
||||
numberOfDistributors,
|
||||
stargate,
|
||||
logging,
|
||||
);
|
||||
const wallets = await createWallets(mnemonic, pathBuilder, addressPrefix, numberOfDistributors, logging);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -52,16 +39,16 @@ export class Faucet {
|
||||
|
||||
private readonly tokenConfig: TokenConfiguration;
|
||||
private readonly tokenManager: TokenManager;
|
||||
private readonly readOnlyClient: CosmosClient | StargateClient;
|
||||
private readonly clients: { [senderAddress: string]: SigningCosmosClient | SigningStargateClient };
|
||||
private readonly readOnlyClient: StargateClient;
|
||||
private readonly clients: { [senderAddress: string]: SigningStargateClient };
|
||||
private readonly logging: boolean;
|
||||
private creditCount = 0;
|
||||
|
||||
private constructor(
|
||||
addressPrefix: string,
|
||||
config: TokenConfiguration,
|
||||
clients: ReadonlyArray<readonly [string, SigningCosmosClient | SigningStargateClient]>,
|
||||
readonlyClient: CosmosClient | StargateClient,
|
||||
clients: ReadonlyArray<readonly [string, SigningStargateClient]>,
|
||||
readonlyClient: StargateClient,
|
||||
logging = false,
|
||||
) {
|
||||
this.addressPrefix = addressPrefix;
|
||||
@ -94,11 +81,7 @@ export class Faucet {
|
||||
*/
|
||||
public async send(job: SendJob): Promise<void> {
|
||||
const client = this.clients[job.sender];
|
||||
if (client instanceof SigningCosmosClient) {
|
||||
const result = await client.sendTokens(job.recipient, [job.amount], constants.memo);
|
||||
return assertIsBroadcastTxSuccessLaunchpad(result);
|
||||
}
|
||||
const fee = calculateFee(constants.gasLimits.send, constants.gasPrice);
|
||||
const fee = calculateFee(constants.gasLimitSend, constants.gasPrice);
|
||||
const result = await client.sendTokens(job.sender, job.recipient, [job.amount], fee, constants.memo);
|
||||
assertIsDeliverTxSuccessStargate(result);
|
||||
}
|
||||
@ -122,11 +105,7 @@ export class Faucet {
|
||||
}
|
||||
|
||||
public async loadAccount(address: string): Promise<MinimalAccount> {
|
||||
const balance =
|
||||
this.readOnlyClient instanceof CosmosClient
|
||||
? (await this.readOnlyClient.getAccount(address))?.balance ?? []
|
||||
: await this.readOnlyClient.getAllBalances(address);
|
||||
|
||||
const balance = await this.readOnlyClient.getAllBalances(address);
|
||||
return {
|
||||
address: address,
|
||||
balance: balance,
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { pathToString } from "@cosmjs/crypto";
|
||||
import { Secp256k1HdWallet, SigningCosmosClient } from "@cosmjs/launchpad";
|
||||
import { DirectSecp256k1HdWallet, isOfflineDirectSigner, OfflineSigner } from "@cosmjs/proto-signing";
|
||||
import { DirectSecp256k1HdWallet, OfflineSigner } from "@cosmjs/proto-signing";
|
||||
import { SigningStargateClient } from "@cosmjs/stargate";
|
||||
|
||||
import * as constants from "./constants";
|
||||
import { PathBuilder } from "./pathbuilder";
|
||||
|
||||
export async function createWallets(
|
||||
@ -11,17 +9,18 @@ export async function createWallets(
|
||||
pathBuilder: PathBuilder,
|
||||
addressPrefix: string,
|
||||
numberOfDistributors: number,
|
||||
stargate: boolean,
|
||||
logging: boolean,
|
||||
): Promise<ReadonlyArray<readonly [string, OfflineSigner]>> {
|
||||
const createWallet = stargate ? DirectSecp256k1HdWallet.fromMnemonic : Secp256k1HdWallet.fromMnemonic;
|
||||
const wallets = new Array<readonly [string, OfflineSigner]>();
|
||||
|
||||
// first account is the token holder
|
||||
const numberOfIdentities = 1 + numberOfDistributors;
|
||||
for (let i = 0; i < numberOfIdentities; 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();
|
||||
if (logging) {
|
||||
const role = i === 0 ? "token holder " : `distributor ${i}`;
|
||||
@ -36,18 +35,13 @@ export async function createWallets(
|
||||
export async function createClients(
|
||||
apiUrl: string,
|
||||
wallets: ReadonlyArray<readonly [string, OfflineSigner]>,
|
||||
): Promise<ReadonlyArray<readonly [string, SigningCosmosClient | SigningStargateClient]>> {
|
||||
): Promise<ReadonlyArray<readonly [string, SigningStargateClient]>> {
|
||||
// we need one client per sender
|
||||
return Promise.all(
|
||||
wallets.map(
|
||||
async ([senderAddress, wallet]): Promise<
|
||||
readonly [string, SigningCosmosClient | SigningStargateClient]
|
||||
> => [
|
||||
const pendingClients = wallets.map(
|
||||
async ([senderAddress, wallet]): Promise<readonly [string, SigningStargateClient]> => [
|
||||
senderAddress,
|
||||
isOfflineDirectSigner(wallet)
|
||||
? await SigningStargateClient.connectWithSigner(apiUrl, wallet)
|
||||
: new SigningCosmosClient(apiUrl, senderAddress, wallet, constants.gasPrice, constants.gasLimits),
|
||||
await SigningStargateClient.connectWithSigner(apiUrl, wallet),
|
||||
],
|
||||
),
|
||||
);
|
||||
return Promise.all(pendingClients);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Coin } from "@cosmjs/launchpad";
|
||||
import { Decimal, Uint53 } from "@cosmjs/math";
|
||||
import { Coin } from "@cosmjs/stargate";
|
||||
|
||||
import { MinimalAccount } from "./types";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Account, Coin } from "@cosmjs/launchpad";
|
||||
import { Coin } from "@cosmjs/stargate";
|
||||
|
||||
export interface SendJob {
|
||||
readonly sender: string;
|
||||
@ -6,4 +6,8 @@ export interface SendJob {
|
||||
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"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cosmjs/launchpad": "workspace:packages/launchpad",
|
||||
"@cosmjs/stargate": "workspace:packages/stargate",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@ledgerhq/hw-transport": "^5.25.0",
|
||||
|
@ -9,10 +9,6 @@ import {
|
||||
} from "@cosmjs/amino";
|
||||
import { Secp256k1, Secp256k1Signature, sha256 } from "@cosmjs/crypto";
|
||||
import { fromBase64 } from "@cosmjs/encoding";
|
||||
import {
|
||||
assertIsBroadcastTxSuccess as assertIsBroadcastTxSuccessLaunchpad,
|
||||
SigningCosmosClient,
|
||||
} from "@cosmjs/launchpad";
|
||||
import {
|
||||
assertIsDeliverTxSuccess as assertIsDeliverTxSuccessStargate,
|
||||
calculateFee,
|
||||
@ -24,9 +20,7 @@ import Transport from "@ledgerhq/hw-transport";
|
||||
import { LedgerSigner } from "./ledgersigner";
|
||||
import {
|
||||
faucet,
|
||||
launchpad,
|
||||
ledgerEnabled,
|
||||
pendingWithoutLaunchpad,
|
||||
pendingWithoutLedger,
|
||||
pendingWithoutSimapp,
|
||||
simapp,
|
||||
@ -166,24 +160,6 @@ describe("LedgerSigner", () => {
|
||||
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(
|
||||
"creates signature accepted by Stargate backend",
|
||||
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 {
|
||||
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 = {
|
||||
endpoint: "ws://localhost:26658",
|
||||
chainId: "simd-testing",
|
||||
|
@ -4,7 +4,7 @@ import { Uint64 } from "@cosmjs/math";
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export function parseCoins(input: string): Coin[] {
|
||||
|
@ -1,14 +1,7 @@
|
||||
import { OfflineAminoSigner, StdSignature } from "@cosmjs/amino";
|
||||
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";
|
||||
|
||||
/**
|
||||
* This is the same as AccountData from @cosmjs/launchpad but those might diverge in the future.
|
||||
*/
|
||||
export interface AccountData {
|
||||
/** A printable address (typically bech32 encoded) */
|
||||
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
|
||||
* 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 {
|
||||
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 {
|
||||
readonly version: {
|
||||
readonly block: string;
|
||||
@ -49,9 +46,6 @@ export interface BlockHeader {
|
||||
readonly time: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the same as Block from @cosmjs/launchpad but those might diverge in the future.
|
||||
*/
|
||||
export interface Block {
|
||||
/** The ID is a hash of the block header (uppercase hex) */
|
||||
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